5
5
from __future__ import division
6
6
7
7
import os
8
+ import time
8
9
import six
9
10
import tempfile
10
11
13
14
from pwnlib .log import getLogger
14
15
from pwnlib .tubes .process import process
15
16
from pwnlib .util .fiddling import enhex
17
+ from pwnlib .util .hashes import sha1filehex , sha256filehex , md5filehex
16
18
from pwnlib .util .misc import read
17
19
from pwnlib .util .misc import which
18
20
from pwnlib .util .misc import write
19
21
from pwnlib .util .web import wget
20
22
21
23
log = getLogger (__name__ )
22
24
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
+ }
24
31
DEBUGINFOD_SERVERS = [
25
32
'https://debuginfod.elfutils.org/' ,
26
33
]
29
36
urls = os .environ ['DEBUGINFOD_URLS' ].split (' ' )
30
37
DEBUGINFOD_SERVERS = urls + DEBUGINFOD_SERVERS
31
38
39
+ # Retry failed lookups after some time
40
+ NEGATIVE_CACHE_EXPIRY = 60 * 60 * 24 * 7 # 1 week
41
+
32
42
# https://gitlab.com/libcdb/libcdb wasn't updated after 2019,
33
43
# but still is a massive database of older libc binaries.
34
44
def provider_libcdb (hex_encoded_id , hash_type ):
@@ -100,7 +110,23 @@ def provider_libc_rip(hex_encoded_id, hash_type):
100
110
return None
101
111
return data
102
112
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 ]
104
130
105
131
def search_by_hash (hex_encoded_id , hash_type = 'build_id' , unstrip = True ):
106
132
assert hash_type in HASHES , hash_type
@@ -109,6 +135,10 @@ def search_by_hash(hex_encoded_id, hash_type='build_id', unstrip=True):
109
135
cache , cache_valid = _check_elf_cache ('libcdb' , hex_encoded_id , hash_type )
110
136
if cache_valid :
111
137
return cache
138
+
139
+ # We searched for this buildid before, but didn't find anything.
140
+ if cache is None :
141
+ return None
112
142
113
143
# Run through all available libc database providers to see if we have a match.
114
144
for provider in PROVIDERS :
@@ -141,6 +171,10 @@ def _search_debuginfo_by_hash(base_url, hex_encoded_id):
141
171
cache , cache_valid = _check_elf_cache ('libcdb_dbg' , hex_encoded_id , 'build_id' )
142
172
if cache_valid :
143
173
return cache
174
+
175
+ # We searched for this buildid before, but didn't find anything.
176
+ if cache is None :
177
+ return None
144
178
145
179
# Try to find separate debuginfo.
146
180
url = '/buildid/{}/debuginfo' .format (hex_encoded_id )
@@ -191,8 +225,11 @@ def _check_elf_cache(cache_type, hex_encoded_id, hash_type):
191
225
192
226
data = read (cache )
193
227
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
196
233
197
234
log .info_once ("Using cached data from %r" , cache )
198
235
return cache , True
0 commit comments