如何绕过PHP查询字符串解释器
本文的内容主要参考或者说翻译自 Abusing PHP query string parser to bypass IDS, IPS, and WAF
我们知道PHP通过向url中增加query参数来获取 $_GET
的值,也可以从HTTP request中增加 $_POST
的值,当这些内容发送到后端后,会首先被转换成数组元素,例如 /?foo=bar
会变成 Array([foo] => "bar")
,而这项工作就是由 query string parser
来完成的。
查询字符串解释器, query string parser 首先会删除一些参数中的字符或者用下划线 _
来进行替代。
例如 /?$20news[id%00=42
会被转换成 Array([news_id] => 42)
如果IDPS or WAF定义了 new_id
的参数不能为非数字的符号就可以用以上的方法进行绕过
此时, %20news[id%00
将会被存储到 $_GET["new_id"]
中去
Why?
PHP需要将所有的参数都转换成合法的变量名,为了保证变量名可以用, query string parser 会做两件事情:
- 移除开头的空格
- 将一些字符转换为下划线(包括空格)
例如:
用户输入 | URL解码后 | PHP实际存储变量名 |
---|---|---|
%20foo_bar%00 | foo_bar | foo_bar |
foo%20bar%00 | foo bar | foo_bar |
foo%5bbar | foo[bar | foo_bar |
[What does a bash sequence ‘\033999D’ mean and where is it explained?
<?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);
usleep(5000);
// 找到parse_str结果是 foo_bar 所对应的字符
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";
}
\033
是C语言风格的八进制字符,表示一个转义符号, [999D
表示将cursor光标回退999列,作用是强制将光标返回到开头, [k
表示清空当前行的内容, \r
还是和我们平常见到的一样的作用,就是将光标返回到一行的开头
这种编码主要和终端模拟器又关系,不同的终端模拟器使用不同的库可能效果就不一样,比如 ANSI X3.64
以及 ECMA-48
两种标准。
parse_str
方法会被用在 get
, post
, cookie
的参数获取上,通过三种形式来观察 parse_str
的行为,我们可以看到以下的符号(除去那些分隔符)可以被 parse_str
方法错误的解释为 foo_bar