Points: 100 Solves: 58 Category: Reverse Engineering Description:

Be patient and find the flag in this file.

Write-up

# file tera 
tera: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0x9629b13ca5a7969979a1e01bd0f061eb6bdf0726, stripped

Let’s take a look at the main function.

(gdb) x/250i 0x400f19
   0x400f19:	push   rbp
   0x400f1a:	mov    rbp,rsp
   0x400f1d:	push   r14
   0x400f1f:	push   r13
   0x400f21:	push   r12
   0x400f23:	push   rbx
   0x400f24:	sub    rsp,0x2330

Function prolog

   0x400f2b:	movabs rax,0x1f40001809e0
   0x400f35:	mov    QWORD PTR [rbp-0x38],rax
   0x400f39:	lea    rax,[rbp-0x1c0]
   0x400f40:	mov    esi,0x401480
   0x400f45:	mov    edx,0x26
   0x400f4a:	mov    rdi,rax
   0x400f4d:	mov    rcx,rdx
   0x400f50:	rep movs QWORD PTR es:[rdi],QWORD PTR ds:[rsi]
   0x400f53:	mov    DWORD PTR [rbp-0x3c],0x26
   0x400f5a:	lea    rax,[rbp-0x250]
   0x400f61:	mov    edx,0x4015c0
   0x400f66:	mov    ecx,0x10
   0x400f6b:	mov    rdi,rax
   0x400f6e:	mov    rsi,rdx
   0x400f71:	rep movs QWORD PTR es:[rdi],QWORD PTR ds:[rsi]
   0x400f74:	mov    rdx,rsi
   0x400f77:	mov    rax,rdi
   0x400f7a:	movzx  ecx,WORD PTR [rdx]
   0x400f7d:	mov    WORD PTR [rax],cx
   0x400f80:	lea    rax,[rax+0x2]
   0x400f84:	lea    rdx,[rdx+0x2]
   0x400f88:	movzx  ecx,BYTE PTR [rdx]
   0x400f8b:	mov    BYTE PTR [rax],cl
   0x400f8d:	lea    rax,[rax+0x1]
   0x400f91:	lea    rdx,[rdx+0x1]
   0x400f95:	mov    rax,QWORD PTR [rip+0x200fb4]        # 0x601f50 <stdout>
   0x400f9c:	mov    esi,0x0
   0x400fa1:	mov    rdi,rax
   0x400fa4:	call   0x400ad0 <setbuf@plt>
   0x400fa9:	mov    DWORD PTR [rbp-0x24],0x0
   0x400fb0:	jmp    0x400fd3
   0x400fb2:	mov    eax,DWORD PTR [rbp-0x24]
   0x400fb5:	add    eax,eax
   0x400fb7:	cdqe   
   0x400fb9:	movzx  eax,BYTE PTR [rbp+rax*1-0x250]
   0x400fc1:	mov    edx,eax
   0x400fc3:	mov    eax,DWORD PTR [rbp-0x24]
   0x400fc6:	cdqe   
   0x400fc8:	mov    BYTE PTR [rbp+rax*1-0x2a0],dl
   0x400fcf:	add    DWORD PTR [rbp-0x24],0x1
   0x400fd3:	cmp    DWORD PTR [rbp-0x24],0x40
   0x400fd7:	jle    0x400fb2
   0x400fd9:	lea    rdx,[rbp-0x12a0]
   0x400fe0:	mov    eax,0x0
   0x400fe5:	mov    ecx,0x200
   0x400fea:	mov    rdi,rdx
   0x400fed:	rep stos QWORD PTR es:[rdi],rax
   0x400ff0:	mov    BYTE PTR [rbp-0x129f],0x2f
   0x400ff7:	mov    BYTE PTR [rbp-0x129d],0x74
   0x400ffe:	mov    BYTE PTR [rbp-0x129b],0x6d
   0x401005:	mov    BYTE PTR [rbp-0x1299],0x70
   0x40100c:	mov    BYTE PTR [rbp-0x1297],0x2f
   0x401013:	mov    BYTE PTR [rbp-0x1295],0x2e
   0x40101a:	mov    BYTE PTR [rbp-0x1293],0x74
   0x401021:	mov    BYTE PTR [rbp-0x1291],0x65
   0x401028:	mov    BYTE PTR [rbp-0x128f],0x72
   0x40102f:	mov    BYTE PTR [rbp-0x128d],0x61
   0x401036:	mov    BYTE PTR [rbp-0x128b],0xa
   0x40103d:	mov    DWORD PTR [rbp-0x28],0x0
   0x401044:	jmp    0x401068
   0x401046:	mov    eax,DWORD PTR [rbp-0x28]
   0x401049:	add    eax,eax
   0x40104b:	add    eax,0x1
   0x40104e:	cdqe   
   0x401050:	movzx  edx,BYTE PTR [rbp+rax*1-0x12a0]
   0x401058:	mov    eax,DWORD PTR [rbp-0x28]
   0x40105b:	cdqe   
   0x40105d:	mov    BYTE PTR [rbp+rax*1-0x22a0],dl
   0x401064:	add    DWORD PTR [rbp-0x28],0x1
   0x401068:	cmp    DWORD PTR [rbp-0x28],0x9
   0x40106c:	jle    0x401046
   0x40106e:	mov    BYTE PTR [rbp-0x2296],0x0
   0x401075:	call   0x400b30 <curl_easy_init@plt>

