## 0CTF 2020 - W (Reversing 523)##### 29/06 - 01/07/2020 (48hr)___
### Description
-
___
### Solution
This is [WebAssembly](https://webassembly.org/) challenge:```ispo@ispo-glaptop2:~/ctf/0ctf_2020/W$ file wwww: WebAssembly (wasm) binary module version 0x1 (MVP)
ispo@ispo-glaptop2:~/ctf/0ctf_2020/W$ ./wasm3 wwSecret: ?ispohmmm...
ispo@ispo-glaptop2:~/ctf/0ctf_2020/W$ rewasm/target/release/rewasm ww > decomp.wasm```
The good news about [WebAssembly](https://webassembly.org/) binaries is that they can be decompiled.A quick search shows several available decompilers.We will use [ReWasm](https://github.com/benediktwerner/rewasm) and[JEB Pro](https://www.pnfsoftware.com/). Let's load `ww` binary on JEB Pro. Program starts fromfunction `__f13` (we can easily find that by checking the XREFs to the `Secret: ?` string). Weclean it up a little and we rename the functions:```cint main__f13(int param0, int param1) {    int v0 = __g0 - 112;
    __g0 -= 112;    *(int*)(v0 + 108) = 0;    *(int*)(v0 + 104) = param0;    *(int*)(v0 + 100) = param1;    setvbuf__f264(0, 2, 0, gvar_5B40_);    setvbuf__f264(0, 2, 0, gvar_5B44);    *(int*)v0 = 0;    int fp = open__f246(v0, 0, "/dev/urandom");    *(int*)(v0 + 96) = fp;    *(long long*)(v0 + &gvar_20_secret) = 0L;    *(long long*)(v0 + 88) = 0L;    *(long long*)(v0 + 80) = 0L;    *(long long*)(v0 + 72) = 0L;    *(long long*)((int*)((int)&gvar_20_secret + v0) + &gvar_8) = 0L;    *(long long*)((int*)((int)&gvar_18 + v0) + &gvar_8) = 0L;    *(unsigned long long*)((int*)((int)&gvar_10 + v0) + &gvar_8) = 0L;    *(long long*)((int*)((int)&gvar_8 + v0) + &gvar_8) = 0L;    print__f258(0x5615);  // Secret:    int v2 = fgets__f263(gvar_5B40_, 64, v0 + &gvar_20_secret);  // secret is up to 64 bytes    if(v2 == 0) {        *(int*)(v0 + 108) = -1;    }    else {        int secret_len = strlen__f292(v0 + &gvar_20_secret);        *(char*)(v0 + secret_len + &gvar_20_secret) = 0;        int buf = calloc__f238(1, (int)(gvar_5604_buflen + 1));  // 0x5201        *(int*)((int)&gvar_1C_buf + v0) = buf;        if(*(unsigned int*)((int)&gvar_1C_buf + v0) == 0) {            *(int*)(v0 + 108) = -1;        }        else {            memcpy__f291(gvar_5604_buflen, &ENC_BUF_START, *(int*)((int)&gvar_1C_buf + v0));            unpack__f42(v0 + &gvar_20_secret, gvar_5604_buflen, *(int*)((int)&gvar_1C_buf + v0));            int v5 = *(int*)((int)&gvar_1C_buf + v0);            int buf = gvar_5604_buflen;            int len = strlen__f292("Welcome to 0CTF/TCTF 2020! Have a g00d time");  // *0x5604 is 0x5200            int start = memmem__f293(len, "Welcome to 0CTF/TCTF 2020! Have a g00d time", buf, v5);            if(!start) {                print__f258("hmmm...");                *(int*)(v0 + 108) = 0;            }            else {                print__f258("Loading...");  // unpacking successful                int v9 = load_wasm__f33(gvar_5604_buflen, *(int*)((int)&gvar_1C_buf + v0));                *(int*)((int)&gvar_18 + v0) = v9;                int v10 = init_interpreter__f43(*(int*)((int)&gvar_18 + v0));                *((int*)((int)&gvar_10 + v0) + 1) = v10;                load_data_section__f46(*((int*)((int)&gvar_10 + v0) + 1));                load_globals__f47(*((int*)((int)&gvar_10 + v0) + 1));                print__f258("Go Go Go!!");                int res = run__f52(22, *((int*)((int)&gvar_10 + v0) + 1)); // start execution from function 22                *(int*)((int)&gvar_10 + v0) = res;                free_interpreter__f45(*((int*)((int)&gvar_10 + v0) + 1));                free_wasm_obj__f41(*(int*)((int)&gvar_18 + v0));                free__f236(*(int*)((int)&gvar_1C_buf + v0));                if(*(int*)(v0 + 96) != -1) {  // fp from urandom                    close__f242(*(int*)(v0 + 96));                }                *(int*)(v0 + 108) = *(int*)((int)&gvar_10 + v0);            }        }    }
    int result = *(int*)(v0 + 108);    __g0 = v0 + 112;    return result;}```
Unfortunately, JEB does not recognize standard functions (for instance `memcpy` is shown as `__f291`in JEB). However, ReWasm can determine function names as you can see:```c    puts(22058);    var_32 = load<i32>(var_2 + 28);    var_33 = load<i32>(0 + 22020);    var_34 = load_wasm(var_32, var_33);    store<i32>(var_2 + 24, var_34)    var_35 = load<i32>(var_2 + 24);    var_36 = init_interpreter(var_35);    store<i32>(var_2 + 20, var_36)    var_37 = load<i32>(var_2 + 20);    load_data_section(var_37);    var_38 = load<i32>(var_2 + 20);    load_globals(var_38);    puts(22069);    var_39 = load<i32>(var_2 + 20);    var_40 = run(var_39, 22);    store<i32>(var_2 + 16, var_40)    var_41 = load<i32>(var_2 + 20);    free_interpreter(var_41);    var_42 = load<i32>(var_2 + 24);    free_wasm_obj(var_42);    var_43 = load<i32>(var_2 + 28);    free(var_43);    var_44 = load<i32>(var_2 + 96);    var_45 = var_44 != -1 & 1;    if var_45 != 0 {        var_46 = load<i32>(var_2 + 96);        close(var_46);    }    var_47 = load<i32>(var_2 + 16);    store<i32>(var_2 + 108, var_47)```
Therefore, we simply rename JEB functions based on ReWasm output. Let's now see how program works.First it computes the length of the secret input and then allocates a buffer of size `5201h` bytes:```cint secret_len = strlen__f292(v0 + &gvar_20_secret);*(char*)(v0 + secret_len + &gvar_20_secret) = 0;
int buf = calloc__f238(1, (int)(gvar_5604_buflen + 1));  // 0x5201*(int*)((int)&gvar_1C_buf + v0) = buf;```
```.data:00005604    gvar_5604_buflen dd 5200h                                  ; xref: main__f13+2A2h (data-adv) / main__f13+280h (data-adv) / main__f13+1D5h (data-adv) / main__f13+255h (data-adv) / main__f13+31Ch (data-adv)```
Then program copies `5200h` bytes from address `400h` (`ENC_BUF_START`) into buffer:```cmemcpy__f291(gvar_5604_buflen, &ENC_BUF_START, *(int*)((int)&gvar_1C_buf + v0));```
Let's see teh contents at address `400h`:```.data:00000400 ENC_BUF_START    db 9Ah.data:00000401        db B6h, A9h, E9h, 95h, CFh, BEh, DCh, B7h, EDh, CBh, F3h, 9Bh, "z6h".data:00000410        db '!', 1Eh, 0, 'f', 81h, 'g', 19h, FFh, AFh, 97h, 'y', 0, F9h, A6h, AEh, 11h.data:00000420        db 0Bh, C5h, ACh, 1Eh, 3, 'T', A6h, 'E', A0h, C8h, 97h, D4h, ':', DBh, 14h, ABh......data:00005200        db 9Ah, BFh, F1h, C7h, FBh, " 'e", 0Eh, ',', 13h, 'K', 2, "`mN".data:00005210        db "N~", 0, "@ g", 19h, '8', 8, "1|", 2, "\`", 11h, '/'.data:00005220        db "K$K?", C7h, F7h, A6h, CBh, EBh, 83h, BCh, C4h, AEh, 8Dh, 'C', 0Bh.....```
Then program invokes a special `unpack` function that takes as input i) the secret (`param0`),ii) the size of the buffer which is `0x5200` (`param1`) and iii) the address of the newlyallocated buffer (`param2`):```cunpack__f42(v0 + &gvar_20_secret, gvar_5604_buflen, *(int*)((int)&gvar_1C_buf + v0));```
Finally, program verifies if unpacking is successfully or not, by searching in the "unpacked" bufferfor the presence of the `Welcome to 0CTF/TCTF 2020! Have a g00d time` string. If this string is notfound the `hmmm...` message is printed and the program terminates. Otherwise, it moves on with the execution of the second stage payload:```cint v5 = *(int*)((int)&gvar_1C_buf + v0);int buf = gvar_5604_bufaddr;int len = strlen__f292("Welcome to 0CTF/TCTF 2020! Have a g00d time");  // *0x5604 is 0x5200int start = memmem__f293(len, "Welcome to 0CTF/TCTF 2020! Have a g00d time", buf, v5);if(!start) {    puts__f258("hmmm...");    *(int*)(v0 + 108) = 0;}else {    print__f258("Loading...");  // unpacking successful    /* .... */}```                
### Reversing the Unpacking Routine
Unfortunately, JEB fails to decompile `unpack`, so we switch to ReWasm:```cvoid unpack__f42(int par0, int par1, int par2) {    // Decompilation error}
```
```c// Function 42fn unpack(i32 arg_0, i32 arg_1, i32 arg_2) {    var_3 = 0;    /* ... */    var_42 = 0;    var_43 = global_0 - 48;    store<i32>(var_43 + 44, arg_0)                      // buf    gl0_b = global_0 - 48;    store<i32>(gl0_b + 40, arg_1)                       // buflen = 0x5200    gl0_c = global_0 - 48;    store<i32>(gl0_c + 36, arg_2)                       // flag    gl0_d = global_0 - 48;    var_47 = load<i32>(gl0_d + 40);                     // g40 = buflen    if (var_47 & 511) == 0 {                            // 0x5200 & 0x1FF == 0 ? yes!        gl0_e = global_0 - 48;        var_49 = load<i32>(gl0_e + 40);                 // g40 = buflen        gl0_e = global_0 - 48;        var_51 = var_49 >>u 9;                          // buflen >> 9        store<i32>(gl0_e + 32, var_51)                  // g32 = buflen >> 9        gl0_g = global_0 - 48;        var_53 = load<i32>(gl0_g + 44);        gl0_h = global_0 - 48;        store<i32>(gl0_h + 28, var_53)                  // g28 = buf        gl0_i = global_0 - 48;        store<i32>(gl0_i + 24, 0)                       // g24 = i = 0        while true {            gl0_j = global_0 - 48;            var_57 = load<i32>(gl0_j + 24);             // i            gl0_k = global_0 - 48;            var_59 = load<i32>(gl0_k + 32);             // buflen >> 9            var_60 = var_57 > 9) ?            if var_60 == 0 {                break;                                  // if not break            }            gl0_l = global_0 - 48;            store_8<i32>(gl0_l + 23, 255)               // g23 = 255 = a            gl0_m = global_0 - 48;            store<i32>(gl0_m + 16, 0)                   // g16 = j = 0            while true {                gl0_n = global_0 - 48;                var_64 = load<i32>(gl0_n + 16);         // g16 = j                var_65 = var_64 (gl0_o + 16);         // j                var_3 = var_67;                var_4 = 31;                var_5 = var_67 & 31;                gl0_p = global_0 - 48;                var_69 = var_67 & 31;                store_8<i32>(gl0_p + 15, var_69)        // g15 = j & 0x1F                gl0_q = global_0 - 48;                var_71 = load<i32>(gl0_q + 36);         // g36 = flag                var_6 = var_71;                gl0_r = global_0 - 48;                var_73 = load_8u<i32>(gl0_r + 15);      // g15 = j & 0x1F                var_7 = var_73;                var_8 = 255;                var_9 = var_73 & 255;                var_10 = var_71 + (var_73 & 255);       // & flag + (j & 0x1F)                var_74 = load_8u<i32>(var_10);          // f = flag[j & 0x1F]  => Flag is 31 characters!                var_11 = var_74;                var_12 = 255;                var_13 = var_74 & 255;                gl0_s = global_0 - 48;                var_76 = load_8u<i32>(gl0_s + 23);      // g23 = a                var_14 = var_76;                var_15 = 24;                var_16 = var_76 << 24;                var_17 = var_76 << 24 >>s 24;           // (a << 24) >> 24 ? ~> a                var_18 = var_74 & 255 ^ var_17;         // flag[j & 0x1F] ^ a                gl0_t = global_0 - 48;                var_78 = load<i32>(gl0_t + 28);         // g28 = buf                var_19 = var_78;                gl0_u = global_0 - 48;                var_80 = load<i32>(gl0_u + 16);         // g16 = j                var_20 = var_80;                var_21 = var_78 + var_80;                var_81 = load_8u<i32>(var_78 + var_80); // buf[j]                var_22 = var_81;                var_23 = 255;                var_24 = var_81 & 255;                var_25 = var_18 ^ var_81 & 255;         // flag[j & 0x1F] ^ a ^ buf[j]                gl0_v = global_0 - 48;                store_8<i32>(gl0_v + 14, var_25)        // g14 = flag[j & 0x1F] ^ a ^ buf[j]                gl0_w = global_0 - 48;                var_84 = load<i32>(gl0_w + 28);         // g28 = buf                var_26 = var_84;                gl0_x = global_0 - 48;                var_86 = load<i32>(gl0_x + 16);         // g16 = i                var_27 = var_86;                var_28 = var_84 + var_86;                var_87 = load_8u<i32>(var_84 + var_86); // buf[j]                var_29 = var_87;                gl0_y = global_0 - 48;                store_8<i32>(gl0_y + 23, var_87)        // g23 = a = buf[j]                gl0_z = global_0 - 48;                var_90 = load_8u<i32>(gl0_z + 14);      // g14 = flag[j & 0x1F] ^ a ^ buf[j]                var_30 = var_90;                var_31 = 255;                var_32 = var_90 & 255;                gl0_aa = global_0 - 48;                var_92 = load_8u<i32>(gl0_aa + 15);     // g15 = j & 0x1F                var_33 = var_92;                var_34 = 255;                var_35 = var_92 & 255;                var_93 = var_92 & 255;                var_36 = (var_90 & 255) - var_93;       // flag[j & 0x1F] ^ a ^ buf[j] - (j & 0x1F)                gl0_ab = global_0 - 48;                var_95 = load<i32>(gl0_ab + 28);        // g28 = buf                var_37 = var_95;                gl0_ac = global_0 - 48;                var_97 = load<i32>(gl0_ac + 16);        // g16 = j                var_38 = var_97;                var_39 = var_95 + var_97;                store_8<i32>(var_95 + var_97, var_36)   // buf[j] = flag[j & 0x1F] ^ a ^ buf[j] - (j & 0x1F)                gl0_ad = global_0 - 48;                var_99 = load<i32>(gl0_ad + 16);        // g16 = j                var_40 = var_99;                var_41 = 1;                var_42 = var_99 + 1;                    // j + 1                gl0_ae = global_0 - 48;                var_101 = var_99 + 1;                store<i32>(gl0_ae + 16, var_101)        // g16 = j = j + 1 (++j)            }            var_102 = global_0 - 48;            var_103 = load<i32>(var_102 + 28);          // g28 = buf            var_104 = global_0 - 48;            var_105 = var_103 + 512;            store<i32>(var_104 + 28, var_105)           // g28 = buf = buf + 512 (move on the next chunk)            var_106 = global_0 - 48;            var_107 = load<i32>(var_106 + 24);          // g24 = i = outer iterator            var_108 = global_0 - 48;            var_109 = var_107 + 1;                      // ++i            store<i32>(var_108 + 24, var_109)           // g24 = i + 1        }    }    return;}```
Let's decompile this function into Python:```pythondef unpack(buf, buflen, secret):    """Unpacks the WebAssebmly stage 2 payload."""    for i in range(0, buflen >> 9):        a = 255        for j in range(512):            b = ord(secret[j & 0x1F]) ^ a ^ buf[i*512 + j]            a = buf[i*512 + j]
            buf[i*512 + j] = (b - (j & 0x1F)) & 0xFF
    return buf```
That's quite simple isn't it? **Each byte of the buffer is XORed with the next character from the secret and the previous plaintext byte of the buffer**. We also know (from `j & 0x1F` part) thatthe **secret is 32 characters long**.
### Cracking the Unpacking Routine
The question now, is how to find the secret? We know that somewhere in the decrypted buffer, thereis the string `Welcome to 0CTF/TCTF 2020! Have a g00d time`, so we can leverage that to do a*Known Plaintext Attack*. If we know the position of the plaintext string in the buffer, we canapply the reverse algorithm and recover the secret. Since the buffer is small only (`5200h` bytes),we can brute force the starting position and check which decryption yields to a meaningful secret(i.e., is ASCII printable). Here's how we do that:```python# Brute force plaintext location inside buf.    for i in range(len(buf) - len(plain)):    secret = [0]*32
    for k in range(0, len(plain)):        j = (i + k) & 0x1F
        b = (plain[k] + (j & 0x1F)) & 0xFF        a = buf[i + k - 1] if i + k - 1 > 0 else 255        secret[j] = b ^ a ^ buf[i + k]     if all(x >= 0x20 and x <= 0x7e for x in secret):        secret = ''.join(chr(s) for s in secret)        print(f'[+] Secret FOUND at offset {i} : {secret}')        break```
The above code finds a unique solution at offset `20346`: `eNj0y_weba5SemB1Y.lstrip("web")!`
We try the secret to see if it works:```ispo@ispo-glaptop2:~/ctf/0ctf_2020/W$ ./wasm3 wwSecret: ?eNj0y_weba5SemB1Y.lstrip("web")!Loading...Go Go Go!!aoshine~ What's up?1. add pair2. dump pair3. check pair4. exit
```
For more details please refer to the [w_crack.py](./w_crack.py) script.
### Reversing Second Stage Payload
If we go back to main (`__f13`) we can see what program does with the decrypted buffer:```c    print__f258("Loading...");  // unpacking successful    int v9 = load_wasm__f33(gvar_5604_buflen, *(int*)((int)&gvar_1C_buf + v0));    *(int*)((int)&gvar_18 + v0) = v9;    int v10 = init_interpreter__f43(*(int*)((int)&gvar_18 + v0));    *((int*)((int)&gvar_10 + v0) + 1) = v10;    load_data_section__f46(*((int*)((int)&gvar_10 + v0) + 1));    load_globals__f47(*((int*)((int)&gvar_10 + v0) + 1));    print__f258("Go Go Go!!");    int res = run__f52(22, *((int*)((int)&gvar_10 + v0) + 1)); // start execution from function 22    *(int*)((int)&gvar_10 + v0) = res;    free_interpreter__f45(*((int*)((int)&gvar_10 + v0) + 1));    free_wasm_obj__f41(*(int*)((int)&gvar_18 + v0));    free__f236(*(int*)((int)&gvar_1C_buf + v0));```
That is, we expect the decrypted buffer to also be a WebAssembly binary:```ispo@ispo-glaptop2:~/ctf/0ctf_2020/W$ file ww.stage2ww.stage2: WebAssembly (wasm) binary module version 0x1 (MVP)```
Let's load it again in JEB and start from function `22` (this is where `run__f52` starts from):```cint main__f22() {    int* glo = __g0 - 0x118;
    __g0 -= 0x118;    *(glo + 0x117) = 0;    int num_a = __f23((int)(gvar_400 * 12));  // 0x80    *(glo + 0x116) = num_a;    if(*(glo + 0x116) == 0) {        *(glo + 0x117) = -1;    }    else {        int num_b = __f23((int)(gvar_400 * 4));  // 0x200        *(glo + 0x115) = num_b;        if(*(glo + 0x115) == 0) {            *(glo + 0x117) = -1;        }        else {            int fp = open(0, 0, "/dev/urandom");            *(glo + 0x114) = fp;            __f31(&gvar_400, 0, (int)(glo + 20));            *(glo + 19) = (int)(glo + 20);            *(glo + 18) = &gvar_400;            *(glo + 17) = 3;            *(long long*)(glo + 8) = 0L;            *(long long*)(glo + 14) = 0L;            *(long long*)(glo + 12) = 0L;            *(long long*)(glo + 10) = 0L;            *(glo + 7) = -1;            p_and_q__f7(*(glo + 0x116));            __f17((int)(gvar_400 * 4), -1, *(glo + 0x115));            puts__f20("aoshine~ What\'s up?\n");            *(glo + 6) = 0;            while(1) {                show_menu__f21();                read_str__f12(2, (int)(glo + 8), 0);                int choice = read_inp__f18((int)(glo + 8));                *(glo + 7) = choice;                if(*(glo + 7) == 1 && *(glo + 17) < gvar_400 && *(glo + 6) < &gvar_400) {                    goto NEW_PAIR;                }                else if(*(glo + 7) == 2) {                    DUMP_PAIR__f10(*(glo + 17), *(glo + 0x115), *(glo + 0x116));                    continue;                }                if(*(glo + 7) == 3) {                    int chk_res = CHECK_PAIR__f3(*(*(unsigned int*)(glo + 0x116) + 7), *(*(unsigned int*)(glo + 0x116) + 4));                    *(glo + 3) = chk_res;                    if(!*(glo + 3)) {  // this must return non zero                        puts__f20(*(*(unsigned int*)(glo + 0x116) + 1));                        puts__f20("hmmm, try again\n");                    }                    else {                        itoa__f19((int)(glo + 8), *(glo + 3));  // secret number is 32-bits                        puts__f20("flag is: flag{secret+");                        puts__f20((int)(glo + 8));                        puts__f20("}\n");                        continue;                    NEW_PAIR:                        puts__f20("input new pair:\n");                        int len_maybe = read_str__f12(*(glo + 18) - *(glo + 6), *(glo + 19), 0);                        *(glo + 5) = len_maybe;                        if(*(glo + 5) <= 0) {                            *(glo + 0x117) = -1;                            break;                        }                        else if(*(glo + 5) + *(glo + 6) + 1 > &gvar_400) {                            continue;                        }                        else {                            *(glo + 6) = *(glo + 5) + *(glo + 6) + 1;                            *(char*)(*(glo + 5) + *(glo + 19)) = 0;                            int v6 = ADD_PAIR__f9(*(glo + 17), *(glo + 0x115), *(glo + 0x116), *(glo + 5), *(glo + 19));                            *(glo + 4) = v6;                            if(*(glo + 4)) {                                goto loc_50002855;                            }                            else {                                puts__f20(*(*(unsigned int*)(glo + 0x116) + 1));                                puts__f20(", bye~\n");                            }                        }                    }                }                if(*(glo + 0x116) != 0) {                    __f25(*(glo + 0x116));                }                if(*(glo + 0x115) != 0) {                    __f25(*(glo + 0x115));                }                *(glo + 0x117) = 0;                break;            loc_50002855:                *(glo + 17) = *(glo + 4) + *(glo + 17);                *(glo + 19) = *(glo + 5) + *(glo + 19) + 1;            }        }    }
    int result = *(glo + 0x117);    __g0 = glo + 0x118;    return result;}```
```cvoid show_menu__f21() {    puts__f20("1. add pair\n");    puts__f20("2. dump pair\n");    puts__f20("3. check pair\n");    puts__f20("4. exit\n");}```
Unfortunately, neither JEB not ReWasm provide function names (e.g., `itoa` or `puts`), but we caneasily recognize them by looking their input/output. Program shows a small menu where it can *add*,*dump* and *check* a pair (whatever that means) in a loop. Let's see how we can get the flag:```cint chk_res = CHECK_PAIR__f3(*(*(unsigned int*)(glo + 0x116) + 7), *(*(unsigned int*)(glo + 0x116) + 4));*(glo + 3) = chk_res;
if(!*(glo + 3)) {  // this must return non zero    puts__f20(*(*(unsigned int*)(glo + 0x116) + 1));    puts__f20("hmmm, try again\n");} else {    itoa__f19((int)(glo + 8), *(glo + 3));  // secret number is 32-bits    puts__f20("flag is: flag{secret+");    puts__f20((int)(glo + 8));    puts__f20("}\n");}```
Function `__f3` is responsible for implementing the `check pair` functionality. The input is a pairof numbers and the output must be non-zero:```c// Function 3fn func_3(i32 arg_0, i32 arg_1) -> i32 {    /* ... */    var_2 = global_0 - 32;    var_3 = global_0 - 32;    global_0 = var_3;    store<i32>(var_2 + 24, arg_0)                       // g24 = arg0    store<i32>(var_2 + 20, arg_1)                       // g20 = arg1    var_4 = load<i32>(var_2 + 24);                      // arg0    var_5 = var_4 != 0 & 1;    var_6 = var_5 == 0;    if !var_6 {                                         // arg0 must be non-zero        var_7 = load<i32>(var_2 + 20);                  // arg1        var_8 = var_7 != 0 & 1;        var_9 = var_8;        if var_9 {                                      // arg1 must be non zero as well            var_10 = load<i32>(var_2 + 24);            var_11 = func_18(var_10);            store<i32>(var_2 + 16, var_11)              // g16 = func_18(arg0)            var_12 = load<i32>(var_2 + 20);            var_13 = func_18(var_12);            store<i32>(var_2 + 12, var_13)              // g12 = func_18(arg1)            var_14 = load<i32>(var_2 + 16);            var_15 = func_4(var_14);                    // func_4(func_18(arg0))            var_16 = var_15 == 0;            if !var_16 {                                // must be nonzero                var_17 = load<i32>(var_2 + 12);                var_18 = func_4(var_17);                // func_4(func_18(arg1))                var_19 = var_18;                if var_19 {                             // must be nonzero too                    var_20 = load<i32>(var_2 + 16);                    var_21 = load<i32>(var_2 + 12);                    var_22 = var_21 * 87;                    var_23 = var_20 * 20 + var_22;                    store<i32>(var_2 + 8, var_23)       // g8 = func_18(arg0)*20 + func_18(arg1)*87                    var_24 = load<i32>(var_2 + 16);                    var_25 = load<i32>(var_2 + 12);                    var_26 = var_24 + var_25;                    store<i32>(var_2 + 4, var_26)       // g4 = func_18(arg0) + func_18(arg1)                    var_27 = load<i32>(var_2 + 8);                    var_28 = var_27 == 20200627 & 1;    // func_18(arg0)*20 + func_18(arg1)*87 == 20200627                    var_29 = var_28 == 0;                    if !var_29 {                        // must be true                        var_30 = load<i32>(var_2 + 4);                        var_31 = var_30 == 249310 & 1;  // func_18(arg0) + func_18(arg1) == 249310                    }                    if var_29 || !var_29 && var_31 == 0 {                        store<i32>(var_2 + 28, 0)       // g28 = 0                    }                    else {                        var_32 = load<i32>(var_2 + 16);                        var_33 = load<i32>(var_2 + 12);                        var_34 = var_33 * 20;           // func_18(arg1)*20                        var_35 = var_32 * 20 + var_34;  // func_18(arg0)*20 + func_18(arg1)*20                        var_36 = var_35 + 628;                        store<i32>(var_2 + 28, var_36)  // g28 = 20*(func_18(arg0) + func_18(arg1)) + 628                    }                }            }            if var_16 || !var_16 && !var_19 {                store<i32>(var_2 + 28, 0)               // g28 = 0            }        }    }    if var_6 || !var_6 && !var_9 {        store<i32>(var_2 + 28, 0)                       // g28 = 0    }    var_37 = load<i32>(var_2 + 28);    global_0 = var_2 + 32;    return var_37;                                      // return g28}```
Let's decompile this function into Python:```pythondef check_pair(arg0, arg1):    """Checks if a pair of numbers is 'good'."""    if not func_4(func_18(arg0)) or not func_4(func_18(arg1)):        return 0
    if func_18(arg0)*20 + func_18(arg1)*87 != 20200627:        return 0
    if func_18(arg0) + func_18(arg1) != 249310:        return 0
    return 20*(func_18(arg0) + func_18(arg1)) + 628```
Function `func_4` is really long, but we do not really need to know what it does as it is onlyused for a check. Apart from that, function takes as input **2** numbers, `x` and `y` and checksif they satisfy the following equations:```20*x + 87*y = 20200627   x +    y = 249310```
If `x` and `y` satisfy the above equations, function returns `20*(x + y) + 628` which is our flag.
This is a linear system of **2** equations, so it's very easy to find `x = 22229` and `y = 227081`.Then the expected return value of `check_pair` will be `4986828`.
 So the flag is: `flag{eNj0y_weba5SemB1Y.lstrip("web")!+4986828}`
### Bonus: Verifying Flag in the Binary  
At this point, the challenge was over (I already had the correct flag), but it was still unclear tome how to pass the right input to the program:```ispo@ispo-glaptop2:~/ctf/0ctf_2020/W$ ./wasm3 wwSecret: ?eNj0y_weba5SemB1Y.lstrip("web")!Loading...Go Go Go!!aoshine~ What's up?1. add pair2. dump pair3. check pair4. exit1input new pair:22229 2270811. add pair2. dump pair3. check pair4. exit3Welcome to 0CTF/TCTF 2020! Have a g00d timehmmm, try again```
Just for my curiosity I continued trying to understand the program. The first interesting part wasat function `__f7`:```cint p_and_q__f7(int param0) {    int* ptr0 = __g0 - 4;
    __g0 -= 4;    *(ptr0 + 3) = param0;    __f17(0x600, 0, *(ptr0 + 3));    **(unsigned int*)(ptr0 + 3) = "0ops";    *(*(unsigned int*)(ptr0 + 3) + 1) = "Welcome to 0CTF/TCTF 2020! Have a g00d time";    *(*(unsigned int*)(ptr0 + 3) + 2) = 25;    *(*(unsigned int*)(ptr0 + 3) + 3) = "p";    *(*(unsigned int*)(ptr0 + 3) + 4) = 0;  // p value    *(*(unsigned int*)(ptr0 + 3) + 5) = 0;    *(*(unsigned int*)(ptr0 + 3) + 6) = "q";    *(*(unsigned int*)(ptr0 + 3) + 7) = 0;  // q value    *(*(unsigned int*)(ptr0 + 3) + 8) = 0;    int result = *(ptr0 + 3);    __g0 = ptr0 + 4;    return result;}```
This function initializes an object of pointers. The important part is the strings `p` and `q`. Nowlet's look at how a new pair is added to the list (program can store multiple pairs):```cint ADD_PAIR__f9(int param0, int param1, int param2, int param3, int param4) {    int* ptr0 = __g0 - &gvar_10;
    __g0 -= &gvar_10;    *(ptr0 + 14) = param0;    *(ptr0 + 13) = param1;    *(ptr0 + 12) = param2;    *(ptr0 + 11) = param3;    *(ptr0 + 10) = param4;  // len maybe    if(*(ptr0 + 14) == 0 || *(ptr0 + 12) == 0 || !(unsigned int)(*(ptr0 + 11) != 0)) {        *(ptr0 + 15) = 0;    }    else {        *(ptr0 + 9) = *(ptr0 + 13) + *(ptr0 + 14);        if(*(ptr0 + 9) < *(ptr0 + 14)) {            *(ptr0 + 15) = 0;        }        else {            *(ptr0 + 8) = *(ptr0 + 14);            *(ptr0 + 7) = *(ptr0 + 14);            *(ptr0 + 6) = 0;            *(ptr0 + 5) = 128 - *(ptr0 + 10);            *(ptr0 + 4) = 0;            *(ptr0 + 3) = 0;            while(((*(ptr0 + 8) - *(ptr0 + 7) < *(ptr0 + 13) ? (unsigned int)(*(ptr0 + 4) < *(ptr0 + 5)): 0) & 0x1) != 0) {                int v0 = strcat__f16(*(ptr0 + 9) - *(ptr0 + 8), 10, *(ptr0 + 8));                *(ptr0 + 6) = v0;                if(*(ptr0 + 6) == 0 || (unsigned int)(*(ptr0 + 6) >= *(ptr0 + 9)) || *(ptr0 + 6) < *(ptr0 + 8)) {                    break;                }                else {                    **(unsigned int*)(ptr0 + 6) = 0;                    if(*(ptr0 + 6) - *(ptr0 + 8) > 1) {                        if((int)*(char*)((char*)*(unsigned int*)(ptr0 + 8) + 1) != '=') {                        loc_50001098:                            *(ptr0 + 3) = *(ptr0 + 10);                        }                        else if((int)**(unsigned int*)(*(unsigned int*)(ptr0 + 12) + 3) == (int)**(unsigned int*)(ptr0 + 8)) {                            *(ptr0 + 3) = 1;  // +3 = p                        }                        else if((int)**(unsigned int*)(*(unsigned int*)(ptr0 + 12) + 6) != (int)**(unsigned int*)(ptr0 + 8)) {                            goto loc_50001098;                        }                        else {                            *(ptr0 + 3) = 2;  // +6 = q                        }                        *(ptr0 + 2) = *(ptr0 + 3) * 12 + *(ptr0 + 12);  // (6 - 3)*4 = 12 ~> pointer to write number                        __f8(*(ptr0 + 6) - *(ptr0 + 8), *(ptr0 + 8), *(ptr0 + 2));                        *(int*)((*(ptr0 + 4) + *(ptr0 + 10)) * 4 + (int)*(unsigned int*)(ptr0 + 11)) = *(ptr0 + 3);                        *(ptr0 + 4) = *(ptr0 + 4) + 1;                    }                    *(ptr0 + 8) = *(ptr0 + 6) + 1;                }            }            if(*(ptr0 + 8) - *(ptr0 + 7) < *(ptr0 + 13) && *(ptr0 + 4) < *(ptr0 + 5)) {                if((int)*(char*)((char*)*(unsigned int*)(ptr0 + 8) + 1) != '=') {                loc_500013BF:                    *(ptr0 + 3) = *(ptr0 + 10);                }                else if((int)**(unsigned int*)(*(unsigned int*)(ptr0 + 12) + 3) == (int)**(unsigned int*)(ptr0 + 8)) {                    *(ptr0 + 3) = 1;                }                else if((int)**(unsigned int*)(*(unsigned int*)(ptr0 + 12) + 6) != (int)**(unsigned int*)(ptr0 + 8)) {                    goto loc_500013BF;                }                else {                    *(ptr0 + 3) = 2;                }                *(ptr0 + 1) = *(ptr0 + 3) * 12 + *(ptr0 + 12);                __f8(*(ptr0 + 7) + *(ptr0 + 13) - *(ptr0 + 8), *(ptr0 + 8), *(ptr0 + 1));                *(int*)((*(ptr0 + 4) + *(ptr0 + 10)) * 4 + (int)*(unsigned int*)(ptr0 + 11)) = *(ptr0 + 3);                *(ptr0 + 4) = *(ptr0 + 4) + 1;            }            *(ptr0 + 15) = *(ptr0 + 4);        }    }
    int result = *(ptr0 + 15);    __g0 = ptr0 + &gvar_10;    return result;}```
This function looks complicated at the first glance, but let's focus on the most impoortant parts.At the beginning we have an important assignment: `*(ptr0 + 12) = param2`. `param2` is a pointer tothe object that `p_and_q__f7` returns. Let's move on:```cif((int)*(char*)((char*)*(unsigned int*)(ptr0 + 8) + 1) != '=') {loc_50001098:    *(ptr0 + 3) = *(ptr0 + 10);}else if((int)**(unsigned int*)(*(unsigned int*)(ptr0 + 12) + 3) == (int)**(unsigned int*)(ptr0 + 8)) {    *(ptr0 + 3) = 1;  // +3 = p}else if((int)**(unsigned int*)(*(unsigned int*)(ptr0 + 12) + 6) != (int)**(unsigned int*)(ptr0 + 8)) {    goto loc_50001098;}else {    *(ptr0 + 3) = 2;  // +6 = q}*(ptr0 + 2) = *(ptr0 + 3) * 12 + *(ptr0 + 12);  // (6 - 3)*4 = 12 ~> pointer to write number__f8(*(ptr0 + 6) - *(ptr0 + 8), *(ptr0 + 8), *(ptr0 + 2));```
`ptr0 + 8` points to the input string. The first `if` checks if `inpt[1] != '='`. If this is true,`ptr0 + 3` gets assigned the string `*(ptr0 + 10)`, which is `*(ptr0 + 8 + 2)`, which is `inp[2:]`.The second `else if` checks the value of `(ptr0 + 12) + 3)` is equal to `(ptr0 + 8)` which is`inp[0]`. `ptr0 + 12` points to the output of `p_and_q__f7` which is initialized to`*(*(unsigned int*)(ptr0 + 3) + 3) = "p";` That is, this if checks if `inp[0] == 'p'`. If so, itassigns `*(ptr0 + 3) = 1`. Then program makes another check but this time with `(ptr0 + 12) + 6)`which is the assignment `*(*(unsigned int*)(ptr0 + 3) + 6) = "q";` in `p_and_q__f7`. That is, itchecks if `inp[0] == 'q'` and if so, it assigns `*(ptr0 + 3) = 2;`. Finally it makes an importantassignment: `*(ptr0 + 2) = *(ptr0 + 3) * 12 + *(ptr0 + 12);`, which can have **1** of the **2**possible values:```if inp[0] == 'p':    *(ptr0 + 3)*12 + *(ptr0 + 8 + 4) = 1*12 + p_and_q_obj = p_and_q_obj->p_val (+3 * 4 + 4 = +16)
if inp[0] == 'q'    *(ptr0 + 3)*12 + *(ptr0 + 8 + 4) = 2*12 + p_and_q_obj = p_and_q_obj->q_val (+6 * 4 + 4 = +28)```
Now let's go main (`__f22`) see the paramters of `check_pair````cp_and_q__f7(*(glo + 0x116));/* ... */
int chk_res = CHECK_PAIR__f3(*(*(unsigned int*)(glo + 0x116) + 7),                             *(*(unsigned int*)(glo + 0x116) + 4));```
For some reason, in ReWasm, parameters appear in the reverse order:```cvar_124 = load<i32>(var_92 + 1112);var_125 = load<i32>(var_124 + 16);
var_126 = load<i32>(var_92 + 1112);var_127 = load<i32>(var_126 + 28);
var_128 = func_3(var_125, var_127);````
The first parameter is `p_and_q_obj->p_val` (offset **+4** or **16**) and the second parameter is`p_and_q_obj->q_val` (offset **+7** or **28**). That is, we need to pass **2** distinct pairs tothe program: `p=22229` and `q=227081`. Let's try it out:```ispo@ispo-glaptop2:~/ctf/0ctf_2020/W$ ./wasm3 wwSecret: ?eNj0y_weba5SemB1Y.lstrip("web")!Loading...Go Go Go!!aoshine~ What's up?1. add pair2. dump pair3. check pair4. exit1input new pair:p=222291. add pair2. dump pair3. check pair4. exit1input new pair:q=2270811. add pair2. dump pair3. check pair4. exit2p=22229q=227081
1. add pair2. dump pair3. check pair4. exit3flag is: flag{secret+4986828}1. add pair2. dump pair3. check pair4. exit4```
Anw, the flag was: `flag{eNj0y_weba5SemB1Y.lstrip("web")!+4986828}`___