Rating:

# ASIS CTF Quals 2015: tera

----------
## Challenge details
| Contest | Challenge | Category | Points |
|:---------------|:--------------|:----------|-------:|
| ASIS CTF Quals 2015 | tera | Reversing | 100 |

**Description:**
>*Be patient and find the flag in this [file](challenge/tera).*

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

Let's take a look at the binary:

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

And get a pseudocode decompilation of its main routine (with some variables renamed for clarity):

>```c
>__int64 __usercall mainroutine@<rax>(unsigned int a1@<ebx>)
>{
> signed __int64 v1; // rsi@1
> __int64 *v2; // rdi@1
> signed __int64 i; // rcx@1
> signed __int64 v4; // rcx@4
> char *v5; // rdi@4
> signed __int64 v6; // rsi@4
> signed __int64 v7; // rcx@10
> char *v8; // rdi@10
> void *v9; // rax@16
> signed __int64 v10; // rsi@17
> int *v11; // rdi@17
> signed __int64 l; // rcx@17
> signed int v13; // eax@21
> void *v14; // rsp@22
> size_t v16; // [sp+0h] [bp-2350h]@22
> __int64 v17; // [sp+8h] [bp-2348h]@22
> int key_table[40]; // [sp+10h] [bp-2340h]@17
> char filename[10]; // [sp+B0h] [bp-22A0h]@14
> char v20; // [sp+BAh] [bp-2296h]@16
> char v21; // [sp+10B0h] [bp-12A0h]@10
> char v22; // [sp+10B1h] [bp-129Fh]@13
> char v23; // [sp+10B3h] [bp-129Dh]@13
> char v24; // [sp+10B5h] [bp-129Bh]@13
> char v25; // [sp+10B7h] [bp-1299h]@13
> char v26; // [sp+10B9h] [bp-1297h]@13
> char v27; // [sp+10BBh] [bp-1295h]@13
> char v28; // [sp+10BDh] [bp-1293h]@13
> char v29; // [sp+10BFh] [bp-1291h]@13
> char v30; // [sp+10C1h] [bp-128Fh]@13
> char v31; // [sp+10C3h] [bp-128Dh]@13
> char v32; // [sp+10C5h] [bp-128Bh]@13
> char v33[80]; // [sp+20B0h] [bp-2A0h]@8
> char v34[144]; // [sp+2100h] [bp-250h]@4
> __int64 index_table[39]; // [sp+2190h] [bp-1C0h]@1
> pthread_t newthread; // [sp+22C8h] [bp-88h]@20
> void *buffer; // [sp+22D0h] [bp-80h]@22
> size_t v38; // [sp+22D8h] [bp-78h]@22
> FILE *v39; // [sp+22E0h] [bp-70h]@22
> int v40; // [sp+22ECh] [bp-64h]@22
> int v41; // [sp+22F0h] [bp-60h]@22
> int v42; // [sp+22F4h] [bp-5Ch]@22
> int v43; // [sp+22F8h] [bp-58h]@22
> int v44; // [sp+22FCh] [bp-54h]@20
> FILE *stream; // [sp+2300h] [bp-50h]@20
> void *arg; // [sp+2308h] [bp-48h]@16
> int v47; // [sp+2314h] [bp-3Ch]@4
> size_t n; // [sp+2318h] [bp-38h]@1
> __int64 m; // [sp+2320h] [bp-30h]@22
> int k; // [sp+2328h] [bp-28h]@13
> int j; // [sp+232Ch] [bp-24h]@7
>
> n = 0x1F40001809E0LL;
> v1 = 0x401480LL;
> v2 = index_table;
> for ( i = 38LL; i; --i )
> {
> *v2 = *(_QWORD *)v1;
> v1 += 8LL;
> ++v2;
> }
> v47 = 38;
> v4 = 16LL;
> v5 = v34;
> v6 = 4199872LL;
> while ( v4 )
> {
> *(_QWORD *)v5 = *(_QWORD *)v6;
> v6 += 8LL;
> v5 += 8;
> --v4;
> }
> *(_WORD *)v5 = *(_WORD *)v6;
> v5[2] = *(_BYTE *)(v6 + 2);
> setbuf(stdout, 0LL);
> for ( j = 0; j <= 64; ++j )
> v33[j] = v34[2 * j];
> v7 = 512LL;
> v8 = &v2;;
> while ( v7 )
> {
> *(_QWORD *)v8 = 0LL;
> v8 += 8;
> --v7;
> }
> v22 = 47;
> v23 = 116;
> v24 = 109;
> v25 = 112;
> v26 = 47;
> v27 = 46;
> v28 = 116;
> v29 = 101;
> v30 = 114;
> v31 = 97;
> v32 = 10;
> for ( k = 0; k <= 9; ++k )
> filename[k] = *(&v21 + 2 * k + 1);
> v20 = 0;
> LODWORD(v9) = curl_easy_init(v8, 0LL);
> arg = v9;
> if ( !v9 )
> {
> puts("Please check your connection :)");
>LABEL_29:
> return 0;
> }
> puts("Please wait until my job be done ");
> v10 = 4200064LL;
> v11 = key_table;
> for ( l = 19LL; l; --l )
> {
> *(_QWORD *)v11 = *(_QWORD *)v10;
> v10 += 8LL;
> v11 += 2;
> }
> stream = fopen(filename, "wb");
> v44 = pthread_create(&newthread, 0LL, (void *(*)(void *))start_routine, arg);
> if ( v44 )
> {
> fprintf(_bss_start, "Error - pthread_create() return code: %d\n", (unsigned int)v44);
> a1 = 0;
> v13 = 0;
> }
> else
> {
> v43 = 10002;
> curl_easy_setopt(arg, 10002LL, v33);
> v42 = 20011;
> curl_easy_setopt(arg, 20011LL, 4197590LL);
> v41 = 10001;
> curl_easy_setopt(arg, 10001LL, stream);
> v40 = curl_easy_perform(arg);
> curl_easy_cleanup(arg);
> fclose(stream);
> v39 = fopen(filename, "r");
> v38 = n - 1;
> v16 = n;
> v17 = 0LL;
> v14 = alloca(16 * ((n + 15) / 0x10));
> buffer = &v1;;
> fread(&v16, 1uLL, n, v39);
> for ( m = 0LL; v47 > m; ++m )
> printf("%c\n", (unsigned int)(char)(*((_BYTE *)buffer + index_table[m]) ^ LOBYTE(key_table[m])));
> fclose(v39);
> v13 = 1;
> }
> if ( v13 == 1 )
> goto LABEL_29;
> return a1;
>}
>```

