ASIS-ctf 2015 Leach

@mrexcessive WHA

The problem

Find the flag in this file.

The solution
Get file.
xz -d
file leach
chmod +x

OK so it bounces an ascii breakout around and says:

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

The game gets played by a forked thread...
Last write out from main thread is the "this may take too long time... :)" then nothing... but the ball bounces around from the cloned thread.


Unlike fork(2), these calls allow the child process to share parts of its execution context with the calling process, such as the memory space, the table of file descriptors, and the table of signal handlers. (Note that on this manual page, "calling process" normally corresponds to "parent process". But see the description of CLONE_PARENT below.) 

A function pointer is passed in to clone()

#define _GNU_SOURCE             /* See feature_test_macros(7) */

int clone(int (*fn)(void *), void *child_stack,
          int flags, void *arg, ...
          /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );

So ... do we just a) speed it up and b) fake the winning bats ?
No... the bats move by themselves. Is it just a timewaster ?

Hmmm... maybe outer thread sends keystrokes or something

$ readelf -a ./leach >elfread.txt

Doesnt' show any obvious input call...

What is being fetched at 0x4011f0 ?

  4011dd:    c7 05 11 1a 20 00 00    movl   $0x0,0x201a11(%rip)        # 602bf8 
  4011e4:    00 00 00 
  4011e7:    83 45 fc 01             addl   $0x1,-0x4(%rbp)
  4011eb:    8b 45 fc                mov    -0x4(%rbp),%eax
  4011ee:    48 98                   cltq   
  4011f0:    48 8b 04 c5 40 25 60    mov    0x602540(,%rax,8),%rax
  4011f7:    00 
  4011f8:    48 89 45 e8             mov    %rax,-0x18(%rbp)
  4011fc:    48 83 7d e8 00          cmpq   $0x0,-0x18(%rbp)
  401201:    0f 85 1a ff ff ff       jne    401121 

Try patching - with bvi - end of game loop to exit not loop

00001070  1B 20 00 85 C0 0F 85* C7 FE FF FF C9 C3 55 48 89 . ...........UH.

change that x85 to x84 - now

401075:    0f 85 c7 fe ff ff       jne    400f42     # 0f84 would be je instead...

Just hangs.. so something checking timer ?

I wonder if inner game has to work.. or not ?

So just for confirmation that something inside matters:

  401201:    0f 85 1a ff ff ff       jne    401121     # ^^^LOOP

Patch that one to je as well... then everything is complete.

OK that just exits...

Lets see if I can speed things up

Two places are:

                                ********* inside the inner game thread *********
  400f42:    bf 40 0d 03 00          mov    $0x30d40,%edi                   ; usleep for 0x30d40
  400f47:    e8 54 fa ff ff          callq  4009a0 


                            ********* outer loop *******
  401133:    8b 04 85 c0 29 60 00    mov    0x6029c0(,%rax,4),%eax
  40113a:    89 c7                   mov    %eax,%edi                    # usleep something edi... 
  40113c:    e8 3f f8 ff ff          callq  400980            # !! not micro-sleep ()

OK so the bats are moving to get in the way... maybe we need to make it lose ;)

Doesn't get to here...

  4011a1:    8b 0c 85 00 23 60 00    mov    0x602300(,%rax,4),%ecx       # read from table.. ?


Does it get to the sleep call.. and what value in there ?

  401133:    8b 04 85 c0 29 60 00    mov    0x6029c0(,%rax,4),%eax
  40113a:    89 c7                   mov    %eax,%edi                    # usleep something edi... 
  40113c:    e8 3f f8 ff ff          callq  400980            # usleep ()
has 0x10cc... lots of seconds

Hmmm... maybe this is something then

(gdb) x/128xw 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

So when this does print something...
It sleeps, then gets time and subtracts start time... so it effectively gets the sleep count back I think
That stops people just bypassing the sleep count

What is at BP-x18 ?
Can we just patch our way to that first bit

(gdb) b *0x401121
; go up to sleep call - but not do it

; RAX has the 0x10cc
; So change that to 1
; Grr never returns

OK need bp maybe, cos thread things


(gdb) b*0x40113c
(gdb) run
(gdb) hb *0x401141
(gdb) set $rax = 1
(gdb) conti

OK weird... that also never returns.

