PwnTools
A Python library that helps in creating scripts for binary exploitation, doing many things automagically
Last updated
A Python library that helps in creating scripts for binary exploitation, doing many things automagically
Last updated
PwnTools is a Python library and includes some command-line tools as well. You can install it to your current Python version with the following command:
The full documentation is available on docs.pwntools.com:
When you install PwnTools, it comes with a few small but useful binaries for binary exploitation. Here are some and how to use them.
checksec
- Check security protectionsThis tool checks a few security-related settings on a binary, which will help you visualize what attacks might work, and which ones won't. You can run it like checksec ./binary
and see an output like the following:
In this output, green is safe, and red is unsafe. Note that if these are all green, it does not mean the binary has no vulnerabilities. It just means that you will have to use some other tricks during exploitation. Let's go over what these mean:
RELRO decides whether or not a few sections in the binary are read-only, preventing relocation tables from being overwritten in some cases. There are 3 possible values for this setting:
A stack canary is a reference to canaries in a coal mine. When a canary got sick, the miners would know it is unsafe here and stop mining. For a binary, it is the same idea: Right before the return address on the stack, a random value is placed. Then before actually returning at the ret
instruction, it checks if this random value is still the same. If it was overwritten by a buffer overflow the value would change, and then the program would panic and exit before anything malicious can happen.
Sometimes attackers will place shellcode on the stack, within their input. This can then be jumped to if they can control the Instruction Pointer, to execute arbitrary instructions, such as a shell.
With NX enabled, the stack is made non-executable to not allow any code to be run that came from the stack. This way, instructions are stored in the code section, and data is stored on the stack/heap.
In this case, an attacker would have to use existing instructions to execute what they want as they cannot add their own, often requiring some sort of Return-Oriented Programming (ROP).
With PIE enabled, the code of the binary will be loaded at a random memory location. This means you cannot use hardcoded addresses for functions or other instructions, as you won't know beforehand at what address they will be.
However, everything is offset together, meaning the relative addresses stay the same. So if you can leak any code address you can find all the relative addresses from that. If for some reason you can make a relative jump, this also won't stop you.
The address PwnTools shows for this when it is disabled means the base address the binary will always start from. This address is often 0x400000
by default, but it may be changed in the binary itself.
cyclic
- Create cyclic patternsAnd when you have triggered the vulnerability, you can take the value you find as the instruction pointer and reverse look it up again to find the exact offset in one go:
The pwn
Python library has many useful features, some more known than others. Here is a collection of a few pieces of syntax that can drastically simplify your exploit scripts.
Learn it once and you can never go back.
Now all the functions and variables of pwntools are imported to the current context.
When working with interaction in PwnTools, you should almost exclusively use bytestings. These guarantee that your payload or text won't be misinterpreted by UTF-8 conversions, and can be easily created using the b""
syntax.
flat()
: The relocation tables are not protected and can be modified at runtime, making the binary vulnerable to GOT and PLT overwrite attacks
: The relocation tables are made read-only, but some parts of the GOT are left writeable to allow lazy symbol binding. This still allows some attacks like ret2dlresolve where this is abused to link arbitrary functions like system()
in a binary without it. This protection often does not make much of a difference when writing your exploit
: This makes the GOT completely read-only, making any attacks that try to write in it impossible. But it is not always enabled because there is a significant slowdown during the startup of the binary which developers might want to avoid
An attacker would have to guess, or more often leak this value to bypass the check. See for more detailed exploit ideas.
This is a simple but very useful one. As explained further in , the cyclic
command allows you to generate de Bruijn sequences. These are very useful for finding the offset in a buffer overflow, as you can search the value that the instruction pointer is trying to jump to back in the original string to find the exact offset. You can generate a string:
For more useful syntax related to Return-Oriented Programming (ROP), see its