Ok so it looks like the binary uses curl to download some file, read its contents to a buffer and xor certain elements of that buffer with bytes from a key table. It's not clear from the pseudocode but in the disassembly we can see what file it tries to download:

>```asm
>.text:0000000000400F53 mov [rbp+var_3C], 26h
>.text:0000000000400F5A lea rax, [rbp+var_250]
>.text:0000000000400F61 mov edx, offset aHttpDarksky_sl ; "http://darksky.slac.stanford.edu/simula"...
>.text:0000000000400F66 mov ecx, 10h
>.text:0000000000400F6B mov rdi, rax
>.text:0000000000400F6E mov rsi, rdx
>```

The URL is in unicode:

>```asm
>.rodata:00000000004015C0 aHttpDarksky_sl: ; DATA XREF: mainroutine+48?o
>.rodata:00000000004015C0 unicode 0, <http://darksky.slac.stanford.edu/simulations/ds14_a/ds14_>
>.rodata:00000000004015C0 unicode 0, <a_1.0000>
>.rodata:00000000004015C0 dw 0Ah, 0
>.rodata:0000000000401646 align 40h
>```

If we look at the directory of the file in question, however, we can see that it is roughly 31TB:

>*ds14_a_1.0000 19-Apr-2014 16:47 31T*

Since we have neither the time nor the disk space to download it in full we will use the fact that we can specify [content ranges](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html) in our HTTP header to only fetch a single byte at the offsets we require. So all we need to emulate the functionality of the binary without downloading the file are the index and key tables which get initialized from the addresses v1 = 0x401480 and v10 = 0x401680 respectively so we can simply extract them from the binary using IDA's built-in hex viewer and produce the [following decoder](solution/tera_decoder.py):

