0xd13a

A rookie in a world of pwns

ASIS CTF Finals 2019 Writeup: Cursed app

Cursed app

104

If we want to say 100, should we start at 1 and count until 100?

Download: cursed_app.txz

This is a reversing challenge, we’ll analyze it in Ghidra.

Decompilation of the code and a quick inspection brings up the following interesting function:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
...
  
undefined8 FUN_001010e0(undefined8 param_1,long param_2)

{

...

  if (iVar2 != 0) {
    puts("please locate license file, run ./app license_key");
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  __stream = fopen(*(char **)(param_2 + 8),"r");
  if (__stream != (FILE *)0x0) {
    fseek(__stream,0,2);
    lVar3 = ftell(__stream);
    fseek(__stream,0,0);
    __ptr = (char *)malloc((long)(int)lVar3);
    if (__ptr != (char *)0x0) {
      fread(__ptr,1,(long)(int)lVar3,__stream);
    }
    fclose(__stream);
    iVar2 = ((int)*__ptr * 0xaf + 0x91) % 0x100;
    if (((((((((iVar2 * iVar2 * 0x164 + 0x202 + iVar2 * 0xeb) % (iVar2 * 0x67 + 2) == 0) &&
             (iVar2 = ((int)__ptr[1] * 0x5d + 0xda) % 0x100,
             (iVar2 * iVar2 * 0x3da + 0x354 + iVar2 * 0x3c) % (iVar2 * 0x56 + 0x35f) == 0)) &&
            (iVar2 = ((int)__ptr[2] * 5 + 0x95) % 0x100,
            (iVar2 * iVar2 * 0x19e + 0x3aa + iVar2 * 0x49) % (iVar2 * 0xb9 + 0xb2) == 0)) &&
           (((iVar2 = ((int)__ptr[3] * 0x35 + 0xd4) % 0x100,
             (iVar2 * iVar2 * 0x210 + 0xba + iVar2 * 0x20c) % (iVar2 * 0x7e + 0x38) == 0 &&

...

         (iVar2 = ((int)__ptr[0x3a] * 0x79 + 0x25) % 0x100,
         (iVar2 * iVar2 * 0x2a9 + 0x349 + iVar2 * 0x203) % (iVar2 * 7 + 0xbb) == 0)))))) {
      puts("Congratz! You got the correct flag!!");
      bVar1 = true;
    }
    if (!bVar1) {
      puts("Bummer! You got the wrong flag!!");
    }
    return 0;
  }
  
...

Essentially it asks for a file, reads it in and checks checksums of 0x3b characters to make sure they are correct.

As we look at the expressions in the big “if” statement it becomes obvious that the checksums of individual characters are independent of each other, and we can bruteforce each character seperately. All expressions have the same structure, so with a bit of editing we can build a table out of them and process characters one by one:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
rules = [
[0xaf, 0x91, 0x164, 0x202, 0xeb, 0x67, 2],
[0x5d, 0xda, 0x3da, 0x354, 0x3c, 0x56, 0x35f],
[5, 0x95, 0x19e, 0x3aa, 0x49, 0xb9, 0xb2],
[0x35, 0xd4, 0x210, 0xba, 0x20c, 0x7e, 0x38],
[0xb5, 0xd, 0x26a, 0x3ce, 0x130, 8, 0x27f],
[0x5b, 2, 5, 0x4b, 0x2f7, 0x30e, 0x55],
[0x43, 0x76, 0x37d, 0x1f1, 0xed, 0x106, 0xdd],
[0xe7, 0x74, 0x213, 0x3e5, 0x287, 0xf5, 0x184], 
[0x77, 0xdf, 0x228, 0x2ce, 0x1e7, 10, 0x22a],
[0xcb, 0x88, 0x336, 0x3bd, 0x95, 0x78, 0x230],
[0x51, 0x96, 0x9d, 0x21a, 0x2b0, 0x144, 0x265],
[0x29, 0x8d, 0x56, 0xcb, 0x2db, 0x15, 0x203],
[0x73, 0x5f, 0x2df, 0x88, 0x178, 0x116, 0x262],
[0xf3, 0xe4, 0x31b, 0x37c, 0xdd, 0xca, 0x33b],
[0x4f, 0x51, 0x66, 0x366, 0x345, 0x234, 0xf9],
[0x7b, 0x8e, 0x12e, 0x282, 0x2b0, 5, 0xd5],
[0xa9, 0x59, 0xe, 0x2a8, 0x272, 0x96, 0x1c8],
[0xad, 0xe6, 0x33e, 0x12e, 0x214, 0x19f, 0x1f1],
[0xab, 0x9a, 0x10f, 0xe2, 0x72, 0x20, 0x292],
[0x4b, 0xb8, 0x366, 0x39, 0x35a, 0x37d, 0x1a8],
[0xf5, 0x5b, 0x375, 0x9d, 0x2f3, 0x6a, 0x3d7],
[0xed, 0x22, 0x36c, 0x173, 0x189, 0x66, 0x196],
[0x3d, 0x69, 0x3cb, 0x214, 0x3c0, 0x3e, 0x390],
[0x9f, 0x2c, 0x215, 0x7a, 0x1d8, 0x29, 0x32],
[0x77, 0xef, 0xa7, 0x308, 0x21f, 0x38, 0x290],
[0xb3, 0x70, 0xf8, 0x19e, 0x3ce, 0x87, 0x52],
[0x47, 0xae, 0x2f3, 0x115, 0x1cf, 10, 0x113],
[0xbf, 0x90, 0xe3, 0x234, 0x2a6, 0x37, 0x27a],
[0x51, 0x9b, 0x379, 0x330, 0x210, 0x173, 0x2ea],
[0x57, 200, 0x14d, 0x25, 0x2e0, 0x2e6, 0xd],
[0xa7, 0xa0, 0x27c, 0xd5, 200, 0x382, 0x265],
[0xe7, 0x66, 0x300, 0x2e2, 0x1a, 0x18a, 0x3e1],
[0x71, 0xd9, 0x8a, 0xfc, 0x34b, 0x14d, 0x244],
[0xaf, 9, 0x1ae, 0x2cb, 0x164, 0x69, 0x1fc],
[0xdb, 0xa6, 0xac, 0x32c, 0x2ca, 2, 0x124],
[0xb3, 0x39, 0x358, 0x2b9, 0x3be, 0x1f1, 0x3be],
[0x97, 0x1b, 0x394, 0x15, 0x397, 0x4b, 699],
[0x3d, 0xf0, 0x141, 0x1ec, 0x3b9, 0x78, 0x199],
[0x29, 0x76, 0x7b, 0x3c4, 0x10c, 0x4a, 0x14e],
[0x2d, 0x96, 0x264, 0x16c, 0x210, 0xd, 0x27d],
[0x4f, 0xd7, 0xd6, 0x3a6, 0x28, 0x3ba, 0xa6], 
[0x3f, 0xa0, 0x388, 0x34d, 0x15a, 0xf0, 0x3d],
[0xd3, 0x72, 0x22f, 0x299, 0x22a, 0x93, 0x379],
[0x79, 2, 0x2cf, 0x32b, 0x3df, 0x82, 0x195],
[0x75, 0xe6, 0x21d, 300, 0x134, 0x27, 0x29],
[0x51, 0x52, 0x234, 0x229, 0x253, 0x23, 0x205],
[0x55, 0xa3, 0x3b0, 0x3b0, 0xe3, 0x8a, 0xd9],
[0xed, 0xa5, 0x203, 0x14b, 0x306, 0x24, 0x28c],
[0x81, 0x7f, 0x362, 0x14, 0x1a4, 0xb, 0x15a],
[0x19, 0xeb, 0xb8, 0x140, 0x67, 0x211, 0x17e],
[0x9b, 0x3a, 0x357, 0xa0, 0x33c, 0x46, 0x10e],
[0x55, 0xf0, 0x3b7, 0xd5, 0x234, 10, 0x207],
[0x49, 0x3d, 0x282, 0x362, 0x79, 7, 0x142],
[0x81, 0x56, 0x3d, 0x181, 0x326, 0x17, 0x14f],
[0x9b, 0xb1, 399, 0x24, 0xc4, 0x65, 0xda],
[0x99, 0x7e, 0x360, 0x2aa, 0x116, 0x3c, 0x142],
[0xe3, 0xf5, 0x123, 0x1f8, 0x152, 0x51, 700],
[0xc3, 0x16, 0x300, 0x13, 0x3ca, 0x148, 0x1e1],
[0x79, 0x25, 0x2a9, 0x349, 0x203, 7, 0xbb]]

out = ""
for x in rules:
	for c in range(256):
		v = (c * x[0] + x[1]) % 0x100
		if (v * v * x[2] + x[3] + v * x[4]) % (v * x[5] + x[6]) == 0:
			out += chr(c)
			break
print out

When we run the script we quickly get the flag: ASIS{y0u_c4N_s33_7h15_15_34513R_7h4n_Y0u_7h1nk_r16h7?__!!!}.