It looks like it uses libcurl to download a file from somewhere. We can see the file it downloads as argument of libcurl in RDI register.

(gdb) x/s $rdi
0x7fffffffe0e0:	 "http://darksky.slac.stanford.edu/simulations/ds14_a/ds14_a_1.0000"
(gdb) 
   0x40107a:	mov    QWORD PTR [rbp-0x48],rax
   0x40107e:	cmp    QWORD PTR [rbp-0x48],0x0
   0x401083:	je     0x4012a5
   0x401089:	mov    rax,rsp
   0x40108c:	mov    r12,rax
   0x40108f:	mov    edi,0x4013d8
   0x401094:	call   0x400a40 <puts@plt>
   0x401099:	lea    rax,[rbp-0x2340]
   0x4010a0:	mov    esi,0x401680
   0x4010a5:	mov    edx,0x13
   0x4010aa:	mov    rdi,rax
   0x4010ad:	mov    rcx,rdx
   0x4010b0:	rep movs QWORD PTR es:[rdi],QWORD PTR ds:[rsi]
   0x4010b3:	lea    rax,[rbp-0x22a0]
   0x4010ba:	mov    esi,0x4013fa
   0x4010bf:	mov    rdi,rax
   0x4010c2:	call   0x400a80 <fopen@plt>

It puts the downloaded file in /tmp/.tera

