37
37
)
38
38
39
39
lookup_parser .add_argument (
40
- '--download-libc' ,
40
+ '-d' , '- -download-libc' ,
41
41
action = 'store_true' ,
42
42
default = False ,
43
43
help = 'Attempt to download the matching libc.so'
44
44
)
45
45
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
-
53
46
lookup_parser .add_argument (
54
47
'--no-unstrip' ,
55
48
action = 'store_false' ,
56
49
dest = 'unstrip' ,
57
50
help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
58
51
)
59
52
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
+
60
61
hash_parser = libc_commands .add_parser (
61
62
'hash' ,
62
63
help = 'Display information of a libc version given an unique hash' ,
80
81
)
81
82
82
83
hash_parser .add_argument (
83
- '--download-libc' ,
84
+ '-d' , '- -download-libc' ,
84
85
action = 'store_true' ,
85
86
default = False ,
86
87
help = 'Attempt to download the matching libc.so'
87
88
)
88
89
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
-
96
90
hash_parser .add_argument (
97
91
'--no-unstrip' ,
98
92
action = 'store_false' ,
99
93
dest = 'unstrip' ,
100
94
help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
101
95
)
102
96
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
+
103
105
file_parser = libc_commands .add_parser (
104
106
'file' ,
105
107
help = 'Dump information about a libc binary' ,
130
132
file_parser .add_argument (
131
133
'--unstrip' ,
132
134
action = 'store_true' ,
133
- default = False ,
135
+ dest = 'unstrip' ,
134
136
help = 'Attempt to unstrip the libc binary inplace with debug symbols from a debuginfod server'
135
137
)
136
138
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
+ )
138
144
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
+ )
148
159
149
- return result . json ()
160
+ common_symbols = [ 'dup2' , 'printf' , 'puts' , 'read' , 'system' , 'write' ]
150
161
151
- def print_libc (libc ):
162
+ def print_libc_info (libc ):
152
163
log .info ('%s' , text .red (libc ['id' ]))
153
164
log .indented ('\t %-20s %s' , text .green ('BuildID:' ), libc ['buildid' ])
154
165
log .indented ('\t %-20s %s' , text .green ('MD5:' ), libc ['md5' ])
@@ -158,14 +169,39 @@ def print_libc(libc):
158
169
for symbol in libc ['symbols' ].items ():
159
170
log .indented ('\t %25s = %s' , symbol [0 ], symbol [1 ])
160
171
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
169
205
170
206
def translate_offset (offs , args , exe ):
171
207
if args .offset :
@@ -182,7 +218,7 @@ def collect_synthetic_symbols(exe):
182
218
available_symbols .append ('str_bin_sh' )
183
219
except StopIteration :
184
220
pass
185
-
221
+
186
222
libc_start_main_return = exe .libc_start_main_return
187
223
if libc_start_main_return > 0 :
188
224
exe .symbols ['__libc_start_main_ret' ] = libc_start_main_return
@@ -200,52 +236,56 @@ def main(args):
200
236
if len (pairs ) % 2 != 0 :
201
237
log .failure ('Uneven number of arguments. Please provide "symbol offset" pairs' )
202
238
return
203
-
239
+
204
240
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
+
206
243
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' ]))
208
249
209
250
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
+
210
254
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 ))
214
262
215
263
elif args .libc_command == 'file' :
216
- from hashlib import md5 , sha1 , sha256
217
264
for file in args .files :
218
265
if not os .path .exists (file ) or not os .path .isfile (file ):
219
266
log .failure ('File does not exist %s' , args .file )
220
267
continue
221
-
268
+
222
269
if args .unstrip :
223
270
libcdb .unstrip_libc (file )
224
271
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
+
249
289
250
290
if __name__ == '__main__' :
251
291
pwnlib .commandline .common .main (__file__ , main )
0 commit comments