戴上你的黑帽,現(xiàn)在我們來學習一些關(guān)于SQL注入真正有趣的東西。請記住,你們都好好地用這些將要看到的東西,好嗎?
SQL注入攻擊因如下幾點而是一種特別有趣的冒險:
SQL注入攻擊因一個非常恰當?shù)脑蚨槐A粼贠Wasp(Open Web application Security PRoject 開放Web應用安全項目)的十大隱患列表中第一位——它特別常見,非常容易利用,而且影響十分劇烈。一個很微小的注入風險經(jīng)常就能使整個系統(tǒng)中的所有數(shù)據(jù)都被泄漏——而我將要展示給你如何運用大量不同的技術(shù)自己來這樣做。
我?guī)啄昵皩憽秚he OWASP Top 10 for .NET developers》時展示過如何防范SQL注入攻擊,所以我不會專注在這些,這都是漏洞利用。受夠了那些無聊的防御工具,讓我們來攻擊別的東西。
如果我們能攻破查詢內(nèi)容,你們的數(shù)據(jù)就都是我們的了讓我們對讓SQL注入攻擊成為可能的原因做一個快速概括。簡而言之,這就是輸入查詢并解密數(shù)據(jù)。讓我把所說的可視化給你:比如說你有一個包含有類似于“id=1”之類的字符串參數(shù)的URL,容納后那個參數(shù)通過如下方式構(gòu)造了一個SQL查詢。
這整個URL可能和這個東西看起來很像:
這是挺基礎(chǔ)的東西,而當你能掌控鏈接中的信息并改變傳遞給查詢的值時會變得有趣。好了,把1變成2會給你另一個你期待的東西,但是如果你這樣做呢?
http://widgetshop.com/widget/?id=1 or 1=1
那可能在數(shù)據(jù)庫服務(wù)器中存留成這樣的:
1 | SELECT * FROM Widget WHERE ID = 1 OR 1=1 |
這告訴我們的是數(shù)據(jù)沒有被凈化——在上例中ID應該只是一個整數(shù)但“1 OR 1=1”的值也被接受。更重要的是,因為數(shù)據(jù)只是簡單地被添加到查詢中,它能夠改變語句的功能。這個查詢將能夠選擇所有的記錄而不是單個記錄,因為”1=1″語句是恒成立的。
或者,我們可以通過把“or 1=1”改成“and 1=2”來強制頁面不返回任何記錄,因為它一直都不成立所以沒有結(jié)果返回。在這兩個可選的方案中我們能方便地確定程序是否受注入攻擊威脅。
這是SQL注入攻擊的本質(zhì)——通過不被信任的數(shù)據(jù)巧妙地操縱查詢的執(zhí)行——而在開發(fā)者做這樣子事時發(fā)生。
123 | query = "SELECT * FROM Widget WHERE ID = " + Request.QueryString[ "ID" ]; // Execute the query... //執(zhí)行查詢... |
當然他們做的是將不被信任的數(shù)據(jù)參數(shù)化,但本文中我不會過多敘述(如果想要了解防范措施,轉(zhuǎn)回part one of my OWASP series),而將更多談?wù)撊绾伟l(fā)動攻擊。
好了,于是背景部分介紹了如何展示SQL注入風險存在,但你能拿它怎么辦?讓我們開始探尋一些普遍的注入模式。
抽絲剝繭:合并基于查詢的注入讓我們舉個例子,表示我們想要返回一堆記錄的頁面,在這里是一個有一堆帶有“TypeId”1的小東西的URL。像這樣:
http://widgetshop.com/Widgets/?TypeId=1
頁面上的結(jié)果會像這樣:
我們會期待這個查詢進入到數(shù)據(jù)庫時變成像這樣的東西:
1 | SELECT Name FROM Widget WHERE TypeId = 1 |
但是如果我們能應用我上述描繪的,也就是說我們可能能夠給查詢字符串中的數(shù)據(jù)添加SQL,我們可能會做出這樣的東西:
http://widgetshop.com/Widgets/?TypeId=1 union all select name from sysobjects where xtype=’u’
然后它將產(chǎn)生一個如下的SQL查詢:
1 | SELECT Name FROM Widget WHERE TypeId = 1 union all select name from sysobjects where xtype= 'u' |
現(xiàn)在記好了系統(tǒng)對象表列舉數(shù)據(jù)庫中所有對象,而在這個例子中我們用 xtype “u” 來篩選這個表,換言之,用戶表。
當一個注入風險存在的時候?qū)腥缦碌妮敵觯?/p>
這就是叫做合并基于查詢的注入攻擊,就像我們剛才簡單地像原始結(jié)果添加一項,它直接到了HTML輸出中——簡單吧!既然我們已經(jīng)知道有一個數(shù)據(jù)表叫“User”,我們可以做這樣的事:
http://widgetshop.com/Widgets/?TypeId=1 union all select passWord from [user]
如果數(shù)據(jù)表中“user”不被中括號括起來,考慮到“user”這個詞在數(shù)據(jù)庫看來有其他含義,SQL服務(wù)器會變得不易控制。不管怎樣,這是它返回的:
當然,UNION ALL語句只在第一個SELECT語句和第二個有相同的字段時起作用。這很容易被發(fā)現(xiàn),你只需試試一些“union all select ‘a’”,如果它查詢失敗就試試“union all select ‘a’, ‘b’”之類的,以此類推。根本上你是在不斷猜測列數(shù)直到你構(gòu)造的查詢發(fā)揮作用。
我們可以繼續(xù)研究這個方面并揪出各種數(shù)據(jù),但還是學習下一種攻擊方式吧。有時一個基于合并查詢的注入不會發(fā)揮作用,與輸入格式、查詢中添加的數(shù)據(jù)甚至結(jié)果如何顯示都有關(guān)。為了繞開它我們需要變得更有創(chuàng)造性一些。
讓程序自己泄密:基于錯誤信息的注入http://widgetshop.com/widget/?id=1 or x=1
等一下,這不是一個合法的SQL語句,那個“x=1”不會被處理,至少在沒有一個叫做x的列時不會被處理。那么它不會拋出一個異常嗎?嚴格地說,事實上你將會看到像這樣的異常:
這是一個asp.net的錯誤,而其他的框架也有類似的樣式。但是重要的是這些錯誤信息暴露了內(nèi)部的實現(xiàn)方式,換言之,這告訴我們數(shù)據(jù)庫中沒有叫做“x”的字段。為什么這很重要?從根本上說,這是因為你一旦確立了一個應用程序在泄漏SQL異常,你就可以做這樣的事:
http://widgetshop.com/widget/?id=convert(int,(select top 1 name from sysobjects where id=(select top 1 id from (select top 1 id from sysobjects where xtype=’u’ order by id) sq order by id DESC)))
這有好多需要吸收理解,我等會將回來詳細解釋。更重要的是通過那條語句你能夠在瀏覽器中得到這樣的結(jié)果:
現(xiàn)在我們得到了,我們已經(jīng)發(fā)現(xiàn)那數(shù)據(jù)庫里有一個表單叫做“Widget”。你將經(jīng)常能看到這中注入攻擊因依賴于數(shù)據(jù)庫內(nèi)部的錯誤而被稱作“基于錯誤信息的注入”。讓我們解構(gòu)URL中的這個查詢:
12345678 | convert ( int , (
select top 1 name from sysobjects where id=(
select top 1 id from (
select top 1 id from sysobjects where xtype= 'u' order by id
) sq order by id DESC
)
) ) |