Rating:

# ASIS CTF Quals 2015: selfie

----------
## Challenge details
| Contest | Challenge | Category | Points |
|:---------------|:--------------|:----------|-------:|
| ASIS CTF Quals 2015 | selfie | Reversing | 150 |

**Description:**
>*Find the flag in this [file](challenge/selfie).*

----------
## Write-up

See what file has to say first:

>```bash
>file selfie
>selfie; ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, not stripped
>```

And fetch some pseudocode:

>```c
>int __cdecl main(int argc, const char **argv, const char **envp)
>{
> const char **v4; // [sp+0h] [bp-110h]@1
> char v5[8]; // [sp+10h] [bp-100h]@4
> char v6[112]; // [sp+20h] [bp-F0h]@2
> char s[8]; // [sp+90h] [bp-80h]@1
> int v8; // [sp+D8h] [bp-38h]@4
> int v9; // [sp+DCh] [bp-34h]@4
> FILE *stream; // [sp+E0h] [bp-30h]@4
> int v11; // [sp+ECh] [bp-24h]@1
> int v12; // [sp+F0h] [bp-20h]@1
> unsigned int v13; // [sp+F4h] [bp-1Ch]@1
> void *ptr; // [sp+F8h] [bp-18h]@1
> int j; // [sp+104h] [bp-Ch]@12
> unsigned int i; // [sp+108h] [bp-8h]@5
> int v17; // [sp+10Ch] [bp-4h]@1
>
> v4 = argv;
> ptr = 0LL;
> v13 = 0;
> v12 = 13848;
> strcpy(s, "ASIS{4a2cdaf7d77165eb3fdb70 : i am the first part of flag\t");
> v11 = strlen(s);
> v17 = 0;
> putchar(10);
> while ( v17 < v11 )
> {
> v6[v17] = s[v17];
> v6[v17 + 1] = 0;
> loading((__int64)v6);
> ++v17;
> }
> putchar(10);
> stream = fopen(*argv, "r");
> fseeko(stream, 0LL, 2);
> v13 = ftello(stream);
> rewind(stream);
> ptr = malloc((signed int)(v13 + 1));
> v9 = fread(ptr, 1uLL, (signed int)v13, stream);
> *((_BYTE *)ptr + (signed int)v13) = 0;
> strcpy(v5, " ");
> v8 = strlen(v5);
> if ( argc == 3 )
> {
> for ( i = 0; (signed int)i < (signed int)v13; ++i )
> {
> if ( *((_BYTE *)ptr + (signed int)i) == 84
> && *((_BYTE *)ptr + (signed int)i + 1) == 114
> && *((_BYTE *)ptr + (signed int)i + 2) == 121 )
> printf("%d[%c]\n", i, *((_BYTE *)ptr + (signed int)i), v4);
> }
> }
> for ( j = 0; j < v8; ++j )
> v5[j] = *((_BYTE *)ptr + v12 + j);
> puts(v5);
> fclose(stream);
> return 0;
>}
>```

We can see from the source the first part of the flag is slowly output to the screen, running it gives us the following:

>```bash
>$ ./selfie
>ASIS{4a2cdaf7d77165eb3fdb70 : i am the first part of flag
>
>Try harder :)
>```

If we look at the code we can see it also performs an fopen() on the first argument of argv, which is the filename of the file itself. It then proceeds to read its own body into memory and, after outputting the flag, output the 15 bytes located at offset 13848, which is the string "Try harder :)". This is confirmed by the (argc == 3) check which searches the binary buffer for the string "Try". Running the binary with two arbitrary commands gives us the following output:

>```bash
>$ ./selfie ayy lmao
>ASIS{4a2cdaf7d77165eb3fdb70 : i am the first part of flag
>
>13848[T]
>Try harder :)
>```

So if we look at this offset in a hex editor we see that the string "Try harder :)" is located amid what looks like a section table:

