From 78ce152b588f963d634b4df2e232780c30a425ee Mon Sep 17 00:00:00 2001 From: Arusekk Date: Sat, 1 Mar 2025 00:45:10 +0100 Subject: [PATCH] Add format string for no_dollars And also reduce payload lengths to minimum. Closes #2077 Closes #2130 Closes #2532 --- pwnlib/fmtstr.py | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/pwnlib/fmtstr.py b/pwnlib/fmtstr.py index 85e59d1d0..f383eecb7 100644 --- a/pwnlib/fmtstr.py +++ b/pwnlib/fmtstr.py @@ -104,6 +104,7 @@ def send_payload(payload): from pwnlib.memleak import MemLeak from pwnlib.util.cyclic import * from pwnlib.util.fiddling import randoms +from pwnlib.util.misc import align from pwnlib.util.packing import * log = getLogger(__name__) @@ -931,10 +932,11 @@ class FmtStr(object): """ - def __init__(self, execute_fmt, offset=None, padlen=0, numbwritten=0, badbytes=frozenset()): + def __init__(self, execute_fmt, offset=None, padlen=0, numbwritten=0, badbytes=frozenset(), no_dollars=False): self.execute_fmt = execute_fmt self.offset = offset self.padlen = padlen + self.no_dollars = no_dollars self.numbwritten = numbwritten self.badbytes = badbytes @@ -946,23 +948,29 @@ def __init__(self, execute_fmt, offset=None, padlen=0, numbwritten=0, badbytes=f self.leaker = MemLeak(self._leaker) def leak_stack(self, offset, prefix=b""): - payload = b"START%%%d$pEND" % offset + if self.no_dollars: + payload = b'%c' * (offset - 1) + b'START%pEND' + else: + payload = b"START%%%d$pEND" % offset + leak = self.execute_fmt(prefix + payload) try: leak = re.findall(br"START(.*?)END", leak, re.MULTILINE | re.DOTALL)[0] leak = int(leak, 16) except ValueError: leak = 0 + except IndexError: + log.error("Cannot leak anything: exec_fmt not returning formatted data") return leak def find_offset(self): - marker = cyclic(20) + marker = cyclic(context.bytes + 3) for off in range(1,1000): leak = self.leak_stack(off, marker) leak = pack(leak) pad = cyclic_find(leak[:4]) - if pad >= 0 and pad < 20: + if 0 <= pad < context.bytes: return off, pad else: log.error("Could not find offset to format string on stack") @@ -978,9 +986,25 @@ def _leaker(self, addr): if addr & 0xfff == 0 and self.leaker._leak(addr+1, 3, False) == b"ELF": return b"\x7f" + max_len = self.padlen + 8 + context.bytes + for _ in range(33): + offset = self.offset + max_len // context.bytes + if self.no_dollars: + payload = b'%c' * (offset - 1) + b'START%sEND' + else: + payload = b"START%%%d$sEND" % offset + if len(payload) > max_len: + max_len += align(len(payload) - max_len, context.bytes) + else: + break + else: + raise RuntimeError("this is a bug ... format string building did not converge") + fmtstr = fit({ - self.padlen: b"START%%%d$sEND" % (self.offset + 16//context.bytes), - 16 + self.padlen: addr + self.padlen: { + 0: payload, + max_len: addr + } }) leak = self.execute_fmt(fmtstr) @@ -1000,7 +1024,7 @@ def execute_writes(self): """ fmtstr = randoms(self.padlen).encode() - fmtstr += fmtstr_payload(self.offset, self.writes, numbwritten=self.padlen + self.numbwritten, badbytes=self.badbytes, write_size='byte') + fmtstr += fmtstr_payload(self.offset, self.writes, numbwritten=self.padlen + self.numbwritten, badbytes=self.badbytes, no_dollars=self.no_dollars, write_size='byte') self.execute_fmt(fmtstr) self.writes = {}