(gdb) x/s 0x4013fa
0x4013fa:	 "wb"
(gdb) x/s $rbp-0x22a0
0x7fffffffc0e0:	 "/tmp/.tera"
(gdb)
   0x4010c7:	mov    QWORD PTR [rbp-0x50],rax
   0x4010cb:	mov    rdx,QWORD PTR [rbp-0x48]
   0x4010cf:	lea    rax,[rbp-0x88]
   0x4010d6:	mov    rcx,rdx
   0x4010d9:	mov    edx,0x400d10
   0x4010de:	mov    esi,0x0
   0x4010e3:	mov    rdi,rax
   0x4010e6:	call   0x400aa0 <pthread_create@plt>
   0x4010eb:	mov    DWORD PTR [rbp-0x54],eax
   0x4010ee:	cmp    DWORD PTR [rbp-0x54],0x0
   0x4010f2:	je     0x40111f
   0x4010f4:	mov    rax,QWORD PTR [rip+0x200e45]        # 0x601f40 <stderr>
   0x4010fb:	mov    edx,DWORD PTR [rbp-0x54]
   0x4010fe:	mov    esi,0x401400
   0x401103:	mov    rdi,rax
   0x401106:	mov    eax,0x0
   0x40110b:	call   0x400b40 <fprintf@plt>
   0x401110:	mov    ebx,0x0
   0x401115:	mov    eax,0x0
   0x40111a:	jmp    0x40129b
   0x40111f:	mov    DWORD PTR [rbp-0x58],0x2712
   0x401126:	mov    ecx,DWORD PTR [rbp-0x58]
   0x401129:	lea    rdx,[rbp-0x2a0]
   0x401130:	mov    rax,QWORD PTR [rbp-0x48]
   0x401134:	mov    esi,ecx
   0x401136:	mov    rdi,rax
   0x401139:	mov    eax,0x0
   0x40113e:	call   0x400af0 <curl_easy_setopt@plt>
   0x401143:	mov    DWORD PTR [rbp-0x5c],0x4e2b
   0x40114a:	mov    ecx,DWORD PTR [rbp-0x5c]
   0x40114d:	mov    rax,QWORD PTR [rbp-0x48]
   0x401151:	mov    edx,0x400cd6
   0x401156:	mov    esi,ecx
   0x401158:	mov    rdi,rax
   0x40115b:	mov    eax,0x0
   0x401160:	call   0x400af0 <curl_easy_setopt@plt>
   0x401165:	mov    DWORD PTR [rbp-0x60],0x2711
   0x40116c:	mov    ecx,DWORD PTR [rbp-0x60]
   0x40116f:	mov    rdx,QWORD PTR [rbp-0x50]
   0x401173:	mov    rax,QWORD PTR [rbp-0x48]
   0x401177:	mov    esi,ecx
   0x401179:	mov    rdi,rax
   0x40117c:	mov    eax,0x0
   0x401181:	call   0x400af0 <curl_easy_setopt@plt>
   0x401186:	mov    rax,QWORD PTR [rbp-0x48]
   0x40118a:	mov    rdi,rax
   0x40118d:	call   0x400a60 <curl_easy_perform@plt>
   0x401192:	mov    DWORD PTR [rbp-0x64],eax
   0x401195:	mov    rax,QWORD PTR [rbp-0x48]
   0x401199:	mov    rdi,rax
   0x40119c:	call   0x400b10 <curl_easy_cleanup@plt>
   0x4011a1:	mov    rax,QWORD PTR [rbp-0x50]
   0x4011a5:	mov    rdi,rax
   0x4011a8:	call   0x400b00 <fclose@plt>

We don’t really care about the above code. It’s just some cleanup, file close and error checking.

   0x4011ad:	lea    rax,[rbp-0x22a0]
   0x4011b4:	mov    esi,0x4013c0
   0x4011b9:	mov    rdi,rax
   0x4011bc:	call   0x400a80 <fopen@plt>
   0x4011c1:	mov    QWORD PTR [rbp-0x70],rax
   0x4011c5:	mov    rax,QWORD PTR [rbp-0x38]
   0x4011c9:	lea    rdx,[rax-0x1]
   0x4011cd:	mov    QWORD PTR [rbp-0x78],rdx
   0x4011d1:	mov    rdx,rax
   0x4011d4:	mov    QWORD PTR [rbp-0x2350],rdx
   0x4011db:	mov    QWORD PTR [rbp-0x2348],0x0
   0x4011e6:	mov    rdx,rax
   0x4011e9:	mov    r13,rdx
   0x4011ec:	mov    r14d,0x0
   0x4011f2:	mov    rdx,rax
   0x4011f5:	mov    eax,0x10
   0x4011fa:	sub    rax,0x1
   0x4011fe:	add    rax,rdx
   0x401201:	mov    esi,0x10
   0x401206:	mov    edx,0x0
   0x40120b:	div    rsi
   0x40120e:	imul   rax,rax,0x10
   0x401212:	sub    rsp,rax
   0x401215:	mov    rax,rsp
   0x401218:	add    rax,0x0
   0x40121c:	mov    QWORD PTR [rbp-0x80],rax
   0x401220:	mov    rdx,QWORD PTR [rbp-0x38]
   0x401224:	mov    rax,QWORD PTR [rbp-0x80]
   0x401228:	mov    rcx,QWORD PTR [rbp-0x70]
   0x40122c:	mov    esi,0x1
   0x401231:	mov    rdi,rax
   0x401234:	call   0x400ae0 <fread@plt>
   0x401239:	mov    QWORD PTR [rbp-0x30],0x0
   0x401241:	jmp    0x40127f
   0x401243:	mov    rax,QWORD PTR [rbp-0x30]
   0x401247:	mov    rax,QWORD PTR [rbp+rax*8-0x1c0]
   0x40124f:	mov    rdx,QWORD PTR [rbp-0x80]
   0x401253:	movzx  eax,BYTE PTR [rdx+rax*1]
   0x401257:	mov    edx,eax
   0x401259:	mov    rax,QWORD PTR [rbp-0x30]
   0x40125d:	mov    eax,DWORD PTR [rbp+rax*4-0x2340]
   0x401264:	xor    eax,edx
   0x401266:	movsx  eax,al
   0x401269:	mov    esi,eax
   0x40126b:	mov    edi,0x40142a
   0x401270:	mov    eax,0x0
   0x401275:	call   0x400a10 <printf@plt>
   0x40127a:	add    QWORD PTR [rbp-0x30],0x1
   0x40127f:	mov    eax,DWORD PTR [rbp-0x3c]
   0x401282:	cdqe   
   0x401284:	cmp    rax,QWORD PTR [rbp-0x30]
   0x401288:	jg     0x401243
   0x40128a:	mov    rax,QWORD PTR [rbp-0x70]
   0x40128e:	mov    rdi,rax
   0x401291:	call   0x400b00 <fclose@plt>

This is where all the magic happens. I will go over the above code section in more details in a little bit.

   0x401296:	mov    eax,0x1
   0x40129b:	mov    rsp,r12
   0x40129e:	cmp    eax,0x1
   0x4012a1:	jne    0x4012b4
   0x4012a3:	jmp    0x4012af
   0x4012a5:	mov    edi,0x401430
   0x4012aa:	call   0x400a40 <puts@plt>
   0x4012af:	mov    ebx,0x0
   0x4012b4:	mov    eax,ebx
   0x4012b6:	lea    rsp,[rbp-0x20]
   0x4012ba:	pop    rbx
   0x4012bb:	pop    r12
   0x4012bd:	pop    r13
   0x4012bf:	pop    r14
   0x4012c1:	pop    rbp
   0x4012c2:	ret

…and the function epilog.

Ok, from the quick overview, it looks like the binary is downloading a file, puts it in /tmp/.tera, reads it and does something to the read bytes. It then prints the result, closes the file and exits.

Looking at the file it downloads, it looks like it’s a 31 TB file… We know we don’t have the disk space nor the ram to read this file.

image

Let’s see what’s the magic that it does with this file.

   0x4011ad:	lea    rax,[rbp-0x22a0]
   0x4011b4:	mov    esi,0x4013c0
   0x4011b9:	mov    rdi,rax
   0x4011bc:	call   0x400a80 <fopen@plt>
(gdb) x/s 0x4013c0
0x4013c0:	 "r"
(gdb) x/s $rbp-0x22a0
0x7fffffffc0e0:	 "/tmp/.tera"
(gdb)
   0x4011c1:	mov    QWORD PTR [rbp-0x70],rax
   0x4011c5:	mov    rax,QWORD PTR [rbp-0x38]
   0x4011c9:	lea    rdx,[rax-0x1]
   0x4011cd:	mov    QWORD PTR [rbp-0x78],rdx
   0x4011d1:	mov    rdx,rax
   0x4011d4:	mov    QWORD PTR [rbp-0x2350],rdx
   0x4011db:	mov    QWORD PTR [rbp-0x2348],0x0
   0x4011e6:	mov    rdx,rax
   0x4011e9:	mov    r13,rdx
   0x4011ec:	mov    r14d,0x0
   0x4011f2:	mov    rdx,rax
   0x4011f5:	mov    eax,0x10
   0x4011fa:	sub    rax,0x1
   0x4011fe:	add    rax,rdx
   0x401201:	mov    esi,0x10
   0x401206:	mov    edx,0x0
   0x40120b:	div    rsi
   0x40120e:	imul   rax,rax,0x10
   0x401212:	sub    rsp,rax
   0x401215:	mov    rax,rsp
   0x401218:	add    rax,0x0
   0x40121c:	mov    QWORD PTR [rbp-0x80],rax
   0x401220:	mov    rdx,QWORD PTR [rbp-0x38]
   0x401224:	mov    rax,QWORD PTR [rbp-0x80]
   0x401228:	mov    rcx,QWORD PTR [rbp-0x70]
   0x40122c:	mov    esi,0x1
   0x401231:	mov    rdi,rax
   0x401234:	call   0x400ae0 <fread@plt>

