Post

Web - Ding-o-Tron - USCG 2024

A WASM web challenge developed by tsuto for Season IV of the U.S. Cyber Open.

Description

What came first? The ding…or the flag?

Solution

Summary:

  1. Every time the button is clicked, clickWrapper.func1 is called in ding.wasm.
  2. Stepping through clickWrapper, the memory address storing the current number of clicks can be found.
  3. Modifying the value to be over 9000 causes a taunting message to be printed to the console.
  4. Digging through the wasm code, the functions flagWrapper.func1 and actualFlagWrapper.func1 are found.
  5. Breaking on flagWrapper.func1 and viewing the callstack shows an indirect call from handleEvent.
  6. Indirect calls use the function table to jump to the correct function. Modifying the function table to instead point to actualFlagWrapper.func1 will cause it to be called, printing the flag.

Pepe Silvia How I feel explaining this

Finding and Overwriting the Click Count

Looking at the HTML source shows that when the bell is clicked it calls window.ding().

1
<img src="/static/assets/img/bell.png" role="button" onclick="window.ding()">

I couldn’t find a function for ding in the JavaScript source so I turned to ding.wasm. Inside ding.wasm I found the functions dingWrapper and dingWrapper.func1. Setting a breakpoint on both, I clicked the bell and started to step through the function until I found the value of the current number of clicks being loaded and incremented by 1.

1
2
3
4
5
6
7
8
i64.const 1259744
i32.wrap_i64
i64.const 1259744
i32.wrap_i64
i64.load
i64.const 1
i64.add
i64.store

The code performs the following actions:

  1. Push 1259744 onto the stack
  2. Push 1259744 onto the stack
  3. Load data memory address 1259744
    • This replaces the second instance of 1259744 on the stack with the current number of clicks.
  4. Increment the number of clicks by one.
  5. Store the new value back at 1259744.

Using this location information, I can overwrite the number of clicks in memory to any value. To modify the value, I take advantage of the function storeValue in wasm_exec.js. To execute this function, it must be in scope, so I set a breakpoint in wasm_exec.js and ran storeValue(1259744, 9000) in the console.

The memory address is now storing 9000 clicks which means I should get the flag now right?

[LOL] Did you think it would be that easy? Can you find my secret hidden function?

Wrong.

Overwriting the Function Table Pointer for flagWrapper.func1

Knowing that there was supposed to be a hidden function, I started looking through the wasm code again for any mention of flag, and came across two interesting functions, flagWrapper.func1 and actualFlagWrapper.func1. Obviously, actualFlagWrapper.func1 is what I want to be called, so to figure out how flagWrapper.func1 is called, I set a breakpoint on it and viewed the callstack.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$main.flagWrapper.func1
$syscall_js.handleEvent
$runtime.handleEvent
$wasm_export_resume
_resume
(anonymous)
syscall/js.valueCall
$syscall_js.valueCall
$syscall_js.value.Call
$main.dingWrapper.func1
$syscall_js.handleEvent
$runtime.handleEvent
$wasm_export_resume
_resume
(anonymous)
onClick

The callstack shows that flagWrapper was called by $syscall_js.handleEvent. Viewing the code where flagWrapper was called reveals a call_indirect instruction. Now, I had no idea how this works, so I started to research and came across this writeup which has a small blurb about function tables and indirect function calls.

https://medium.com/tenable-techblog/coding-a-webassembly-ctf-challenge-5560576e9cb7#bd80

Using this new information, I found the function table in the debug menu under the module dropdown. To find the index of flagWrapper in the function table, I set a breakpoint on the indirect call and took note of the value at the top of stack; 5503. With this information, I looked at the function table around index 5503 and found actualFlagWrapper at index 5502.

Knowing this, I assumed that overwriting the flagWrapper at index 5503 with actualFlagWrapper would print the flag. Running the following command in the console successfully changed the function pointer which printed the flag the next time I clicked the bell.

1
2
3
4
> tables.$table0.set(5503,tables.$table0.get(5502))
undefined
> tables.$table0.get(5502)
ƒ $main.actualFlagWrapper.func1() { [native code] }

Flag: SIVUSCG{d1ng_d1ng_d1ng_d1ng}

WTF Did I Just Read?!

After I solved the challenge a friend and I were discussing the solve, and come to find out, there is a reason this challenge has the most solves in the web category.

1
if you go to console and type window. it'll come up with a list of functions you can call, I just scrolled thru it and there was a function that gave the flag lol

Discord message from friend

The Intended Solve

  1. Notice that there is a commented line // giveFlag() in main.js.
  2. Run that in the console and get the taunting message.
  3. List all of the functions in the current context by running window.
  4. Find the function superSecretFunction_d81d7981b32efd3d() and run it to get the flag.

my solve is cooler

This post is licensed under CC BY 4.0 by the author.