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

Find the flag in this file.

Write-up

Another 64 bit ELF binary, stripped.

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

If we start the binary, we see an never ending game of pong.

# ./leach
this may take too long time ... :)
#                            #
##############################
#|                           #
#                            #
#                            #
#                           |#
#                            #
#                            #
#                            #
#                o           #
##############################

Let’s take a look at the assembly.
Main function:

(gdb) x/200i 0x40107d
=> 0x40107d:	push   rbp
   0x40107e:	mov    rbp,rsp
   0x401081:	sub    rsp,0x110
   0x401088:	mov    DWORD PTR [rbp-0x104],edi
   0x40108e:	mov    QWORD PTR [rbp-0x110],rsi
   0x401095:	mov    edi,0x401350
   0x40109a:	call   0x4008c0 <puts@plt>
   0x40109f:	mov    DWORD PTR [rbp-0x4],0x0
   0x4010a6:	mov    DWORD PTR [rbp-0x1c],0x0
   0x4010ad:	mov    DWORD PTR [rbp-0x8],0x0
   0x4010b4:	mov    DWORD PTR [rbp-0xc],0x0
   0x4010bb:	lea    rax,[rbp-0xf8]
   0x4010c2:	mov    ecx,0x0
   0x4010c7:	mov    edx,0x400ee9
   0x4010cc:	mov    esi,0x0
   0x4010d1:	mov    rdi,rax
   0x4010d4:	call   0x4008a0 <pthread_create@plt>
   0x4010d9:	mov    DWORD PTR [rbp-0x10],eax
   0x4010dc:	cmp    DWORD PTR [rbp-0x10],0x0
   0x4010e0:	je     0x401108
   0x4010e2:	mov    rax,QWORD PTR [rip+0x201b1f]        # 0x602c08 <stderr>
   0x4010e9:	mov    edx,DWORD PTR [rbp-0x10]
   0x4010ec:	mov    esi,0x401378
   0x4010f1:	mov    rdi,rax
   0x4010f4:	mov    eax,0x0
   0x4010f9:	call   0x400920 <fprintf@plt>
   0x4010fe:	mov    eax,0x0
   0x401103:	jmp    0x401216

Starting with the function’s prolog, the function prints an entry message and starts a new thread. If not successful, prints an error message and exits. If successful it jumps to the next block of code.

   0x401108:	mov    rax,QWORD PTR [rip+0x201af1]        # 0x602c00 <stdout>
   0x40110f:	mov    esi,0x0
   0x401114:	mov    rdi,rax
   0x401117:	call   0x4008e0 <setbuf@plt>
   0x40111c:	jmp    0x4011eb
   0x401121:	mov    edi,0x0
   0x401126:	call   0x400940 <time@plt>
   0x40112b:	mov    DWORD PTR [rbp-0x8],eax
   0x40112e:	mov    eax,DWORD PTR [rbp-0x4]
   0x401131:	cdqe   
   0x401133:	mov    eax,DWORD PTR [rax*4+0x6029c0]
   0x40113a:	mov    edi,eax
   0x40113c:	call   0x400980 <sleep@plt>
   0x401141:	mov    edi,0x0
   0x401146:	call   0x400940 <time@plt>
   0x40114b:	mov    edx,eax
   0x40114d:	mov    eax,DWORD PTR [rbp-0x8]
   0x401150:	sub    edx,eax
   0x401152:	mov    eax,edx
   0x401154:	mov    DWORD PTR [rbp-0xc],eax

This is where my solution lies. It basically gets the current time with the first call to time(). Sets a sleep interval based on some constant from rax*4+0x6029c0. After the sleep call is over, it calls time() again and subtracts it from the value time() was called the first time. I’m not going to cover the rest of the code because it’s pretty lengthy, but it basically uses the result as a key for flag decryption routine. Also, while the process is sleeping, the pong game plays… pretty cool :).
The way I accomplished this was in edb, set a Break Point at 0x40113a before the sleep call, when the BP was hit I manually modified the RAX register to 0. And another Break Point after at 0x401152 so I could modify the result of time - time to the sleep interval in the EDX register. Yes, I know doing it manually is kind of a pain… Looking at how many sleep values are provided in the binary… (exactly 144 :P)

