URL編碼與XSS


0x00 背景

最近遇到一個雙重編碼繞過過濾的xss漏洞,成功在大佬的指點下彈出成功之后記錄一下學習。

0x01 URL編碼

一個URL的形式如下:

foo://example.com:8042/over/there?name=ferret#nose
協議 域名 端口 路徑 search參數 hash參數

通常來說,如果某種文本需要編碼,說明他並不適合傳輸。原因多種多樣,或壓縮尺寸,或隱藏隱私數據……

對於URL來說,之所以要進行編碼,一方面是因為URL中有些字符會引起歧義。例如,URL參數字符串中使用key=value鍵值對這樣的形式來傳參,鍵值對之間以&符號分隔,如/s?name=chen&city=beijing。但是這個時候如果value字符串包含了=或者&,那么服務器解析肯定會出錯,因此必須將引起歧義的&和=符號進行編碼。

另一方面,因此URL編碼采用的是ASCII碼,這也就是說你不能在Url中包含任何非ASCII字符,例如中文。否則如果客戶端瀏覽器和服務端瀏覽器支持的字符集不同的情況下,中文可能會造成問題。

Javascript中對於URL編碼主要有以下幾個函數:

escape(string):該方法不會對 ASCII 字母和數字進行編碼,也不會對下面這些 ASCII 標點符號進行編碼: * @ - _ + . / 。其他所有的字符都會被轉義序列替換。
escape("http://www.xxx.com/My first/a?name=chen&city=beijing")======>>>>>> http%3A//www.xxx.com/My%20first/a%3Fname%3Dchen%26city%3Dbeijing

encodeURI(string):該方法不會對 ASCII 字母和數字進行編碼,也不會對這些 ASCII 標點符號進行編碼: - _ . ! ~ * ' ( ) 。該方法的目的是對 URI 進行完整的編碼,因此對以下在 URI 中具有特殊含義的 ASCII 標點符號,encodeURI() 函數是不會進行轉義的:;/?:@&=+$,#
encodeURI("http://www.xxx.com/My first/a?name=chen&city=beijing")========>>>>>> http://www.xxx.com/My%20first/a?name=chen&city=beijing

encodeURIComponent(string):該方法不會對 ASCII 字母和數字進行編碼,也不會對這些 ASCII 標點符號進行編碼: - _ . ! ~ * ' ( ) 。其他字符(比如 :;/?:@&=+$,# 這些用於分隔 URI 組件的標點符號),都是由一個或多個十六進制的轉義序列替換的。
encodeURIComponent("http://www.xxx.com/My first/a?name=chen&city=beijing")=======>>>>>> http%3A%2F%2Fwww.xxx.com%2FMy%20first%2Fa%3Fname%3Dchen%26city%3Dbeijin

0x02 URL雙重編碼

那么為什么需要雙重編碼呢?因為以上未討論value為中文的情況。

當value的值為中文字符時,提交到后台經過一次encodeURI()對中文URL參數進行編碼時,“測試”二字會被轉換為“%E6%B5%8B%E8%AF%95”。 但是編碼后的字符串信息,瀏覽器機制會認為“%”是一個轉義字符,它並未將“%”認為是個普通字符。因此需要使用encodeURI進行二次編碼
操作: encodeURI(encodeURI("測試"));
經過以上操作處理后的"測試"變為”%25E6%25B5%258B%25E8%25AF%2595“,通過再次編碼原有被瀏覽起解析為轉義字符的”%“被再次編碼,轉換成了普通字符轉”%25“。
代碼邏輯如下:
首先,在前端頁面准備參數的時候,需要對中文參數進行encode處理:
var
url = '/?page_name='+encodeURI(encodeURI("測試")); window.open(url);

后端處理:
String starName = java.net.URLDecoder.decode(request.getParameter("page_name"),"UTF-8");
前端在進行encode編碼為什么用了兩次encodeURI,而服務器后端在解碼時只解了一次?這是因為容器會默認幫你解一次碼。
既然容器會默認解一次碼,那么為什么不只編碼一次然后讓容器自動解析一次就好呢?即直接在前端只進行一次encode,服務端程序直接request.getParameter(“page_name”) ?
這是因為容器默認解碼時采用的是容器的默認編碼,可能是UTF-8,GBK,也可能是其他編碼方式。這與你當前的應用的編碼方式未必一致。所以你直接獲取的話可能會出現亂碼。
 
當然也可以通過修改容器的默認編碼,從而實現“前端一次encode——后端直接獲取”的途徑獲取中文參數。例如:在Tomcat下默認編碼修改方式:修改%TOMCAT_HOME%/conf/server.xml,找到這行代碼:
<Connector port="80" protocol="HTTP/1.1" redirectPort="8449" connectionTimeout="20000"/>
在后面可以追加URIEncoding屬性,即:
<Connector port="80" protocol="HTTP/1.1" redirectPort="8449" connectionTimeout="20000" URIEncoding="UTF-8"/>
如果不方便改容器默認編碼方式,或者應用程序本身就有多種編碼方式的話,還是采取“前端兩次encode——后端一次decode”的途徑獲取中文參數吧。

0x03 URL編碼與XSS

 XSS原理這里就不講了。這里直接使用自己遇到的一個反射性xss的例子,問題參數出在url的search參數上。

首先是是在搜索框中插入尖括號,可以看到這是在黑名單之中的:

然后輸入正常的查詢字段發現會被編碼並使用get方式進行提交,所以就在對應的參數后面進行提交,還是首先測試尖括號的url編碼是否被過濾,經過測試發現經過一次URL編碼的%3c依舊會被過濾,但是經過二次URL編碼的%253c就能夠在搜索框與頁面正常顯示:

這里就通過查看源代碼和查看元素發現都被正常解析了,這也意味着可以出發XSS了,一個觸發點時搜索框中,一個觸發點時搜索[<]的中括號里面。

這里繼續嘗試在搜索的中括號進行XSS,將payload  <img src=1 onerror=alert(1)>進行2次URL編碼如下:%253Cimg%2520src%253D1%2520onerror%253Dalert%25281%2529%253E

可以看到<img>標簽被正確解析了,但是onerror 的alert卻沒有被觸發,所以后台對於alert應該是有防護的,這里嘗試雙寫繞過即可觸發:

還有一個觸發點就是在輸入框利用引號閉合,增加onclick或者onmouseover等事件進行觸發。

0x04 繞過原理

首先就是瀏覽器在前端與后端都有過濾措施。

首先前端在輸入框對於輸入的參數校驗是很嚴格的,對於未經編碼與經過編碼的特殊符號都能夠做到完整過濾,但是卻使用了get進行提交,導致攻擊者可以任意修改url參數進行繞過前端驗證。

其次后端的驗證對於非法字符的檢測與過濾機制就太不嚴格了,當我們傳輸只經過一次URL編碼的payload時,后台可以經過解碼可以檢測出敏感輸入,但是在經過2次URL編碼的時候就無法檢測出惡意的符號內容。

 

 

 

 

 

 

 

 

 

參考鏈接:

https://blog.csdn.net/zmx729618/article/details/51381655

https://www.cnblogs.com/longling2344/p/5476785.html

https://blog.csdn.net/zhxtpray/article/details/52440076

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM