55from __future__ import division
66
77import os
8+ import time
89import six
910import tempfile
1011
1314from pwnlib .log import getLogger
1415from pwnlib .tubes .process import process
1516from pwnlib .util .fiddling import enhex
17+ from pwnlib .util .hashes import sha1filehex , sha256filehex , md5filehex
1618from pwnlib .util .misc import read
1719from pwnlib .util .misc import which
1820from pwnlib .util .misc import write
1921from pwnlib .util .web import wget
2022
2123log = getLogger (__name__ )
2224
23- HASHES = ['build_id' , 'sha1' , 'sha256' , 'md5' ]
25+ HASHES = {
26+ 'build_id' : lambda path : enhex (ELF (path , checksec = False ).buildid or b'' ),
27+ 'sha1' : sha1filehex ,
28+ 'sha256' : sha256filehex ,
29+ 'md5' : md5filehex ,
30+ }
2431DEBUGINFOD_SERVERS = [
2532 'https://debuginfod.elfutils.org/' ,
2633]
2936 urls = os .environ ['DEBUGINFOD_URLS' ].split (' ' )
3037 DEBUGINFOD_SERVERS = urls + DEBUGINFOD_SERVERS
3138
39+ # Retry failed lookups after some time
40+ NEGATIVE_CACHE_EXPIRY = 60 * 60 * 24 * 7 # 1 week
41+
3242# https://gitlab.com/libcdb/libcdb wasn't updated after 2019,
3343# but still is a massive database of older libc binaries.
3444def provider_libcdb (hex_encoded_id , hash_type ):
@@ -100,7 +110,23 @@ def provider_libc_rip(hex_encoded_id, hash_type):
100110 return None
101111 return data
102112
103- PROVIDERS = [provider_libcdb , provider_libc_rip ]
113+ # Check if the local system libc matches the requested hash.
114+ def provider_local_system (hex_encoded_id , hash_type ):
115+ if hash_type == 'id' :
116+ return None
117+ shell_path = os .environ .get ('SHELL' , None ) or '/bin/sh'
118+ if not os .path .exists (shell_path ):
119+ log .debug ('Shell path %r does not exist. Skipping local system libc matching.' , shell_path )
120+ return None
121+ local_libc = ELF (shell_path , checksec = False ).libc
122+ if not local_libc :
123+ log .debug ('Cannot lookup libc from shell %r. Skipping local system libc matching.' , shell_path )
124+ return None
125+ if HASHES [hash_type ](local_libc .path ) == hex_encoded_id :
126+ return local_libc .data
127+ return None
128+
129+ PROVIDERS = [provider_local_system , provider_libcdb , provider_libc_rip ]
104130
105131def search_by_hash (hex_encoded_id , hash_type = 'build_id' , unstrip = True ):
106132 assert hash_type in HASHES , hash_type
@@ -109,6 +135,10 @@ def search_by_hash(hex_encoded_id, hash_type='build_id', unstrip=True):
109135 cache , cache_valid = _check_elf_cache ('libcdb' , hex_encoded_id , hash_type )
110136 if cache_valid :
111137 return cache
138+
139+ # We searched for this buildid before, but didn't find anything.
140+ if cache is None :
141+ return None
112142
113143 # Run through all available libc database providers to see if we have a match.
114144 for provider in PROVIDERS :
@@ -141,6 +171,10 @@ def _search_debuginfo_by_hash(base_url, hex_encoded_id):
141171 cache , cache_valid = _check_elf_cache ('libcdb_dbg' , hex_encoded_id , 'build_id' )
142172 if cache_valid :
143173 return cache
174+
175+ # We searched for this buildid before, but didn't find anything.
176+ if cache is None :
177+ return None
144178
145179 # Try to find separate debuginfo.
146180 url = '/buildid/{}/debuginfo' .format (hex_encoded_id )
@@ -191,8 +225,11 @@ def _check_elf_cache(cache_type, hex_encoded_id, hash_type):
191225
192226 data = read (cache )
193227 if not data .startswith (b'\x7F ELF' ):
194- log .info_once ("Skipping unavailable ELF %s" , hex_encoded_id )
195- return cache , False
228+ # Retry failed lookups after some time
229+ if time .time () > os .path .getmtime (cache ) + NEGATIVE_CACHE_EXPIRY :
230+ return cache , False
231+ log .info_once ("Skipping invalid cached ELF %s" , hex_encoded_id )
232+ return None , False
196233
197234 log .info_once ("Using cached data from %r" , cache )
198235 return cache , True
0 commit comments