>```asm
>00003610 00 00 00 00 00 00 00 00 54 72 79 20 68 61 72 64 Try hard
>00003620 65 72 20 3A 29 0A 00 47 43 43 3A 20 28 44 65 62 er :) GCC: (Deb
>00003630 69 61 6E 20 34 2E 39 2E 32 2D 31 30 29 20 34 2E ian 4.9.2-10) 4.
>00003640 39 2E 32 00 47 43 43 3A 20 28 44 65 62 69 61 6E 9.2 GCC: (Debian
>00003650 20 34 2E 38 2E 34 2D 31 29 20 34 2E 38 2E 34 00 4.8.4-1) 4.8.4
>00003660 00 2E 73 79 6D 74 61 62 00 2E 73 74 72 74 61 62 .symtab .strtab
>00003670 00 2E 73 68 73 74 72 74 61 62 00 2E 69 6E 74 65 .shstrtab .inte
>00003680 72 70 00 2E 6E 6F 74 65 2E 41 42 49 2D 74 61 67 rp .note.ABI-tag
>00003690 00 2E 6E 6F 74 65 2E 67 6E 75 2E 62 75 69 6C 64 .note.gnu.build
>000036A0 2D 69 64 00 2E 67 6E 75 2E 68 61 73 68 00 2E 64 -id .gnu.hash .d
>000036B0 79 6E 73 79 6D 00 2E 64 79 6E 73 74 72 00 2E 67 ynsym .dynstr .g
>000036C0 6E 75 2E 76 65 72 73 69 6F 6E 00 2E 67 6E 75 2E nu.version .gnu.
>000036D0 76 65 72 73 69 6F 6E 5F 72 00 2E 72 65 6C 61 2E version_r .rela.
>000036E0 64 79 6E 00 2E 72 65 6C 61 2E 70 6C 74 00 2E 69 dyn .rela.plt .i
>```

This, however, is neither were the section table is supposed to be in this ELF nor does it correspond to this ELF's section table so this made us suspicious and we looked up a little to find this:

>```asm
>00008960 7F 45 4C 46 02 01 01 00 00 00 00 00 00 00 00 00 ELF
>00008976 02 00 3E 00 01 00 00 00 30 05 40 00 00 00 00 00 > 0 @
>00008992 40 00 00 00 00 00 00 00 C8 1D 00 00 00 00 00 00 @ È
>00009008 00 00 00 00 40 00 38 00 08 00 40 00 1E 00 1B 00 @ 8 @
>00009024 06 00 00 00 05 00 00 00 40 00 00 00 00 00 00 00 @
>```

It seems that there is another ELF binary hidden in our binary so we extract it and [take a look at it](challenge/hidden_selfie):

>```bash
>$file hidden_selfie
>hidden_selfie; ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, not stripped
>```

We load it into IDA to get pseudocode:

>```c
>int __cdecl main(int argc, const char **argv, const char **envp)
>{
> signed __int64 v3; // rcx@1
> char *v4; // rdi@1
> signed __int64 v5; // rsi@1
> char targetbuf[1536]; // [sp+0h] [bp-620h]@1
> char *dest; // [sp+600h] [bp-20h]@11
> unsigned int v9; // [sp+608h] [bp-18h]@4
> int v10; // [sp+60Ch] [bp-14h]@4
> __int64 timestamp; // [sp+610h] [bp-10h]@4
> unsigned int i; // [sp+61Ch] [bp-4h]@5
>
> v3 = 190LL;
> v4 = targetbuf;
> v5 = 4196736LL;
> while ( v3 )
> {
> *(_QWORD *)v4 = *(_QWORD *)v5;
> v5 += 8LL;
> v4 += 8;
> --v3;
> }
> *(_WORD *)v4 = *(_WORD *)v5;
> timestamp = (unsigned int)time(0LL);
> sitoor(timestamp);
> printf("%lu %lu\n ", g_r, timestamp - g_r * g_r);
> v10 = timestamp - g_r * g_r;
> v9 = (unsigned __int64)((g_r - ((signed int)timestamp - (signed int)g_r * (signed int)g_r)) / 2) - 49;
> printf("%d\n", v9);
> if ( 3013 * g_r == 3286 * v10 + 5 )
> {
> for ( i = 0; (signed int)i < (signed int)v9; ++i )
> {
> if ( !(unsigned int)sitoor((signed int)i) )
> putchar(targetbuf[i + 1]);
> }
> }
> else
> {
> dest = 0LL;
> dest = (char *)malloc(0x572uLL);
> strcpy(dest, hidden);
> }
> return 0;
>}
>
>__int64 __fastcall sitoor(signed __int64 a1)
>{
> __int64 result; // rax@2
> signed int i; // [sp+2Ch] [bp-14h]@3
> long double v3; // [sp+30h] [bp-10h]@3
>
> if ( a1 * a1 == a1 )
> {
> result = 0LL;
> }
> else
> {
> v3 = (long double)(a1 / 2);
> for ( i = 0; i < 1000; ++i )
> v3 = (v3 * v3 + (long double)a1) / (v3 + v3);
> g_r = (signed __int64)v3;
> result = 0.0 != v3 - (long double)(signed __int64)v3;
> }
> return result;
>}
>```

