Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow libcdb search offline database #2259

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pwnlib/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ def STDERR(v):
"""Sends logging to ``stderr`` by default, instead of ``stdout``"""
context.log_console = sys.stderr

def LOCAL_LIBCDB(v):
"""Sets local libcdb path"""
context.defaults['local_libcdb'] = v

hooks = {
'LOG_LEVEL': LOG_LEVEL,
'LOG_FILE': LOG_FILE,
Expand All @@ -170,6 +174,7 @@ def STDERR(v):
'NOASLR': NOASLR,
'NOPTRACE': NOPTRACE,
'STDERR': STDERR,
'LOCAL_LIBCDB': LOCAL_LIBCDB,
}

def initialize():
Expand Down
108 changes: 94 additions & 14 deletions pwnlib/commandline/libcdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@
help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
)

lookup_parser.add_argument(
'--no-offline',
action = 'store_true',
default = False,
help = 'Disable offline libcdb search mode'
)

lookup_parser.add_argument(
'--no-online',
action = 'store_true',
default = False,
help = 'Disable online libcdb search mode'
)

hash_parser = libc_commands.add_parser(
'hash',
help = 'Display information of a libc version given an unique hash',
Expand Down Expand Up @@ -100,6 +114,20 @@
help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
)

hash_parser.add_argument(
'--no-offline',
action = 'store_true',
default = False,
help = 'Disable offline libcdb search mode'
)

hash_parser.add_argument(
'--no-online',
action = 'store_true',
default = False,
help = 'Disable online libcdb search mode'
)

file_parser = libc_commands.add_parser(
'file',
help = 'Dump information about a libc binary',
Expand Down Expand Up @@ -136,18 +164,71 @@

common_symbols = ['dup2', 'printf', 'puts', 'read', 'system', 'write']

def find_libc(params):

def find_in_online_mode(params):
import requests
url = "https://libc.rip/api/find"
url = "https://libc.rip/api/find"
result = requests.post(url, json=params, timeout=20)
log.debug('Request: %s', params)
log.debug('Result: %s', result.json())
if result.status_code != 200 or len(result.json()) == 0:
log.failure("Could not find libc for %s on libc.rip", params)
return []

return result.json()


def find_in_offline_mode(params):
# lookup parser
if params.get("symbols"):
matching_libcs = libcdb.find_local_libc(params)
return matching_libcs if matching_libcs else []

# hash parser
hash_type, hash_value = list(params.items())[0]

local_db = libcdb._fetch_local_database_path()
if not local_db:
log.warn_once("The environment variable `PWNLIB_LOCAL_LIBCDB` or `context.local_libcdb` is not configured.")
return []

db_path = Path(local_db) / "db"
libs_id = None

if hash_type == "id":
libs_id = hash_value
else:
if hash_type == "buildid":
hash_type = "build_id"

libc_path = libcdb.search_by_hash(hash_value, hash_type, unstrip=False, offline=True)
if libc_path:
libs_id = read(libc_path + ".id").decode()

if libs_id:
libc_path = db_path / ("%s.so" % libs_id)
symbol_path = db_path / ("%s.symbols" % libs_id)

syms = libcdb.get_libc_symbols(symbol_path)
return [libcdb.get_libc_info(db_path, libc_path.stem, syms)]

return []


def find_libc(params, offline=True, online=True):
offline_matching = find_in_offline_mode(params) if offline else []
online_matching = find_in_online_mode(params) if online else []
log.debug("Offline result: %s, Online result: %s", offline_matching, online_matching)

matching_id = []
matching_libcs = []
for x in offline_matching + online_matching:
if x["id"] not in matching_id:
matching_id.append(x["id"])
matching_libcs.append(x)

return matching_libcs


def print_libc(libc):
log.info('%s', text.red(libc['id']))
log.indented('\t%-20s %s', text.green('BuildID:'), libc['buildid'])
Expand All @@ -158,13 +239,10 @@ def print_libc(libc):
for symbol in libc['symbols'].items():
log.indented('\t%25s = %s', symbol[0], symbol[1])

def handle_remote_libc(args, libc):
print_libc(libc)
def fetch_libc(args, libc):
if args.download_libc:
path = libcdb.search_by_build_id(libc['buildid'], args.unstrip)
path = libcdb.search_by_build_id(libc['buildid'], args.unstrip, not args.no_offline, not args.no_online)
if path:
if args.unstrip:
libcdb.unstrip_libc(path)
shutil.copy(path, './{}.so'.format(libc['id']))

def translate_offset(offs, args, exe):
Expand Down Expand Up @@ -196,25 +274,27 @@ def main(args):
if len(pairs) % 2 != 0:
log.failure('Uneven number of arguments. Please provide "symbol offset" pairs')
return

symbols = {pairs[i]:pairs[i+1] for i in range(0, len(pairs), 2)}
matched_libcs = find_libc({'symbols': symbols})
matched_libcs = find_libc({'symbols': symbols}, not args.no_offline, not args.no_online)
for libc in matched_libcs:
handle_remote_libc(args, libc)
print_libc(libc)
fetch_libc(args, libc)

elif args.libc_command == 'hash':
for hash_value in args.hash_value:
matched_libcs = find_libc({args.hash_type: hash_value})
matched_libcs = find_libc({args.hash_type: hash_value}, args.offline)
for libc in matched_libcs:
handle_remote_libc(args, libc)
print_libc(libc)
fetch_libc(args, libc)

elif args.libc_command == 'file':
from hashlib import md5, sha1, sha256
for file in args.files:
if not os.path.exists(file) or not os.path.isfile(file):
log.failure('File does not exist %s', args.file)
continue

if args.unstrip:
libcdb.unstrip_libc(file)

Expand Down
5 changes: 5 additions & 0 deletions pwnlib/context/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ class ContextType(object):
'endian': 'little',
'gdbinit': "",
'kernel': None,
'local_libcdb': "",
'log_level': logging.INFO,
'log_file': _devnull(),
'log_console': sys.stdout,
Expand Down Expand Up @@ -1070,6 +1071,10 @@ def log_console(self, stream):
stream = open(stream, 'wt')
return stream

@_validator
def local_libcdb(self, path):
return path

@property
def mask(self):
return (1 << self.bits) - 1
Expand Down
Loading