Ok, it opens the downloaded file and reads it.

   0x401239:	mov    QWORD PTR [rbp-0x30],0x0
   0x401241:	jmp    0x40127f
   0x401243:	mov    rax,QWORD PTR [rbp-0x30]   <------------
   0x401247:	mov    rax,QWORD PTR [rbp+rax*8-0x1c0] <------|------
   0x40124f:	mov    rdx,QWORD PTR [rbp-0x80]               |     | It takes 1 byte argument from offset 
   0x401253:	movzx  eax,BYTE PTR [rdx+rax*1]        <------|------
   0x401257:	mov    edx,eax                                |
   0x401259:	mov    rax,QWORD PTR [rbp-0x30]        <------|----- On these 2 lines takes another 1 byte offset
   0x40125d:	mov    eax,DWORD PTR [rbp+rax*4-0x2340]<------|-----
   0x401264:	xor    eax,edx        <=======================|=========== XORs the two bytes
   0x401266:	movsx  eax,al                                 |
   0x401269:	mov    esi,eax                                |
   0x40126b:	mov    edi,0x40142a   <=======================|====== Prints the result in single character format.
   0x401270:	mov    eax,0x0                                |       (gdb) x/s 0x40142a
   0x401275:	call   0x400a10 <printf@plt>                  |       0x40142a:	 "%c\n"
   0x40127a:	add    QWORD PTR [rbp-0x30],0x1               |
   0x40127f:	mov    eax,DWORD PTR [rbp-0x3c]               |
   0x401282:	cdqe                                          |
   0x401284:	cmp    rax,QWORD PTR [rbp-0x30]         <=====|===== Looks like it loops 38 times.
   0x401288:	jg     0x401243                   <------------
   0x40128a:	mov    rax,QWORD PTR [rbp-0x70]
   0x40128e:	mov    rdi,rax
   0x401291:	call   0x400b00 <fclose@plt>

If we inspect the memory regions we can get the following 38 offsets.

4C89617CF4
0B4B5E95F83
0E4598D686B
136A62674EF
1837A65BEB7
19FA831467C
2A6202ACD01
4493F10645E
4CDCE6D65E4
5028EC8DE7E
56219504A56
5BD2D191DB8
72BD5D02592
73DEE6D04FE
0A25E5AFE320
0A73B464FB9E
0B6259F6E34B
0B9AA45094DC
0BC548E0EA39
0C7AC41ECC56
0C85F073FB8B
0C92536A9116
0D930BE6DABF
0E61B989DA40
0F37999CA268
0FB7C59B9D1F
1018D3A3939D
10202AED0369
10E8FB926CF3
113BC38EA065
13257504044F
14FB0612DC3C
16572370DA92
173D75634441
1B9D0F2D9374
1BA90DE42D8E
1BE9EF4C8F3E
1BFDA4B84E00

We need to convert them from hex to decimal and use the value as an offset to extract 1 byte from the 31 TB file. But how do we do that if we don’t have the file ?

Well, luckily for us the web server uses Accept-Ranges HTTP response header. This means we can use the Range header to extract data from any arbitrary offset. Here is a one-liner bash script to fetch the byte at the given offsets and store it into a file bytes.txt.

$ for i in `cat offsets.txt` ; do curl http://darksky.slac.stanford.edu/simulations/ds14_a/ds14_a_1.0000 -H "Range: bytes=$(( 0x$i ))-$(( 0x$i ))" | xxd ; done >> bytes.txt

We have the bytes that needs to be XORed with the keys. We can get the 1 byte XOR keys from the second offset in the binary.

f2
9a
83
12
39
45
e7
f4
6f
a1
06
e7
95
f3
90
f2
f0
6b
33
e3
a8
78
37
d5
44
39
61
8a
fb
22
fa
9e
e7
11
39
a6
f3
33

Now let’s make a quick python script to XOR each file extracted byte with the key byte.

#!/usr/bin/env python

inbytes = open('bytes.txt', 'r')
inkeys = open('keys.txt', 'r')

byte_array = []
key_array = []

flag = []

for i in inbytes:
	byte_array.append(i.strip())

for x in inkeys:
	key_array.append(x.strip())

for byte in range(len(byte_array)):
	b = byte_array[byte]
	k = key_array[byte]
	flag.append(chr(int(b, 16) ^ int(k, 16)))

print ''.join(flag)
$python ./xor_tera.py 
ASIS{3149ad5d3629581b17279cc889222b93}