The above code retrieves the current unix timestamp, performs some arithmetic and transformations (in the form of the sitoor function) on it and checks if the resulting values satisfy some equation:

>```c
> v9 = (unsigned __int64)((g_r - ((signed int)timestamp - (signed int)g_r * (signed int)g_r)) / 2) - 49;
> printf("%d\n", v9);
> if ( 3013 * g_r == 3286 * v10 + 5 )
>```

If this is the case then the following loop iterates (with a length determined by arithmetic over the current timestamp) over a buffer embedded in the binary and decide (using the sitoor function over the loop index) whether to output the current byte or not:

>```c
> for ( i = 0; (signed int)i < (signed int)v9; ++i )
> {
> if ( !(unsigned int)sitoor((signed int)i) )
> putchar(targetbuf[i + 1]);
> }
>```

We can see from the disassembly that the buffer in question is initialized as follows:

>```asm
>.text:00000000004006FE lea rax, [rbp+targetbuf]
>.text:0000000000400705 mov edx, offset aUthGed
>.text:000000000040070A mov ecx, 0BEh
>.text:000000000040070F mov rdi, rax
>.text:0000000000400712 mov rsi, rdx
>.text:0000000000400715 rep movsq
>```

and looks like this:

>```asm
>0000000000400980 93 74 68 B2 47 65 64 00 D9 EE 20 B5 9A 55 E4 62 ôth¦Ged.+e ¦ÜUSb
>0000000000400990 75 73 EC 1E A5 FA 8D 2D 9C 96 65 68 26 3D 81 59 us8.Ñ·.-£ûeh&=.Y
>00000000004009A0 10 0F 91 AA 95 63 86 C3 C0 3C C7 11 57 D9 92 AD ..æ¬òcå++<¦.W+Æ¡
>00000000004009B0 9B 73 6F F6 FF F4 7B 57 1B FE 5A 18 A6 C2 FB 7E ¢so÷ ({W.¦Z.ª-v~
>00000000004009C0 17 64 D3 4E BE 39 2A 0D E9 1D 35 DA C4 03 AB A6 .d+N+9*.T.5+-.½ª
>00000000004009D0 58 08 6E 1D 73 A1 D4 B0 E7 7A 0B F0 2C A7 57 56 X.n.sí+¦tz.=,ºWV
>```

So we know our scrambled buffer consists of 0xBE QWORDS (1520 bytes) and we can simply extract the buffer and iterate over it in its entirety testing the index against a ported version of the sitoor function. This way we won't have to find out at what time we have to execute the binary in order to match the intended timestamp. The [following script](solution/selfie_descramble.py) automates the whole process:

>```python
>#!/usr/bin/python
>#
># ASIS CTF Quals 2015
># selfie (REVERSING/150)
>#
># @a: Smoke Leet Everyday
># @u: https://github.com/smokeleeteveryday
>#
>
>g_r = 0
>
># fetch hidden binary
>def get_hidden_elf(selfie):
> data = open(selfie, "rb").read()
> offset = 8960 # offset of hidden ELF
> return data[offset: ]
>
># fetch scrambled buffer from hidden ELF
>def get_scrambled_buffer(hidden_selfie_buffer):
> offset = 2432 # offset of scrambled buffer
> size = 1521 # size of scrambled buffer
> return hidden_selfie_buffer[offset: offset+size]
>
># ported sitoor function
>def sitoor(a):
> global g_r
>
> if(a*a == a):
> return False
>
> v3 = float(a) / 2
> for i in xrange(1000):
> v3 = float(v3*v3 + a) / (v3+v3)
> g_r = long(v3)
> return (0.0 != (v3 - float(long(v3))))
>
>def descramble(selfie):
> scrambled = get_scrambled_buffer(get_hidden_elf(selfie))
> #Try bruteforce approach
> v9 = 1521
> res = ""
> for i in xrange(v9):
> if not(sitoor(i)):
> res += scrambled[i + 1]
> return res
>
>print "[+]Got flag: [%s]" % descramble("./selfie")
>```

Which gives us the second part of the flag:

>```bash
>$ ./selfie_descramble.py
>[+]Got flag: [the secodn part of flag is: 93a641a99a}]
>```

Combined with the first part we get the flag:

>*ASIS{4a2cdaf7d77165eb3fdb7093a641a99a}*

Original writeup (https://github.com/smokeleeteveryday/CTF_WRITEUPS/tree/master/2015/ASISCTF/reversing/selfie).