3737)
3838
3939lookup_parser .add_argument (
40- '--download-libc' ,
40+ '-d' , '- -download-libc' ,
4141 action = 'store_true' ,
4242 default = False ,
4343 help = 'Attempt to download the matching libc.so'
4444)
4545
46- lookup_parser .add_argument (
47- '--unstrip' ,
48- action = 'store_true' ,
49- default = True ,
50- help = 'Attempt to unstrip the libc binary with debug symbols from a debuginfod server'
51- )
52-
5346lookup_parser .add_argument (
5447 '--no-unstrip' ,
5548 action = 'store_false' ,
5649 dest = 'unstrip' ,
5750 help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
5851)
5952
53+ lookup_parser .add_argument (
54+ '--offline-only' ,
55+ action = 'store_true' ,
56+ default = False ,
57+ dest = 'offline_only' ,
58+ help = 'Attempt to searching with offline only mode'
59+ )
60+
6061hash_parser = libc_commands .add_parser (
6162 'hash' ,
6263 help = 'Display information of a libc version given an unique hash' ,
8081)
8182
8283hash_parser .add_argument (
83- '--download-libc' ,
84+ '-d' , '- -download-libc' ,
8485 action = 'store_true' ,
8586 default = False ,
8687 help = 'Attempt to download the matching libc.so'
8788)
8889
89- hash_parser .add_argument (
90- '--unstrip' ,
91- action = 'store_true' ,
92- default = True ,
93- help = 'Attempt to unstrip the libc binary with debug symbols from a debuginfod server'
94- )
95-
9690hash_parser .add_argument (
9791 '--no-unstrip' ,
9892 action = 'store_false' ,
9993 dest = 'unstrip' ,
10094 help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
10195)
10296
97+ hash_parser .add_argument (
98+ '--offline-only' ,
99+ action = 'store_true' ,
100+ default = False ,
101+ dest = 'offline_only' ,
102+ help = 'Attempt to searching with offline only mode'
103+ )
104+
103105file_parser = libc_commands .add_parser (
104106 'file' ,
105107 help = 'Dump information about a libc binary' ,
130132file_parser .add_argument (
131133 '--unstrip' ,
132134 action = 'store_true' ,
133- default = False ,
135+ dest = 'unstrip' ,
134136 help = 'Attempt to unstrip the libc binary inplace with debug symbols from a debuginfod server'
135137)
136138
137- common_symbols = ['dup2' , 'printf' , 'puts' , 'read' , 'system' , 'write' ]
139+ fetch_parser = libc_commands .add_parser (
140+ 'fetch' ,
141+ help = 'Fetch libc database' ,
142+ description = 'Fetch libc database. If no argument passed, it will init and upgrade libc-database repository' ,
143+ )
138144
139- def find_libc (params ):
140- import requests
141- url = "https://libc.rip/api/find"
142- result = requests .post (url , json = params , timeout = 20 )
143- log .debug ('Request: %s' , params )
144- log .debug ('Result: %s' , result .json ())
145- if result .status_code != 200 or len (result .json ()) == 0 :
146- log .failure ("Could not find libc for %s on libc.rip" , params )
147- return []
145+ fetch_parser .add_argument (
146+ 'path' ,
147+ nargs = '?' ,
148+ default = context .local_libcdb ,
149+ help = 'Set libc-database path, If it is empty, the default path will be `context.local_libcdb` (%s)' % context .local_libcdb
150+ )
151+
152+ fetch_parser .add_argument (
153+ '-u' , '--update' ,
154+ metavar = 'update' ,
155+ nargs = '+' ,
156+ choices = ['all' , 'ubuntu' , 'debian' , 'rpm' , 'centos' , 'arch' , 'alpine' , 'kali' , 'parrotsec' , 'launchpad' ],
157+ help = 'Fetch the desired libc categories'
158+ )
148159
149- return result . json ()
160+ common_symbols = [ 'dup2' , 'printf' , 'puts' , 'read' , 'system' , 'write' ]
150161
151- def print_libc (libc ):
162+ def print_libc_info (libc ):
152163 log .info ('%s' , text .red (libc ['id' ]))
153164 log .indented ('\t %-20s %s' , text .green ('BuildID:' ), libc ['buildid' ])
154165 log .indented ('\t %-20s %s' , text .green ('MD5:' ), libc ['md5' ])
@@ -158,14 +169,39 @@ def print_libc(libc):
158169 for symbol in libc ['symbols' ].items ():
159170 log .indented ('\t %25s = %s' , symbol [0 ], symbol [1 ])
160171
161- def handle_remote_libc (args , libc ):
162- print_libc (libc )
163- if args .download_libc :
164- path = libcdb .search_by_build_id (libc ['buildid' ], args .unstrip )
165- if path :
166- if args .unstrip :
167- libcdb .unstrip_libc (path )
168- shutil .copy (path , './{}.so' .format (libc ['id' ]))
172+ def print_libc_elf (exe ):
173+ from hashlib import md5 , sha1 , sha256
174+
175+ log .info ('%s' , text .red (os .path .basename (exe .path )))
176+
177+ libc_version = get_libc_version (exe )
178+ if libc_version :
179+ log .indented ('%-20s %s' , text .green ('Version:' ), libc_version )
180+
181+ if exe .buildid :
182+ log .indented ('%-20s %s' , text .green ('BuildID:' ), enhex (exe .buildid ))
183+
184+ log .indented ('%-20s %s' , text .green ('MD5:' ), md5 (exe .data ).hexdigest ())
185+ log .indented ('%-20s %s' , text .green ('SHA1:' ), sha1 (exe .data ).hexdigest ())
186+ log .indented ('%-20s %s' , text .green ('SHA256:' ), sha256 (exe .data ).hexdigest ())
187+
188+ # Always dump the basic list of common symbols
189+ log .indented ('%s' , text .green ('Symbols:' ))
190+ synthetic_symbols = collect_synthetic_symbols (exe )
191+
192+ symbols = common_symbols + (args .symbols or []) + synthetic_symbols
193+ symbols .sort ()
194+ for symbol in symbols :
195+ if symbol not in exe .symbols :
196+ log .indented ('%25s = %s' , symbol , text .red ('not found' ))
197+ else :
198+ log .indented ('%25s = %#x' , symbol , translate_offset (exe .symbols [symbol ], args , exe ))
199+
200+ def get_libc_version (exe ):
201+ res = re .search (br'libc[ -](\d+\.\d+)' , exe .data )
202+ if res :
203+ return res .group (1 ).decode ()
204+ return None
169205
170206def translate_offset (offs , args , exe ):
171207 if args .offset :
@@ -182,7 +218,7 @@ def collect_synthetic_symbols(exe):
182218 available_symbols .append ('str_bin_sh' )
183219 except StopIteration :
184220 pass
185-
221+
186222 libc_start_main_return = exe .libc_start_main_return
187223 if libc_start_main_return > 0 :
188224 exe .symbols ['__libc_start_main_ret' ] = libc_start_main_return
@@ -200,52 +236,56 @@ def main(args):
200236 if len (pairs ) % 2 != 0 :
201237 log .failure ('Uneven number of arguments. Please provide "symbol offset" pairs' )
202238 return
203-
239+
204240 symbols = {pairs [i ]:pairs [i + 1 ] for i in range (0 , len (pairs ), 2 )}
205- matched_libcs = find_libc ({'symbols' : symbols })
241+ matched_libcs = libcdb .search_by_symbol_offsets (symbols , offline_only = args .offline_only , return_raw = True )
242+
206243 for libc in matched_libcs :
207- handle_remote_libc (args , libc )
244+ print_libc_info (libc )
245+ if args .download_libc :
246+ path = libcdb .search_by_build_id (libc ['buildid' ], args .unstrip )
247+ if path :
248+ shutil .copy (path , './{}.so' .format (libc ['id' ]))
208249
209250 elif args .libc_command == 'hash' :
251+ inverted_map = {v : k for k , v in libcdb .MAP_TYPES .items ()}
252+ hash_type = inverted_map .get (args .hash_type , args .hash_type )
253+
210254 for hash_value in args .hash_value :
211- matched_libcs = find_libc ({args .hash_type : hash_value })
212- for libc in matched_libcs :
213- handle_remote_libc (args , libc )
255+ path = libcdb .search_by_hash (hash_value , hash_type , unstrip = args .unstrip , offline_only = args .offline_only )
256+ exe = ELF (path , checksec = False )
257+ print_libc_elf (exe )
258+
259+ if args .download_libc :
260+ # if we cannot get actual libc version then copy with cache name
261+ shutil .copy (path , './libc-{}.so' .format (get_libc_version (exe ) or Path (path ).stem ))
214262
215263 elif args .libc_command == 'file' :
216- from hashlib import md5 , sha1 , sha256
217264 for file in args .files :
218265 if not os .path .exists (file ) or not os .path .isfile (file ):
219266 log .failure ('File does not exist %s' , args .file )
220267 continue
221-
268+
222269 if args .unstrip :
223270 libcdb .unstrip_libc (file )
224271
225- exe = ELF (file , checksec = False )
226- log .info ('%s' , text .red (os .path .basename (file )))
227-
228- libc_version = re .search (br'libc[ -](\d+\.\d+)' , exe .data )
229- if libc_version :
230- log .indented ('%-20s %s' , text .green ('Version:' ), libc_version .group (1 ).decode ())
231-
232- if exe .buildid :
233- log .indented ('%-20s %s' , text .green ('BuildID:' ), enhex (exe .buildid ))
234- log .indented ('%-20s %s' , text .green ('MD5:' ), md5 (exe .data ).hexdigest ())
235- log .indented ('%-20s %s' , text .green ('SHA1:' ), sha1 (exe .data ).hexdigest ())
236- log .indented ('%-20s %s' , text .green ('SHA256:' ), sha256 (exe .data ).hexdigest ())
237-
238- # Always dump the basic list of common symbols
239- log .indented ('%s' , text .green ('Symbols:' ))
240- synthetic_symbols = collect_synthetic_symbols (exe )
241-
242- symbols = common_symbols + (args .symbols or []) + synthetic_symbols
243- symbols .sort ()
244- for symbol in symbols :
245- if symbol not in exe .symbols :
246- log .indented ('%25s = %s' , symbol , text .red ('not found' ))
247- else :
248- log .indented ('%25s = %#x' , symbol , translate_offset (exe .symbols [symbol ], args , exe ))
272+ print_libc_elf (ELF (file , checksec = False ))
273+
274+ elif args .libc_command == 'fetch' :
275+
276+ if args .update :
277+ subprocess .check_call (['./get' ] + args .update , cwd = args .path )
278+
279+ else :
280+ if not Path (args .path ).exists ():
281+ if yesno ("Would you like to initialize the libc-database repository? "
282+ "If the path already exists, this prompt will not display, and automatically upgrade repository." ):
283+ log .waitfor ("init libc-database repository" )
284+ subprocess .check_call (['git' , 'clone' , 'https://github.com/niklasb/libc-database/' , args .path ])
285+ else :
286+ log .waitfor ("upgrade libc-database repository" )
287+ subprocess .check_call (['git' , 'pull' ], cwd = args .path )
288+
249289
250290if __name__ == '__main__' :
251291 pwnlib .commandline .common .main (__file__ , main )
0 commit comments