php異或計(jì)算繞過preg_match()實(shí)現(xiàn)原理
2019-09-20
原理
以制作免殺馬為例:
在制作免殺馬的過程,根據(jù)php的語言特性對字符進(jìn)行!運(yùn)算會(huì)將字符類型轉(zhuǎn)為bool類型,而bool類型遇到運(yùn)算符號(hào)時(shí),true會(huì)自動(dòng)轉(zhuǎn)為數(shù)字1,false會(huì)自動(dòng)轉(zhuǎn)為數(shù)字0,如果將bool類型進(jìn)行計(jì)算,并使用chr()函數(shù)轉(zhuǎn)為字符,使用"."進(jìn)行連接,便可以繞過preg_match匹配。
詳情了解php不同于其他語言部分
但是很多的preg_match會(huì)過濾掉".",所以需要使用異或運(yùn)算進(jìn)行繞過,很多的免殺馬都是這樣制作的。php對字符進(jìn)行異或運(yùn)算是先將字符轉(zhuǎn)換成ASCII碼然后進(jìn)行異或運(yùn)算,并且php能直接對一串字符串進(jìn)行異或運(yùn)算,例如"123"^"abc"是"1"與"a"進(jìn)行異或然后"2"與"b"進(jìn)行異或,以此類推,在異或結(jié)束后就獲得了想要的字符串。
注意點(diǎn):進(jìn)行異或運(yùn)算時(shí)要將數(shù)字轉(zhuǎn)換成字符形式,如果數(shù)字(int)和字符異或的話,結(jié)果只會(huì)是數(shù)字,例如1^"a"=1,"a"^2=2,將數(shù)字轉(zhuǎn)換成字符串可以使用trim()函數(shù)。
拓展:
php特性u(píng)se of undefined constant,會(huì)將沒有引號(hào)的字符都自動(dòng)視為字符串,ASCII碼大于0x7F的都會(huì)被當(dāng)作字符串,由此可知可以簡化異或過程,任何字符與0xff異或都會(huì)取相反,這樣就能減少運(yùn)算量了。
以GET或POST傳入字符繞preg_match為例:
php的eval()函數(shù)在執(zhí)行時(shí)如果內(nèi)部有類似"abc"^"def"的計(jì)算式,那么就先進(jìn)行計(jì)算再執(zhí)行,我們可以利用再創(chuàng)參數(shù)來實(shí)現(xiàn)更方便的操作,例如傳入?a=$_GET[b],由于b不受限制就可以任意傳值了,不過
注意1:在測試過程中發(fā)現(xiàn)問題,類似phpinfo();的,需要將后面的();放在第個(gè)參數(shù)的后面,例如url?a={_GET}();&b=phpinfo,也就是?a=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo,在傳入后實(shí)際上為${????^????}{?}();但是到了eval()函數(shù)內(nèi)部就會(huì)變成${_GET}{?}();成功執(zhí)行。
注意2:測試中發(fā)現(xiàn),傳值時(shí)對于要計(jì)算的部分不能用括號(hào)括起來,因?yàn)槔ㄌ?hào)也將被識(shí)別為傳入的字符串,可以使用{}代替,原因是php的use of undefined constant特性,例如${_GET}{a}這樣的語句php是不會(huì)判為錯(cuò)誤的,因?yàn)閧}使用來界定變量的,這句話就是會(huì)將_GET自動(dòng)看為字符串,也就是$_GET['a']
Demo
Suctf easyphp
<?php
$hhh = @$_GET['_'];
if (!$hhh){
highlight_file(__FILE__);
}
if(strlen($hhh)>18){
die('One inch long, one inch strong!');
}
if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
die('Try something else!');
$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");
eval($hhh);
?>
用戶傳入?_=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
成功顯示phpinfo頁面