Maybe just not do the sleep :)
Patch it out...

$ bvi
Patch to NOP = x90 * 5
00001130  FC 48 98 8B 04 85 C0 29 60 00 89 C7 90 90 90 90 .H.....)`.......
00001140  90 BF 00 00 00 00 E8 F5 F7 FF FF 89 C2 8B 45 F8 ..............E.

Rerun but bp at 0x401146
Gets there...
EDX has 6... (seconds elapsed time)
so patch that to 0x10cc
and follow on, until... just before strcpy() @0x401181

RSI: 0x4012a8 --> 0x3300373935393031 ('109597')            # this is the 'extra bit'
RDI: 0x7fffffffe260 --> 0x7ffff7bc71e0 (<__wait_lookup_done>:    push   r14)

; Ah OK maybe this is output bufferage... building huge number ?
; Now strcat() on the time bit 0x10cc => 4300 decimal, @ 0x401197
RSI: 0x7fffffffe2d0 --> 0x30303334 ('4300')
RDI: 0x7fffffffe260 --> 0x373935393031 ('109597')
; Giving:
RDI: 0x7fffffffe260 ("1095974300")

Table lookup via RAX*4 + 0x602300 gives these :

(gdb) x/100xw 0x602300
0x602300:    0x0000012c  0x00000014  0x00000017  0x00000009
0x602310:    0x00000014  0x0000000d  0x0000000f  0x0000001e
0x602320:    0x0000000e  0x00000003  0x00000002  0x0000000a
0x602330:    0x00000006  0x0000000c  0x00000009  0x00000021
0x602340:    0x00000005  0x00000014  0x0000000a  0x00000003
0x602350:    0x0000000d  0x00000002  0x00000007  0x00000010
0x602360:    0x0000012c  0x0000000d  0x00000002  0x00000016
0x602370:    0x0000000d  0x00000012  0x0000000a  0x00000004
0x602380:    0x0000000c  0x00000063  0x00000006  0x0000000b
0x602390:    0x00000014  0x00000013  0x00000020  0x0000000b
0x6023a0:    0x00000009  0x0000000a  0x0000000f  0x0000000b
0x6023b0:    0x00000002  0x00000013  0x00000011  0x00000004
0x6023c0:    0x00000032  0x0000000c  0x0000000a  0x00000003
0x6023d0:    0x00000016  0x00000001  0x0000000c  0x0000000f
0x6023e0:    0x0000000b  0x00000013  0x0000000b  0x0000000c
0x6023f0:    0x00000002  0x0000000a  0x00000028  0x00000019
0x602400:    0x00000008  0x0000000b  0x0000000c  0x00000003
0x602410:    0x00000004  0x0000000c  0x00000013  0x00000006
0x602420:    0x0000000d  0x00000014  0x0000001f  0x00000009
0x602430:    0x00000010  0x00000001  0x00000003  0x00000007
0x602440:    0x0000000b  0x00000004  0x00000003  0x00000013
0x602450:    0x0000000b  0x00000021  0x0000000a  0x0000001e
0x602460:    0x00000009  0x00000003  0x00000024  0x0000003c
0x602470:    0x00000033  0x0000000b  0x00000014  0x0000000c
0x602480:    0x000000c8  0x00000002  0x00000001  0x00000009

to RCX
So in this case first entry 0x12c
RDX gets pointer to a function or table
RDX: 0x7fffffffe334 --> 0x4012a800000000


(gdb) x/256bx 0x4012a8
0x4012a8:    0x31    0x30    0x39    0x35    0x39    0x37    0x00    0x33
0x4012b0:    0x30    0x30    0x30    0x00    0x33    0x30    0x30    0x00
0x4012b8:    0x38    0x30    0x38    0x00    0x33    0x36    0x39    0x00
0x4012c0:    0x31    0x36    0x34    0x37    0x36    0x30    0x30    0x30
0x4012c8:    0x00    0x31    0x36    0x34    0x37    0x36    0x30    0x30
0x4012d0:    0x00    0x31    0x36    0x34    0x37    0x36    0x30    0x00
0x4012d8:    0x36    0x34    0x34    0x00    0x36    0x35    0x30    0x30
0x4012e0:    0x00    0x36    0x35    0x30    0x00    0x36    0x36    0x39
0x4012e8:    0x00    0x36    0x33    0x37    0x30    0x00    0x38    0x39
0x4012f0:    0x32    0x35    0x36    0x30    0x30    0x30    0x00    0x38
0x4012f8:    0x39    0x32    0x35    0x36    0x30    0x30    0x00    0x38
0x401300:    0x39    0x32    0x35    0x36    0x30    0x00    0x33    0x31
0x401308:    0x35    0x00    0x31    0x00    0x00    0x00    0x25    0x78
0x401310:    0x00    0x30    0x31    0x32    0x33    0x34    0x35    0x36
0x401318:    0x37    0x38    0x39    0x61    0x62    0x63    0x64    0x65
0x401320:    0x66    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x401328:    0x23    0x23    0x23    0x23    0x23    0x23    0x23    0x23
0x401330:    0x23    0x23    0x23    0x23    0x23    0x23    0x23    0x23
0x401338:    0x23    0x23    0x23    0x23    0x23    0x23    0x23    0x23
0x401340:    0x23    0x23    0x23    0x23    0x23    0x23    0x00    0x1b
0x401348:    0x5b    0x39    0x41    0x00    0x00    0x00    0x00    0x00
0x401350:    0x74    0x68    0x69    0x73    0x20    0x6d    0x61    0x79
0x401358:    0x20    0x74    0x61    0x6b    0x65    0x20    0x74    0x6f
0x401360:    0x6f    0x20    0x6c    0x6f    0x6e    0x67    0x20    0x74
0x401368:    0x69    0x6d    0x65    0x20    0x2e    0x2e    0x2e    0x20
0x401370:    0x3a    0x29    0x00    0x00    0x00    0x00    0x00    0x00
0x401378:    0x45    0x72    0x72    0x6f    0x72    0x20    0x2d    0x20
0x401380:    0x70    0x74    0x68    0x72    0x65    0x61    0x64    0x5f
0x401388:    0x63    0x72    0x65    0x61    0x74    0x65    0x28    0x29
0x401390:    0x20    0x72    0x65    0x74    0x75    0x72    0x6e    0x20
0x401398:    0x63    0x6f    0x64    0x65    0x3a    0x20    0x25    0x64
0x4013a0:    0x0a    0x00    0x25    0x64    0x00    0x00    0x00    0x00

RAX gets pointer to this time
RAX: 0x7fffffffe260 ("1095974300")

ESI gets the table pointer from ECX
RDI gets the one from EAX

OK so just want to see what comes out from the loopy/crazy/complicated routines... called via 0x400d65

(gdb) hb *0x4011b8

OK just going to patch it to bypass the time/sleep checks passing correct value through and see what happens...

We NOP out a block

  401133:    8b 04 85 c0 29 60 00    mov    0x6029c0(,%rax,4),%eax

NOP all this --->
  40113a:    89 c7                   mov    %eax,%edi                    # sleep something edi... 
  40113c:    e8 3f f8 ff ff          callq  400980            # sleep ()  !!!! not usleep... LOOONG sleep()s
  401141:    bf 00 00 00 00          mov    $0x0,%edi
  401146:    e8 f5 f7 ff ff          callq  400940             # get time()
  40114b:    89 c2                   mov    %eax,%edx                    #
  40114d:    8b 45 f8                mov    -0x8(%rbp),%eax              # subtract our stored time
  401150:    29 c2                   sub    %eax,%edx                    # ... result -> edx
  401152:    89 d0                   mov    %edx,%eax                    # now --> eax
END NOPpage // ok

  401154:    89 45 f4                mov    %eax,-0xc(%rbp)              # now --> [bp-xc]

Leaving the top and bottom instructions there...^^^ this gets the sleep() time value and pretends we slept that long
... probably (TM) ...

Now we have

00001110  00 00 00 00 48 89 C7 E8 C4 F7 FF FF E9 CA 00 00 ....H...........
00001120  00 BF 00 00 00 00 E8 15 F8 FF FF 89 45 F8 8B 45 ............E..E
00001130  FC 48 98 8B 04 85 C0 29 60 00 90 90 90 90 90 90 .H.....)`.......    ; 
00001160  13 40 00 48 89 C7 B8 00 00 00 00 E8 00 F8 FF FF .@.H............

Run it
$ ./leach

Yay !!!