>```python
>#!/usr/bin/python
>#
># ASIS CTF Quals 2015
># tera (REVERSING/100)
>#
># @a: Smoke Leet Everyday
># @u: https://github.com/smokeleeteveryday
>#
>
>from struct import unpack
>import urllib2
>
># Fetch single byte from URL using content range
>def get_buffer_byte(url, offset):
> req = urllib2.Request(url)
> req.headers['Range'] = 'bytes=%s-%s' % (offset, offset)
> f = urllib2.urlopen(req)
> return f.read()
>
># Decryption functionality
>def decrypt(url, index_table, key_table):
> plaintext = ""
> for m in xrange(0, 38):
> i = unpack('<Q', index_table[m*8: (m+1)*8])[0]
> b = get_buffer_byte(url, i)
> k = unpack('<I', key_table[m*4: (m+1)*4])[0]
> plaintext += chr(ord(b) ^ k)
> return plaintext
>
># Target URL
>url = "http://darksky.slac.stanford.edu/simulations/ds14_a/ds14_a_1.0000"
>
># Key and indexing tables
>key_table = "\xF2\x00\x00\x00\x9A\x00\x00\x00\x83\x00\x00\x00\x12\x00\x00\x00\x39\x00\x00\x00\x45\x00\x00\x00\xE7\x00\x00\x00\xF4\x00\x00\x00\x6F\x00\x00\x00\xA1\x00\x00\x00\x06\x00\x00\x00\xE7\x00\x00\x00\x95\x00\x00\x00\xF3\x00\x00\x00\x90\x00\x00\x00\xF2\x00\x00\x00\xF0\x00\x00\x00\x6B\x00\x00\x00\x33\x00\x00\x00\xE3\x00\x00\x00\xA8\x00\x00\x00\x78\x00\x00\x00\x37\x00\x00\x00\xD5\x00\x00\x00\x44\x00\x00\x00\x39\x00\x00\x00\x61\x00\x00\x00\x8A\x00\x00\x00\xFB\x00\x00\x00\x22\x00\x00\x00\xFA\x00\x00\x00\x9E\x00\x00\x00\xE7\x00\x00\x00\x11\x00\x00\x00\x39\x00\x00\x00\xA6\x00\x00\x00\xF3\x00\x00\x00\x33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x59\x40"
>index_table = "\xF4\x7C\x61\x89\x4C\x00\x00\x00\x83\x5F\xE9\xB5\xB4\x00\x00\x00\x6B\x68\x8D\x59\xE4\x00\x00\x00\xEF\x74\x26\xA6\x36\x01\x00\x00\xB7\xBE\x65\x7A\x83\x01\x00\x00\x7C\x46\x31\xA8\x9F\x01\x00\x00\x01\xCD\x2A\x20\xA6\x02\x00\x00\x5E\x64\x10\x3F\x49\x04\x00\x00\xE4\x65\x6D\xCE\xCD\x04\x00\x00\x7E\xDE\xC8\x8E\x02\x05\x00\x00\x56\x4A\x50\x19\x62\x05\x00\x00\xB8\x1D\x19\x2D\xBD\x05\x00\x00\x92\x25\xD0\xD5\x2B\x07\x00\x00\xFE\x04\x6D\xEE\x3D\x07\x00\x00\x20\xE3\xAF\xE5\x25\x0A\x00\x00\x9E\xFB\x64\xB4\x73\x0A\x00\x00\x4B\xE3\xF6\x59\x62\x0B\x00\x00\xDC\x94\x50\xA4\x9A\x0B\x00\x00\x39\xEA\xE0\x48\xC5\x0B\x00\x00\x56\xCC\x1E\xC4\x7A\x0C\x00\x00\x8B\xFB\x73\xF0\x85\x0C\x00\x00\x16\x91\x6A\x53\x92\x0C\x00\x00\xBF\xDA\xE6\x0B\x93\x0D\x00\x00\x40\xDA\x89\xB9\x61\x0E\x00\x00\x68\xA2\x9C\x99\x37\x0F\x00\x00\x1F\x9D\x9B\xC5\xB7\x0F\x00\x00\x9D\x93\xA3\xD3\x18\x10\x00\x00\x69\x03\xED\x2A\x20\x10\x00\x00\xF3\x6C\x92\xFB\xE8\x10\x00\x00\x65\xA0\x8E\xC3\x3B\x11\x00\x00\x4F\x04\x04\x75\x25\x13\x00\x00\x3C\xDC\x12\x06\xFB\x14\x00\x00\x92\xDA\x70\x23\x57\x16\x00\x00\x41\x44\x63\x75\x3D\x17\x00\x00\x74\x93\x2D\x0F\x9D\x1B\x00\x00\x8E\x2D\xE4\x0D\xA9\x1B\x00\x00\x3E\x8F\x4C\xEF\xE9\x1B\x00\x00\x00\x4E\xB8\xA4\xFD\x1B\x00\x00"
>
>print "[+]Got flag: [%s]" % decrypt(url, index_table, key_table)
>```

Which produces the following output:

>```bash
>$ ./tera_decoder.py
>[+]Got flag: [ASIS{3149ad5d3629581b17279cc889222b93}]
>```

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