User's guide
This is the user's guide for formatstring, a python 3 library to help the exploitation of format string vulnerabilities.
The best way to install formatstring is to use pip:
pip install formatstring
On Archlinux, there is also a package on the AUR: python-formatstring-git
The first thing you will need to do when exploiting a format string is to get the offset of your buffer (and the padding if you need one).
Formatstring is here to help you with that. Using fmtstr_pattern_create
, you can create a pattern to guess this offset, given the maximum size of your buffer:
$ fmtstr_pattern_create 64
ABCDEFGH|%1$p|%2$p|%3$p|%4$p|%5$p|%6$p|%7$p|%8$p|%9$p|%10$p
You can specify the starting offset with -s
(default: 1).
Once you got the result, you can give it to fmtstr_pattern_offset
:
$ fmtstr_pattern_offset --arch x86_32
Enter the result of the format string on a pattern given by pattern_create:
ABCDEFGH|0x3ff|0xf77205a0|0x4|0x4|0x7|0x1b3220|0x43424120|0x47464544|0x31257c48|0x257c7024
Found buffer at offset 8 with a padding of 3 bytes
You can specify the architecture using --arch
. See fmtstr_pattern_offset -h
for more information.
You can generate payloads using the formatstring.payloads
module:
import sys
from formatstring import *
settings = PayloadSettings(offset=8, padding=3, arch=x86_32)
p = WritePayload()
p[0x8049790] = b'/bin/sh\x00'
p[0x80497a8] = struct.pack('@I', 0x01020304)
sys.stdout.buffer.write(p.generate(settings))
PayloadSettings is the object holding the settings of your format string vulnerability.
Attributes:
-
offset
(int): The offset of your buffer -
padding
(int): The padding, if you need one (default: 0) -
arch
(Architecture): The architecture (default: your computer architecture). See formatstring.architectures for more information. -
forbidden_bytes
(bytes): The bytes to avoid (default: empty) -
padding_byte
(bytes): The byte use for padding (default: None)
Example:
settings = PayloadSettings(offset=8, padding=3, arch=x86_64, forbidden_bytes=b'\x00\n')
ReadPayload generates a payload to read a null-terminated string (%s) at a given address.
Constructor:
-
address
(int): The address where you want to read
generate
method parameters:
-
settings
(PayloadSettings): The payload settings -
start_len
(int): The length of already printed data
Example:
p = ReadPayload(0xdeadbeef)
sys.stdout.buffer.write(b'42' + p.generate(settings, start_len=2))
WritePayload generates a payload to write bytes at given addresses.
Constructor: no parameter.
__setitem__
method parameters:
-
address
(int): The address where you want to write -
value
(bytes): The set of bytes you want to write
generate
method parameters:
-
settings
(PayloadSettings): The payload settings -
start_len
(int): The length of already printed data
Example:
p = WritePayload()
p[0x8049790] = b'/bin/sh\x00'
p[0x80497a8] = struct.pack('@I', 0x01020304) + struct.pack('@I', 0x05060708) # you can write a ROP chain!
sys.stdout.buffer.write(p.generate(settings))
Note: the generate
method can raise a ForbiddenByteException
exception if the library is unable to generate a payload without a forbidden byte.
The formatstring.architectures
module allows you to specify an architecture to PayloadSettings
.
Define an architecture.
Constructor parameters:
-
name
(str): The architecture name -
bits
(int): The number of bits (8, 16, 32, 64, ...) -
endian
(str): Specify the endianness ('little' or 'big')
Here is the set of already defined architectures: x86_32, x86_64, aarch64, alpha, avr, amd64, arm, cris, i386, ia64, m68k, mips, mips64, msp430, powerpc, powerpc64, s390, sparc, sparc64, thumb, vax
Return the architecture of your computer.
binary_arch
returns the architecture used by a binary file, given the path to this file
Example:
settings = PayloadSettings(offset=4, arch=binary_arch('/bin/ls'))
The formatstring.pattern
module allows you to generate patterns and guess the offset of your buffer.
You can use directly the scripts fmtstr_pattern_create
and fmtstr_pattern_offset
.
Generate a pattern to get the offset of your buffer
arguments:
-
buffer_size
(int): The maximum size of your buffer -
start_offset
(int): The starting offset (default: 1)
Compute the offset of your buffer given the result of the format string on the pattern generated by make_pattern
.
arguments:
-
buffer
(string): The result ofmake_pattern
-
start_offset
(int): The starting offset (default: 1) -
arch
(Architecture): The architecture of your system. (default: your computer architecture). See theformatstring.architectures
module.
returns:
- False if the offset is not found
- Otherwise, returns the couple (offset, padding)