[Reverse] 36c3 - xmas_future

xmas_future

Points
96
Solves
95
Category
Reverse

Description:
Most people just give you a present for christmas, hxp gives you a glorious future.

If you’re confused, simply extract the flag from this 山葵 and you shall understand. :)
xmas_future-265eb0be46555aad.tar.xz (15.5 KiB)
by benediktwerner

So we are given a bunch of html/wasm file, after running the php web server with the run.sh file we are presented with a page:

The system will say the flag was correct if we insert the right flag, so let’s inspect the source:

Next step is to check hxp2019.js:

Check function is located at the WebAssembly file and its parameters are, the pointer offset to the string and the length of the string.

Instead of debugging the file through OP_CODES in the browser I found a tool that can decompile it and also convert it to a c file.

After cloning the repo I followed the instructions on readme to build and compile the project:

After building everything new executables are added to the bin/ folder:

1
2
3
$ ls bin/
spectest-interp* wasm2wat* wasm-interp* wasm-opcodecnt* wasm-validate* wat2wasm*
wasm2c* wasm-decompile* wasm-objdump* wasm-strip* wast2json* wat-desugar*

First I decompiled the file using wasm-decompile:

1
2
$ mkdir ../../challenge
$ ./wasm-decompile ../../hxp2019_bg.wasm -o ../../challenge/dec.js

And now lets convert also to c:

1
$ ./wasm2c ../../hxp2019_bg.wasm -o ../../challenge/hxp2019_bg.c

Lets see the new files created:

1
2
3
$ cd ../../challenge
$ ls
decompiled.js hxp2019_bg.c hxp2019_bg.h

Lets start first with the decompiled file which is a lot easier to read:

Looking at the hxp2019_check_h578f31d490e10a31

Checking the verifications of the rest of the characters:

Now that we know what is going on, we can start to look where the final check is located in the c generated files, so we can do dynamic analysis with gdb…

First let’s fix some wrong paths at hxp2019_bg.c from:

1
2
3
4
5
#include <math.h>
#include <string.h>

#include "../../challenge/hxp2019_bg.h"
...

To:

1
2
3
4
5
#include <math.h>
#include <string.h>

#include "hxp2019_bg.h"
...

The function in c is named hxp2019__check__h578f31d490e10a31:

1
2
3
4
5
6
7
8
9
10
11
static u32 hxp2019__check__h578f31d490e10a31(u32 p0, u32 p1) {
u32 l2 = 0, l3 = 0, l4 = 0, l5 = 0, l6 = 0, l7 = 0, l8 = 0, l9 = 0,
l10 = 0;
FUNC_PROLOGUE;
u32 i0, i1, i2;
...
i1 &= i2;
i0 = i0 == i1; // final check is here we might want to put a breakpoint here.
if (i0) {goto L7;}
...
}

Putting a break point there is a solution but this makes a lot of effort to make the conditions always true and check the correct character.

We could also write a gdbscript or r2script but once again takes a lot of time…

Since this c files are compilable we can just modify the source code to print the flag characters and turn this condition to always return true.

But first we need to learn how to compile this kind of auto generated files, an example can be found at the wabt directory:

1
2
3
4
5
6
7
8
9
10
11
# dependencies
$ ls ../wabt/wasm2c/
wasm-rt.h wasm-rt-impl.c wasm-rt-impl.h
$ cp ../wabt/wasm2c/wasm-rt.h .
$ cp ../wabt/wasm2c/wasm-rt-impl.c .
$ cp ../wabt/wasm2c/wasm-rt-impl.h .
# Copying fac example files
$ ls ../wabt/wasm2c/examples/fac/
fac.c fac.h fac.wasm fac.wat main.c
$ cp ../wabt/wasm2c/examples/fac/* .
$ rm fac.c fac.h fac.wasm

Now looking at the example of main.c file from fac:

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
28
#include <stdio.h>
#include <stdlib.h>

/* Uncomment this to define fac_init and fac_Z_facZ_ii instead. */
/* #define WASM_RT_MODULE_PREFIX fac_ */

#include "fac.h" // Change this to hxp2019_bg.h

int main(int argc, char** argv) {
/* Make sure there is at least one command-line argument. */
if (argc < 2) return 1;

/* Convert the argument from a string to an int. We'll implictly cast the int
to a `u32`, which is what `fac` expects. */
u32 x = atoi(argv[1]);

/* Initialize the fac module. Since we didn't define WASM_RT_MODULE_PREFIX,
the initialization function is called `init`. */
init();

/* Call `fac`, using the mangled name. */
u32 result = Z_facZ_ii(x); // We need to change this function too the real name is located at hxp2019_bg.h

/* Print the result. */
printf("fac(%u) -> %u\n", x, result);

return 0;
}

As you can see we need to adapt the example main function the current file we want to debug to find the correct Z_xxxZ function we can look at the header file generated hxp2019_bg.h:

