• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Gallopsled / pwntools / 11532588290

26 Oct 2024 02:29PM UTC coverage: 73.585% (+2.1%) from 71.491%
11532588290

push

github

peace-maker
Merge branch 'dev' into interactive

3767 of 6364 branches covered (59.19%)

1463 of 2234 new or added lines in 69 files covered. (65.49%)

85 existing lines in 22 files now uncovered.

13246 of 18001 relevant lines covered (73.58%)

0.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.29
/pwnlib/commandline/libcdb.py
1
#!/usr/bin/env python
2
from __future__ import absolute_import
1✔
3
from __future__ import division
1✔
4
from __future__ import print_function
1✔
5

6
import re
1✔
7
import shutil
1✔
8
import sys
1✔
9

10
import pwnlib.args
1✔
11
pwnlib.args.free_form = False
1✔
12

13
from pwn import *
1✔
14
from pwnlib.commandline import common
1✔
15

16
parser = common.parser_commands.add_parser(
1✔
17
    'libcdb',
18
    help = 'Print various information about a libc binary',
19
    description = 'Print various information about a libc binary'
20
)
21

22
libc_commands = parser.add_subparsers(
1✔
23
    dest = 'libc_command'
24
)
25

26
lookup_parser = libc_commands.add_parser(
1✔
27
    'lookup',
28
    help = 'Lookup a libc version by function offsets',
29
    description = 'Lookup a libc version by function offsets'
30
)
31

32
lookup_parser.add_argument(
1✔
33
    'symbol_offset_pairs',
34
    metavar = 'symbol_offset_pairs',
35
    nargs = '+',
36
    help = 'Symbol and offset pairs to lookup matching libc version. Can be any number of pairs to narrow the search. Example: "read 3e0 write 520"'
37
)
38

39
lookup_parser.add_argument(
1✔
40
    '-d', '--download-libc',
41
    action = 'store_true',
42
    default = False,
43
    help = 'Attempt to download the matching libc.so'
44
)
45

46
lookup_parser.add_argument(
1✔
47
    '--no-unstrip',
48
    action = 'store_false',
49
    dest = 'unstrip',
50
    help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
51
)
52