(gdb) x/200wx $rax*4+0x6029c0
0x6029c0:	0x000010cc	0x000010d1	0x000010d4	0x000010d8
0x6029d0:	0x000010db	0x000010eb	0x00001109	0x00001325
0x6029e0:	0x0000132d	0x00001341	0x00001352	0x00001370
0x6029f0:	0x00001380	0x00001750	0x00001760	0x000017ce
0x602a00:	0x00001c83	0x00001ca5	0x00001cb5	0x00001cc1
0x602a10:	0x00001d20	0x00001fcf	0x00001fea	0x00001fd9
0x602a20:	0x00002005	0x00002016	0x0000201a	0x00000179
0x602a30:	0x0000024b	0x00000a9f	0x0000177a	0x0000178e
0x602a40:	0x000017d5	0x00001b04	0x00001b3c	0x00001b85
0x602a50:	0x00001b99	0x00001bb1	0x00001c51	0x00001ca3
0x602a60:	0x00001d9d	0x00001dd0	0x00000ff1	0x00001003
0x602a70:	0x000010eb	0x000013ec	0x0000140f	0x00001420
0x602a80:	0x00001487	0x000014a2	0x000014ce	0x00001506
0x602a90:	0x0000002a	0x00000031	0x00000048	0x0000004f
0x602aa0:	0x000000aa	0x0000048b	0x00000498	0x000004af
0x602ab0:	0x000004b1	0x000004cd	0x000004d9	0x000004e5
0x602ac0:	0x0000054e	0x00001ed4	0x00001edb	0x00001ef5
0x602ad0:	0x00001ef6	0x00001f24	0x00001f39	0x00001f67
0x602ae0:	0x00001f97	0x00001fb6	0x00001fdf	0x000003aa
0x602af0:	0x000003cc	0x000003de	0x00000441	0x00000645
0x602b00:	0x00000859	0x0000097a	0x000009c1	0x00000a00
0x602b10:	0x00000a0a	0x00000a5c	0x00001d61	0x00001d92
0x602b20:	0x00001dd1	0x00001df5	0x00001e27	0x00001e4c
0x602b30:	0x00001e69	0x00001eef	0x0000015d	0x00000191
0x602b40:	0x000001b6	0x00000201	0x00000227	0x0000025b
0x602b50:	0x00000001	0x00000033	0x0000004f	0x00000114
0x602b60:	0x000001ad	0x00000210	0x00000261	0x000002e6
0x602b70:	0x0000033d	0x000003b7	0x000003d3	0x00001ef2
0x602b80:	0x00001f82	0x00001fb6	0x0000204a	0x00002075
0x602b90:	0x000020e2	0x0000211f	0x000021ae	0x00002268
0x602ba0:	0x00001afb	0x00001c0f	0x00001d80	0x000020c3
0x602bb0:	0x000021e0	0x0000223c	0x000023a3	0x00002491
0x602bc0:	0x00000831	0x000008c9	0x00000959	0x000009bc
0x602bd0:	0x000009d4	0x00000a5e	0x00000ac1	0x00000af7
0x602be0:	0x00000b6d	0x00000c19	0x00000c7d	0x00000da7
0x602bf0:	0x00000e22	0x00000ea3	0x00000001	0x00000000

Thanks to gaffe you don’t have to go through this manually. You can use the following gdb python script :).

#!/usr/bin/python3

import gdb

seconds = [0x10CC, 0x10D1, 0x10D4, 0x10D8, 0x10DB, 0x10EB, 0x1109, 0x1325, 0x132D, 0x1341, 0x1352, 0x1370, 0x1380, 0x1750, 0x1760, 0x17CE, 0x1C83, 0x1CA5, 0x1CB5, 0x1CC1, 0x1D20, 0x1FCF, 0x1FEA, 0x1FD9, 0x2005, 0x2016, 0x201A, 0x179, 0x24B, 0x0A9F, 0x177A, 0x178E, 0x17D5, 0x1B04, 0x1B3C, 0x1B85, 0x1B99, 0x1BB1, 0x1C51, 0x1CA3, 0x1D9D, 0x1DD0, 0x0FF1, 0x1003, 0x10EB, 0x13EC, 0x140F, 0x1420, 0x1487, 0x14A2, 0x14CE, 0x1506, 0x2A, 0x31, 0x48, 0x4F, 0x0AA, 0x48B, 0x498, 0x4AF, 0x4B1, 0x4CD, 0x4D9, 0x4E5, 0x54E, 0x1ED4, 0x1EDB, 0x1EF5, 0x1EF6, 0x1F24, 0x1F39, 0x1F67, 0x1F97, 0x1FB6, 0x1FDF, 0x3AA, 0x3CC, 0x3DE, 0x441, 0x645, 0x859, 0x97A, 0x9C1, 0x0A00, 0x0A0A, 0x0A5C, 0x1D61, 0x1D92, 0x1DD1, 0x1DF5, 0x1E27, 0x1E4C, 0x1E69, 0x1EEF, 0x15D, 0x191, 0x1B6, 0x201, 0x227, 0x25B, 1, 0x33, 0x4F, 0x114, 0x1AD, 0x210, 0x261, 0x2E6, 0x33D, 0x3B7, 0x3D3, 0x1EF2, 0x1F82, 0x1FB6, 0x204A, 0x2075, 0x20E2, 0x211F, 0x21AE, 0x2268, 0x1AFB, 0x1C0F, 0x1D80, 0x20C3, 0x21E0, 0x223C, 0x23A3, 0x2491, 0x831, 0x8C9, 0x959, 0x9BC, 0x9D4, 0x0A5E, 0x0AC1, 0x0AF7, 0x0B6D, 0x0C19, 0x0C7D, 0x0DA7, 0x0E22, 0x0EA3]

# right before call to sleep()
gdb.Breakpoint("*0x40113a")
# right before saving the time difference after calling time()
gdb.Breakpoint("*0x401154")

gdb.execute("run", False, True)

for timevalue in seconds:
	gdb.execute("set $rax = 0")
	gdb.execute("continue", False, True)
	        # fix time interval
	gdb.execute("set $rax = 0x%08x" % timevalue)
	gdb.execute("continue", False, True)
$ gdb -q ./leach 
Reading symbols from ctf/ASIS/re250/leach...(no debugging symbols found)...done.
(gdb) source gdb_leach.py
Breakpoint 1 at 0x40113a
Breakpoint 2 at 0x401154
this may take too long time ... :)

##############################
ASIS{f18b0b4f1bc6c8af21a4a53ef002f9a2}(gdb)