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)