53
lookup_parser.add_argument(
1✔
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

61
hash_parser = libc_commands.add_parser(
1✔
62
    'hash',
63
    help = 'Display information of a libc version given an unique hash',
64
    description = 'Display information of a libc version given an unique hash'
65
)
66

67
hash_parser.add_argument(
1✔
68
    'hash_value',
69
    metavar = 'hash_value',
70
    nargs = '+',
71
    help = 'Hex encoded hash value'
72
)
73

74
hash_parser.add_argument(
1✔
75
    '-t', '--hash_type',
76
    nargs = '?',
77
    type = str,
78
    choices = ['id', 'buildid', 'md5', 'sha1', 'sha256'],
79
    default = 'buildid',
80
    help = 'The type of the provided hash value. Supported hashtypes: id, buildid, md5, sha1, sha256'
81
)
82

83
hash_parser.add_argument(
1✔
84
    '-d', '--download-libc',
85
    action = 'store_true',
86
    default = False,
87
    help = 'Attempt to download the matching libc.so'
88
)
89

90
hash_parser.add_argument(
1✔
91
    '--no-unstrip',
92
    action = 'store_false',
93
    dest = 'unstrip',
94
    help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
95
)
96

97
hash_parser.add_argument(
1✔
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

105
file_parser = libc_commands.add_parser(
1✔
106
    'file',
107
    help = 'Dump information about a libc binary',
108
    description = 'Dump information about a libc binary'
109
)
110

111
file_parser.add_argument(
1✔
112
    'files',
113
    metavar = 'files',
114
    nargs = '+',
115
    help = 'Libc binary to dump'
116
)
117

118
file_parser.add_argument(
1✔
119
    '-s', '--symbols',
120
    metavar = 'symbols',
121
    nargs = '*',
122
    help = 'List of symbol offsets to dump in addition to the common ones'
123
)
124

125
file_parser.add_argument(
1✔
126
    '-o', '--offset',
127
    metavar = 'offset',
128
    type = str,
129
    help = 'Display all offsets relative to this symbol'
130
)
131

132
file_parser.add_argument(
1✔
133
    '--unstrip',
134
    action = 'store_true',
135
    dest = 'unstrip',
136
    help = 'Attempt to unstrip the libc binary inplace with debug symbols from a debuginfod server'
137
)
138

139
fetch_parser = libc_commands.add_parser(
1✔
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
)
144

145
fetch_parser.add_argument(
1✔
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(
1✔
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
)
159

160
common_symbols = ['dup2', 'printf', 'puts', 'read', 'system', 'write']
1✔
161

162
def print_libc_info(libc):
1✔
163
    log.info('%s', text.red(libc['id']))
1✔
164
    log.indented('\t%-20s %s', text.green('BuildID:'), libc['buildid'])
1✔
165
    log.indented('\t%-20s %s', text.green('MD5:'), libc['md5'])
1✔
166
    log.indented('\t%-20s %s', text.green('SHA1:'), libc['sha1'])
1✔
167
    log.indented('\t%-20s %s', text.green('SHA256:'), libc['sha256'])
1✔
168
    log.indented('\t%s', text.green('Symbols:'))
1✔
169
    for symbol in libc['symbols'].items():
1✔
170
        log.indented('\t%25s = %s', symbol[0], symbol[1])
1✔
171

172
def print_libc_elf(exe):
1✔
173
    from hashlib import md5, sha1, sha256
1✔
174

175
    log.info('%s', text.red(os.path.basename(exe.path)))
1✔
176

177
    libc_version = get_libc_version(exe)
1✔
178
    if libc_version:
1!
179
        log.indented('%-20s %s', text.green('Version:'), libc_version)
1✔
180

181
    if exe.buildid:
1!
182
        log.indented('%-20s %s', text.green('BuildID:'), enhex(exe.buildid))
1✔
183

184
    log.indented('%-20s %s', text.green('MD5:'), md5(exe.data).hexdigest())
1✔
185
    log.indented('%-20s %s', text.green('SHA1:'), sha1(exe.data).hexdigest())
1✔
186
    log.indented('%-20s %s', text.green('SHA256:'), sha256(exe.data).hexdigest())
1✔
187

188
    # Always dump the basic list of common symbols
189
    log.indented('%s', text.green('Symbols:'))
1✔
190
    synthetic_symbols = collect_synthetic_symbols(exe)
1✔
191

192
    symbols = common_symbols + (args.symbols or []) + synthetic_symbols
1✔
193
    symbols.sort()
1✔
194
    for symbol in symbols:
1✔
195
        if symbol not in exe.symbols:
1!
NEW
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))
1✔
199

200
def get_libc_version(exe):
1✔
201
    res = re.search(br'libc[ -](\d+\.\d+)', exe.data)
1✔
202
    if res:
1!
203
        return res.group(1).decode()
1✔
NEW
204
    return None
×
205

206
def translate_offset(offs, args, exe):
1✔
207
    if args.offset:
1!
208
        if args.offset not in exe.symbols:
×
209
            log.info_once('offset symbol %s not found. ignoring.', args.offset)
×
210
            return offs
×
211
        return offs - exe.symbols[args.offset]
×
212
    return offs
1✔
213

214
def collect_synthetic_symbols(exe):
1✔
215
    available_symbols = []
1✔
216
    try:
1✔
217
        exe.symbols['str_bin_sh'] = next(exe.search(b'/bin/sh\x00'))
1✔
218
        available_symbols.append('str_bin_sh')
1✔
NEW
219
    except StopIteration:
×
NEW
220
        pass
×
221

222
    libc_start_main_return = exe.libc_start_main_return
1✔
223
    if libc_start_main_return > 0:
1!
224
        exe.symbols['__libc_start_main_ret'] = libc_start_main_return
1✔
225
        available_symbols.append('__libc_start_main_ret')
1✔
226

227
    return available_symbols
1✔
228

229
def main(args):
1✔
230
    if len(sys.argv) < 3:
1!
231
        parser.print_usage()
×
232
        sys.exit()
×
233

234
    if args.libc_command == 'lookup':
1✔
235
        pairs = args.symbol_offset_pairs
1✔
236
        if len(pairs) % 2 != 0:
1!
237
            log.failure('Uneven number of arguments. Please provide "symbol offset" pairs')
×
238
            return
×
239

240
        symbols = {pairs[i]:pairs[i+1] for i in range(0, len(pairs), 2)}
1✔
241
        matched_libcs = libcdb.search_by_symbol_offsets(symbols, offline_only=args.offline_only, return_raw=True)
1✔
242

243
        for libc in matched_libcs:
1✔
244
            print_libc_info(libc)
1✔
245
            if args.download_libc:
1!
NEW
246
                path = libcdb.search_by_build_id(libc['buildid'], args.unstrip)
×
NEW
247
                if path:
×
NEW
248
                    shutil.copy(path, './{}.so'.format(libc['id']))
×
249

250
    elif args.libc_command == 'hash':
1✔
251
        inverted_map = {v: k for k, v in libcdb.MAP_TYPES.items()}
1✔
252
        hash_type = inverted_map.get(args.hash_type, args.hash_type)
1✔
253

254
        for hash_value in args.hash_value:
1✔
255
            path = libcdb.search_by_hash(hash_value, hash_type, unstrip=args.unstrip, offline_only=args.offline_only)
1✔
256
            exe = ELF(path, checksec=False)
1✔
257
            print_libc_elf(exe)
1✔
258

259
            if args.download_libc:
1!
260
                # if we cannot get actual libc version then copy with cache name
NEW
261
                shutil.copy(path, './libc-{}.so'.format(get_libc_version(exe) or Path(path).stem))
×
262

263
    elif args.libc_command == 'file':
1!
264
        for file in args.files:
1✔
265
            if not os.path.exists(file) or not os.path.isfile(file):
1!
266
                log.failure('File does not exist %s', args.file)
×
267
                continue
×
268

269
            if args.unstrip:
1!
270
                libcdb.unstrip_libc(file)
×
271

272
            print_libc_elf(ELF(file, checksec=False))
1✔
273

NEW
274
    elif args.libc_command == 'fetch':
×
275

NEW
276
        if args.update:
×
NEW
277
            subprocess.check_call(['./get'] + args.update, cwd=args.path)
×
278

279
        else:
NEW
280
            if not Path(args.path).exists():
×
NEW
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."):
NEW
283
                    log.waitfor("init libc-database repository")
×
NEW
284
                    subprocess.check_call(['git', 'clone', 'https://github.com/niklasb/libc-database/', args.path])
×
285
            else:
NEW
286
                log.waitfor("upgrade libc-database repository")
×
NEW
287
                subprocess.check_call(['git', 'pull'], cwd=args.path)
×
288

289

290
if __name__ == '__main__':
1✔
291
    pwnlib.commandline.common.main(__file__, main)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc