from autorop import PwnState, Pipe
from pwn import log, ELF, re, subprocess
from typing import List
LIBC_NAME_REGEX = re.compile(r"^.* \((.*)\)$")
[docs]class Database(Pipe):
[docs] def __init__(self) -> None:
"""Acquire libc version using local installation of `libc-database <https://github.com/niklasb/libc-database>`_
We can programmatically find libc based on function address leaks
(two or more preferred). This pipe will set ``state.libc``, including setting
``state.libc.address`` for ready-to-use address calculation.
"""
super().__init__(())
[docs] def __call__(self, state: PwnState) -> PwnState:
"""Acquire libc version using local installation of `libc-database <https://github.com/niklasb/libc-database>`_
Arguments:
state: The current ``PwnState`` with the following set
- ``leaks``: Leaked symbols of libc.
- ``libc_database_path``: Path to libc-database installation.
Returns:
Mutated ``PwnState``, with the following updated
- ``libc``: Path to ``target``'s libc.
- ``libc_base``: Base address of ``libc``.
"""
assert state.leaks is not None
assert state.libc_database_path is not None
flattened_args: List[str] = []
for symbol, address in state.leaks.items():
flattened_args.append(symbol)
flattened_args.append(hex(address))
log.info("Searching for libc based on leaks")
command = [state.libc_database_path + "/find"] + flattened_args
results = (
subprocess.run(command, check=True, stdout=subprocess.PIPE)
.stdout.decode("utf-8")
.splitlines()
)
log.debug(repr(results))
if len(results) == 0:
log.error("could not find any matching libc!")
if len(results) > 1:
log.warning(f"{len(results)} matching libc's found, picking first one")
# parse the output
libc_name = LIBC_NAME_REGEX.fullmatch(results[0]).group(1)
path_to_libc = f"{state.libc_database_path}/db/{libc_name}.so"
libc = ELF(path_to_libc)
# pick first leak and use that to calculate base
some_symbol, its_address = next(iter(state.leaks.items()))
libc.address = its_address - libc.symbols[some_symbol]
state.libc = path_to_libc
state.libc_base = libc.address
# sanity check
for symbol, address in state.leaks.items():
assert state.libc is not None
diff = address - libc.symbols[symbol]
if diff != 0:
log.warning(
f"symbol {symbol} has delta with actual libc of {hex(diff)}"
)
return state