Bypassing Canary & PIE
Last updated
Last updated
If you are facing a binary protected by a canary and PIE (Position Independent Executable) you probably need to find a way to bypass them.
Note that checksec
might not find that a binary is protected by a canary if this was statically compiled and it's not capable to identify the function.
However, you can manually notice this if you find that a value is saved in the stack at the begging of a function call and this value is checked before exiting.
The best way to bypass a simple canary is if the binary is a program forking child processes every time you establish a new connection with it (network service), because every time you connect to it the same canary will be used.
Then, the best way to bypass the canary is just to brute-force it char by char, and you can figure out if the guessed canary byte was correct checking if the program has crashed or continues its regular flow. In this example the function brute-forces an 8 Bytes canary (x64) and distinguish between a correct guessed byte and a bad byte just checking if a response is sent back by the server (another way in other situation could be using a try/except):
This example is implemented for 64bits but could be easily implemented for 32 bits.
This is implemented for 32 bits, but this could be easily changed to 64bits. Also note that for this example the program expected first a byte to indicate the size of the input and the payload.
Another way to bypass the canary is to print it.
Imagine a situation where a program vulnerable to stack overflow can execute a puts function pointing to part of the stack overflow. The attacker knows that the first byte of the canary is a null byte (\x00
) and the rest of the canary are random bytes. Then, the attacker may create an overflow that overwrites the stack until just the first byte of the canary.
Then, the attacker calls the puts functionality on the middle of the payload which will print all the canary (except from the first null byte).
With this info the attacker can craft and send a new attack knowing the canary (in the same program session)
Obviously, this tactic is very restricted as the attacker needs to be able to print the content of his payload to exfiltrate the canary and then be able to create a new payload (in the same program session) and send the real buffer overflow. CTF example: https://guyinatuxedo.github.io/08-bof_dynamic/csawquals17_svc/index.html
In order to bypass the PIE you need to leak some address. And if the binary is not leaking any addresses the best to do it is to brute-force the RBP and RIP saved in the stack in the vulnerable function. For example, if a binary is protected using both a canary and PIE, you can start brute-forcing the canary, then the next 8 Bytes (x64) will be the saved RBP and the next 8 Bytes will be the saved RIP.
To brute-force the RBP and the RIP from the binary you can figure out that a valid guessed byte is correct if the program output something or it just doesn't crash. The same function as the provided for brute-forcing the canary can be used to brute-force the RBP and the RIP:
The last thing you need to defeat the PIE is to calculate useful addresses from the leaked addresses: the RBP and the RIP.
From the RBP you can calculate where are you writing your shell in the stack. This can be very useful to know where are you going to write the string "/bin/sh\x00" inside the stack. To calculate the distance between the leaked RBP and your shellcode you can just put a breakpoint after leaking the RBP an check where is your shellcode located, then, you can calculate the distance between the shellcode and the RBP:
From the RIP you can calculate the base address of the PIE binary which is what you are going to need to create a valid ROP chain.
To calculate the base address just do objdump -d vunbinary
and check the disassemble latest addresses:
In that example you can see that only 1 Byte and a half is needed to locate all the code, then, the base address in this situation will be the leaked RIP but finishing on "000". For example if you leaked 0x562002970ecf the base address is 0x562002970000