The adapted main.c file:

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
28
29
30
31
32
33
#include <stdio.h>
#include <stdlib.h>

/* Uncomment this to define fac_init and fac_Z_facZ_ii instead. */
/* #define WASM_RT_MODULE_PREFIX fac_ */

#include "hxp2019_bg.h"
/*
b hxp2019_bg.c:2268
b hxp2019_bg.c:2434
r 1048576 50
*/
int main(int argc, char** argv) {
/* Make sure there is at least one command-line argument. */
if (argc < 2) return 1;

/* Convert the argument from a string to an int. We'll implictly cast the int
to a `u32`, which is what `fac` expects. */
u32 x = atoi(argv[1]);
u32 y = atoi(argv[2]);

/* Initialize the fac module. Since we didn't define WASM_RT_MODULE_PREFIX,
the initialization function is called `init`. */
init();

/* Call `fac`, using the mangled name. */
u32 result = Z_checkZ_iii(x,y); // 1048576 50

/* Print the result. */
printf("check(%u,%u) -> %u\n", x,y, result);

return 0;
}

Let’s use gcc to compile everything:

1
2
3
4
5
6
7
$ gcc -m32 -ggdb wasm-rt-impl.c -o wasm-rt-impl.o -c
$ gcc -m32 -ggdb hxp2019_bg.c -o hxp2019_bg.o -c
$ gcc -m32 -ggdb main.c -o main.o -c
# linking everything
$ gcc -m32 -ggdb -o main main.o hxp2019_bg.o wasm-rt-impl.o
$ ./main 1048576 50
check(1048576,50) -> 0

Generating a make file so we don’t have to repeat ourselfs over and over:

1
2
3
4
5
6
7
8
9
10
11
12
13
CC=gcc
CFLAGS=-I. -ggdb -m32
DEPS = hxp2019_bg.h wasm-rt.h wasm-rt-impl.h
OBJ = hxp2019_bg.o wasm-rt-impl.o main.o

%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)

main: $(OBJ)
$(CC) -o $@ $^ $(CFLAGS)
clean:
rm *.o
rm -f main

Now we just need do make clean and make to compile everything:

1
2
3
4
5
6
7
8
$ make clean
rm *.o
rm -f main
$ make
gcc -c -o hxp2019_bg.o hxp2019_bg.c -I. -ggdb -m32
gcc -c -o wasm-rt-impl.o wasm-rt-impl.c -I. -ggdb -m32
gcc -c -o main.o main.c -I. -ggdb -m32
gcc -o main hxp2019_bg.o wasm-rt-impl.o main.o -I. -ggdb -m32

Note that the flag -m32 is to compile the binary in 32 bits and the -ggdb is to add symbols to gdb so we can debug everything and watch the source code instead of only viewing the assembly :).

Now advancing to change hxp2019_bg.c file to print us the flag on execution we need to populate the input string before doing the checks, also that loop we investigated before is only doing the checks inside of the flag brackets hxp{…}, the rest of the flag is being checked somewhere else in the code, we don’t really need to know where, we just need to populate the begining and the end with the right characters and the rest with As…

Let’s do a function that does that:

1
2
3
4
5
6
7
8
9
10
11
void populate() {
memory.data[1048576u+0] = 'h'; // i32_store((&memory), (u64)(1048576u + 0), 'h');
memory.data[1048576u+1] = 'x';
memory.data[1048576u+2] = 'p';
memory.data[1048576u+3] = '{';

for (int i = 4; i < 49; ++i) {
memory.data[1048576u+i] = 'A';
}
memory.data[1048576u+49] = '}';
}

We add this call before the check call at static u32 check(u32 p0, u32 p1):

1
2
3
4
5
static u32 check(u32 p0, u32 p1) {
...
populate();
i0 = hxp2019__check__h578f31d490e10a31(i0, i1);
...

Now modifying hxp2019__check__h578f31d490e10a31:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static u32 hxp2019__check__h578f31d490e10a31(u32 p0, u32 p1) {
printf("%s","hxp{"); // print flag header
...
i0 = i32_load8_u((&memory), (u64)(i0));
i1 = l6;
i2 = 255u;
i1 &= i2;
i1 = i0; // make the condition always true
printf("%c", i1); // print current flag character
i0 = i0 == i1; // condition
if (i0) {goto L7;} // continue with the loop
...
puts("}");
return i0;
}

You can download the files here.

Now compiling everything with make:

1
2
3
4
5
$ make
gcc -c -o hxp2019_bg.o hxp2019_bg.c -I. -ggdb -m32
gcc -c -o wasm-rt-impl.o wasm-rt-impl.c -I. -ggdb -m32
gcc -c -o main.o main.c -I. -ggdb -m32
gcc -o main hxp2019_bg.o wasm-rt-impl.o main.o -I. -ggdb -m32

Running and getting the flag:

1
2
3
$ ./main 1048576 50
hxp{merry_xmas___github.com/benediktwerner/rewasm}
check(1048576,50) -> 1