一位客戶讓我們針對只有他們企業員工和顧客能使用的企業內網進行滲透測試。這是安全評估的一個部分,所以盡管我們之前沒有使用過SQL注入來滲透網絡,但對其概念也相當熟悉了。最后我們在這項任務中大獲成功,現在來回顧一下這個過程的每一步,將它記錄為一個
一位客戶讓我們針對只有他們企業員工和顧客能使用的企業內網進行滲透測試。這是安全評估的一個部分,所以盡管我們之前沒有使用過SQL注入來滲透網絡,但對其概念也相當熟悉了。最后我們在這項任務中大獲成功,現在來回顧一下這個過程的每一步,將它記錄為一個案例。
我們記錄下了在多次錯誤的轉折后經歷的曲折過程,而一個更有經驗的人會有這不同的 — 甚至更好的 — 方法。但事實上我們成功以后才明白,我們并沒有完全被誤導。
其他的SQL文章包含了更多的細節,但是這篇文章不僅展示了漏洞利用的過程,還講述了發現漏洞的原理。
展現在我們眼前的是一個完整定制網站,我們之前沒見過這個網站,也無權查看它的源代碼:這是一次“黑盒”攻擊。‘刺探’結果顯示這臺服務器運行在微軟的IIS6上,并且是ASP.NET架構。這就暗示我們數據庫是微軟的SQL server:我們相信我們的技巧可以應用在任何web應用上,無論它使用的是哪種SQL 服務器。
登陸頁有傳統的用戶-密碼表單,但多了一個 “把我的密碼郵給我”的鏈接;后來,這個地方被證實是整個系統陷落的關鍵。
當鍵入郵件地址時,系統假定郵件存在,就會在用戶數據庫里查詢郵件地址,然后郵寄一些內容給這個地址。但我的郵件地址無法找到,所以它什么也不會發給我。
對于任何SQL化的表單而言,第一步測試,是輸入一個帶有單引號的數據:目的是看看他們是否對構造SQL的字符串進行了過濾。當把單引號作為郵件地址提交以后,我們得到了500錯誤(服務器錯誤),這意味著“有害”輸入實際上是被直接用于SQL語句了。就是這了!
我猜測SQL代碼可能是這樣:
[sql] view plaincopy$EMAIL 是用戶從表單提交的地址,并且這段查詢在字符串末端$EMAIL上提供了引號。我們不知道字段或表的確切名字,但是我們了解他們的本質,這有助于我們做正確的猜測。
當我們鍵入 steve@unixwiz.net‘ -注意這個末端的引號 – 下面是這個SQL字段的構成:
[sql] view plaincopy當這段SQL開始執行,SQL解析器就會發現多余的引號然后中斷執行,并給出語法錯誤的提示。這個錯誤如何清楚的表述給用戶,基于應用內部的錯誤恢復規程,但一般來說都不會提示“郵件地址不存在”。這個錯誤響應成了死亡之門,它告訴別人用戶輸入沒有被正確的處理,這就為應用破解留下了可乘之機。
這個數據呈現在WHERE的從句中,讓我們以符合SQL規范的方式改變輸入試試,看看會發生什么。鍵入anything’ OR ‘x’=‘x, 結果如下:
[sql] view plaincopy因為應用不會思考輸入 – 僅僅構造字符串 - 我們使用單引號把WHERE從句的單一組成變成了雙組成,’x'=‘x’從句是恒成立的,無論第一個從句是什么。(有一種更好的方式來確保“始終為真”,我們隨后會接觸到)。
但與每次只返回單一數據的“真實”查詢不同,上面這個構造必須返回這個成員數據庫的所有數據。要想知道在這種情況下應用會做什么,唯一的方法就是嘗試,嘗試,再嘗試。我們得到了這個:
你的登錄信息已經被郵寄到了 random.person@example.com.
我們猜測這個地址是查詢到的第一條記錄。這個家伙真的會在這個郵箱里收到他忘記的密碼,想必他會很吃驚也會引起他的警覺。
我們現在知道可以根據自己的需要來篡改查詢語句了,盡管對于那些看不到的部分還不夠了解,但是我們注意到了在多次嘗試后得到了三條不同的響應:
前兩個響應是有效的SQL,最后一個響應是無效的SQL:當猜測查詢語句結構的時候,這種區別非常有用。
第一步是猜測字段名:我們合理的推測了查詢包含“email address”和“password”,可能也會有“US Mail address”或者“userid”或“phone number”這樣的字段。我們特別想執行 SHOW TABLE語句, 但我們并不知道表名,現在沒有比較明顯的辦法可以拿到表名。
我們進行了下一步。在每次測試中,我們會用我們已知的部分加上一些特殊的構造語句。我們已經知道這個SQL的執行結果是email地址的比對,因此我們來猜測email的字段名:
[sql] view plaincopy目的是假定的查詢語句的字段名(email),來試試SQL是不是有效。我不關心匹配的郵件地址是啥(我們用了個偽名’x’),’ ——’這個符號表示SQL注釋的起始。對于去除應用末尾提供的引號,這是一個很有效的方式,我們不用在乎我們屏蔽掉的是啥。
如果我們得到了服務器錯誤,意味著SQL有不恰當的地方,并且語法錯誤會被拋出:更有可能是字段名有錯。如果我們得到了任何有效的響應,我們就可以猜測這個字段名是正確的。這就是我們得到“email unknown”或“password was sent”響應的過程。
我們也可以用AND連接詞代替OR:這是有意義的。在SQL的模式映射階段,我們不需要為猜一個特定的郵件地址而煩惱,我們也不想應用隨機的泛濫的給用戶發“這是你的密碼”的郵件 - 這不太好,有可能引起懷疑。而使用AND連接郵件地址,就會變的無效,我們就可以確保查詢語句總是返回0行,永遠不會生成密碼提醒郵件。
提交上面的片段的確給了我們“郵件地址未知”的響應,現在我們知道郵件地址的確是存儲在email字段名里。如果沒有生效,我們可以嘗試email_address或mail這樣的字段名。這個過程需要相當多的猜測。
接下來,我們猜測其他顯而易見的名字:password,user ID, name等等。每次只猜一個字段,只要響應不是“server failure”,那就意味著我們猜對了。
[sql] view plaincopy在這個過程中,我們找到了幾個正確的字段名:
無疑還有更多(有一個線索是表單中的字段名),一陣挖掘后沒有發現更多了。但是我們依然不知道這些字段名的表名,它們在哪找到的?
尋找數據庫表名
應用的內建查詢指令已經建立了表名,但是我們不知道是啥:有幾個方法可以找到表名。其中一個是依靠subselect(字查詢)。
一個獨立的查詢
[sql] view plaincopy返回表里記錄的數量,如果表名無效,查詢就會失敗。我們可以建立自己的字符串來探測表名:
[sql] view plaincopy我們不關心到底有多少條記錄,只關心表名是不是正確。重復多次猜測以后,我們終于發現members是這個數據庫里的有效表名。但它是用在這個查詢里的么?所以我們需要另一個測試,使用table.field:實際查詢的部分只工作在這個表中,而不是只要表存在就執行。
[sql] view plaincopy當返回“Email unknown”時,就意味著我們的SQL注入成功了,并且我們正確的猜測出了表名。這對后面的工作很重要,但是我們暫時先試試其他的方法。
找用戶賬號
我們對members表的結構有了一個局部的概念,但是我們僅知道一個用戶名:任意用戶都可能得到“Here is your password”的郵件。回想起來,我們從未得到過信息本身,只有它發送的地址。我們得再弄幾個用戶名,這樣就能得到更多的數據。
首先,我們從公司網站開始找幾個人:“About us”或者“Contact”頁通常提供了公司成員列表。通常都包含郵件地址,即使它們沒有提供這個列表也沒關系,我們可以根據某些線索用我們的工具找到它們。
LIKE從句可以進行用戶查詢,允許我們在數據庫里局部匹配用戶名或郵件地址,每次提交如果顯示“We sent your password”的信息并且郵件也真發了,就證明生效了。
警告:這么做拿到了郵件地址,但也真的發了郵件給對方,這有可能引起懷疑,小心使用。
我們可以查詢email name或者full name(或者推測出來的其他信息),每次放入%通配符進行如下查詢:
[sql] view plaincopy記住盡管可能不只有一個“Bob”,但我們只能看到一條信息:建議精煉LIKE從句。
密碼暴力破解
可以肯定的是,我們能在登陸頁進行密碼的暴力破解,但是許多系統都針對此做了監測甚至防御。可能有的手段有操作日志,帳號鎖定,或者其他能阻礙我們行動的方式,但是因為存在未過濾的輸入,我們就能繞過更多的保護措施。
我們在構造的字符串里包含進郵箱名和密碼來進行密碼測試。在我們的例子中,我們用了受害者bob@example.com 并嘗試了多組密碼。
[sql] view plaincopy這是一條很好使的SQL語句,我們不會得到服務器錯誤的提示,只要我們得到“your password has been mailed to you”的提示信息,就證明我們已經得到密碼了。這時候受害人可能會警覺起來,但誰關心他呢,我們已經得到密碼了。
這個過程可以使用perl腳本自動完成,然而,我們在寫腳本的過程中,發現了另一種方法來破解系統。
迄今為止,我們沒做查詢數據庫之外的事,盡管SELECT是只讀的,但不代表SQL只能這樣。SQL使用分號表示結束,如果輸入沒有正確過濾,就沒有什么能阻止我們在字符串后構造與查詢無關的指令。
The most drastic example is:
這劑猛藥是這樣的:
[sql] view plaincopy第一部分我們準備了一個偽造的email地址——‘X’——我們不關心查詢結果返回什么:我們想要得到的只是我們自己構造的SQL指令。這次攻擊刪除了整個members表,這就不太好玩了。
這表明我們不僅僅可以切分SQL指令,而且也可以修改數據庫。這是被允許的。
我們已經了解了members表的局部結構,添加一條新紀錄到表里視乎是一個可行的方法:如果這成功了,我們就能簡單的用我們新插入的身份登陸到系統了。
不要太驚訝,這條SQL有點長,我們把它分行顯示以便于理解,但它依然是一條語句:
[sql] view plaincopy即使我們得到了正確的字段名和表名,但在成功攻擊之前我們還有幾件事需要了解:
在這個案例里,我們遇到了問題#4或#5,我們無法確定到底是哪個—— 因為用構造好的用戶名登陸進去的時候,返回了服務器錯誤的提示。盡管這就暗示了我們那些沒有構造的字段是必須的,但我們沒有辦法正確處理。
一個可行的辦法是猜測其他字段,但這是一個勞力費神的過程:盡管我們可以猜測其他“顯而易見”的字段,但要想得到整個應用的組織結構圖太難了。
我們最后嘗試了其他方式。
把密碼郵給我
我們意識到雖然我們無法添加新紀錄到members數據庫里,但我們可以修改已經存在的,這被證明是可行的。
從上一步得知 bob@example.com 賬戶在這個系統里,我們用SQL注入把數據庫中的這條記錄改成我們自己的email地址:
[sql] view plaincopy聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com