PHP弱类型及一些绕过姿势
前言:
做web題時經常會遇到各種php弱類型和一些函數的繞過方法,由于知識比較零碎,就總結一下我所遇到的,也方便自己以后觀看。
0x00:弱類型介紹:
弱類型是可以隨意轉換變量的類型,也就是說php并不會驗證變量的類型,可以隨時的轉換類型,雖然提升了效率,但是引發了很多安全問題。
0x01:Hash比較缺陷
簡單介紹:
PHP在處理哈希字符串時,通過!=或==來對哈希值進行比較,它把每一個以0e開頭的哈希值都解釋為0,所以如果兩個不同的密碼經過哈希以后,其哈希值都是以0e開頭的,那么PHP將會認為他們相同,都是0
具體實例:
審計代碼,我們輸入的不能相等,但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() 函數使用數組鍵名作為變量名,使用數組鍵值作為變量值,當變量中有同名的元素時,該函數默認將原有的值給覆蓋掉。這就造成了變量覆蓋
具體實例:
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
具體實例:
這道題也涉及了===和!==,不了解的可以去官方文檔查看一下:
審計一下代碼,第一個if語句要求我們輸入的值進行首尾匹配必須是1-9之間的數字,第二個if語句又讓我們利用strpos() 函數查找相應的字符串在輸入的值中第一次出現的位置,且后面是!==,所以要不讓值相等類型不同,或是值不同類型相同即可。
但strpos()函數如果傳入數組,便會返回NULL。利用這個漏洞,便可以構造出繞過的payload:
?nctf[]=1ereg()函數返回NULL,===判斷NULL和FALSE是不相等的(類型不同),所以進入第二個語句,因為strpos處理數組時,也是返回NULL,又因為NULL!==FALSE條件成立所以得出flag
除此之外,還可以構造利用%00來構造payload:
?nctf=123%00%23biubiubiu0x04:strcmp比較字符串
簡單介紹:
strcmp()函數比較兩個字符串(區分大小寫),定義中是比較字符串類型的,但如果輸入其他類型這個函數將發生錯誤,在官方文檔的說明中說到在php 5.2版本之前,利用strcmp函數將數組與字符串進行比較會返回-1,但是從5.3開始,會返回0。
具體實例:
審計代碼,很簡單關鍵就在于我們如何繞過檢查,構造如下payload:
數組和字符進行比較結果不會返回1,即為false,加上非的作用,即可變成true,則滿足條件
0x05:SESSION驗證繞過
簡單介紹:
在PHP配置中的默認情況下,Session是用Session ID來確定當前對話所對應的服務器Session,sessionID可在cookie中找到,當我們刪除cookie中的sessionID后,$_SESSION[‘password’]就會返回空,我們同樣傳入空的password就能繞過了
具體實例:
觀察代碼發現需要我們GET傳入的password和session中存儲的password相同才可以得出flag,如果刪掉session值,或者修改session值為一個不存在的session,服務器獲取不到session,則password為空,這時候我們再GET進去一個空的password的進去拿到flag
0x06: sha()函數比較繞過、md5()函數繞過
簡單介紹:
一、md5()函數獲取不到數組的值,默認數組為0
二、sha1()函數無法處理數組類型,將報錯并返回false
具體實例:
如果知道了sha1()函數不能處理數組,那這道題將非常好做,構造payload:
注意這里是===,不是==,所以這里采用md5()函數獲取不到數組的值,默認數組為0這個特性來做,payload:
0x07:十六進制與數字比較
php在轉碼時會把16進制轉化為十進制
payload:
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()}
得到flag
0x09:繞過escapeshellarg+escapeshellcmd函數
原理分析:
模仿師傅的例子進行學習
談談escapeshellarg參數繞過和注入的問題
escapeshellarg
escapeshellarg() 將給字符串增加一個單引號并且能引用或者轉碼任何已經存在的單引號,這樣以確保能夠直接將一個字符串傳入shell 函數,并且還是確保安全的。
escapeshellcmd
escapeshellcmd() 對字符串中可能會欺騙 shell 命令執行任意命令的字符進行轉義。反斜線(\)會在以下字符之前插入: &#;`|?~<>^()[]{}$, \x0A 和 \xFF。 *’ 和 “ 僅在不配對兒的時候被轉義。 在 Windows 平臺上,所有這些字符以及 % 和 ! 字符都會被空格代替。
先通過例子來查看一下escapeshellarg函數的作用吧
<?php var_dump(escapeshellarg("123")); var_dump(escapeshellarg("12' 3")); ?>
在解析單引號的時候 , 被單引號包裹的內容中如果有變量 , 這個變量名是不會被解析成值的,但是雙引號不同 , bash 會將變量名解析成變量的值再使用。
所以即使參數用了 escapeshellarg 函數過濾單引號,但參數在拼接命令的時候如果用了雙引號的話還是會導致命令執行的漏洞。
再來看一下escapeshellcmd 函數的作用
兩個函數都會對單引號進行處理,但是有區別的,如下:
對于單個單引號, escapeshellarg 函數轉義后,還會在左右各加一個單引號,但 escapeshellcmd 函數是直接加一個轉義符,對于成對的單引號, escapeshellcmd 函數默認不轉義,但 escapeshellarg 函數轉義
那既然有這個差異,如果escapeshellcmd() 和 escapeshellarg() 一起出現會有什么問題
測試
結果
分析
因此最后system函數是對127.0.0.1\發起請求,POST 數據為a=1',如果兩個函數翻過來則不會出現這個問題
接下來就通過一個題目來實踐一下:
Online Tool
代碼中是先使用了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:
但還有一個問題,escapeshellcmd會把一句話木馬中的一些字符給轉義的,又該怎么辦,測試一下在本地傳一下發現雖然看起來轉義了,但寫入的話還是沒有被轉義的。
所以最終的payload:
創建的目錄也出來了,寫的一句話木馬文件在該目錄下,連接一下
得到flag
也可以傳一個GET進去,調用system函數
0x10:md5強類型
(string)$_POST['a1']!==(string)$_POST['a2']&& md5($_POST['a1'])===md5($_POST['a2'])}例如這段代碼,使用數組就不可行,因為最后轉為字符串進行比較,所以只能構造兩個MD5值相同的不同字符串.
兩組經過url編碼后的值
0x11:PHP精度繞過缺陷
幾次都碰到這個點,記錄一下,省的以后忘了再去查
浮點運算的坑
在用PHP進行浮點數的運算中,經常會出現一些和預期結果不一樣的值,先來看個小例子
輸出的是57,而我們預想的應該是58
具體詳細的原理可以看這位師傅的描述
http://www.haodaquan.com/12
簡單的說因為PHP 通常使用 IEEE 754 雙精度格式而且由于浮點數的精度有限的原因。除此之外取整而導致的最大相對誤差為 1.11e-16,當小數小于10^-16后,PHP對于小數就大小不分了,如下圖:
easytrick
2020Ciscn初賽就考察到了這一點,前面總結過了,這里就不在敘述了
鏈接如下:
https://blog.csdn.net/qq_43431158/article/details/108158174
總結
以上是生活随笔為你收集整理的PHP弱类型及一些绕过姿势的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【MRCTF—Web】做题+复现记录
- 下一篇: 了解PHP伪协议