-
Notifications
You must be signed in to change notification settings - Fork 2
/
scan.py
75 lines (59 loc) · 2.12 KB
/
scan.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import os
import sys
from pprint import pprint
from machotools import rewriter_factory
from machotools.errors import MachoError
def main():
if len(sys.argv) < 2:
print("Usage: python scan.py <directory>")
exit()
files = list_files(sys.argv[1])
load_entries = {}
for f in files:
try:
entries = list_load_entries(f)
except:
continue
if len(entries['rpaths']) > 1 and len(entries['libraries']) > 0:
load_entries[f] = entries
possible_hijacks = {}
# These are technically different things, but this script doesn't distinguish.
# this may lead to FPs
path_replacements = ['@executable_path', '@loader_path']
for file, entry in load_entries.items():
exec_path = os.path.join(os.sep, *file.split(os.sep)[:-1])
vuln_libs = []
for lib in entry['libraries']:
first = True
for rpath in entry['rpaths']:
for replacement in path_replacements:
rpath = rpath.replace(replacement, '')
dylib_check = os.path.normpath(exec_path + rpath + lib)
if os.path.isfile(dylib_check):
if first: break
vuln_libs.append(lib)
first = False
if len(vuln_libs) > 0:
possible_hijacks[file] = vuln_libs
pprint(possible_hijacks)
def list_load_entries(filepath):
"""
Return load entry dict for the given filepath.
:param filepath: file to get rpaths from
"""
try:
rewriter = rewriter_factory(filepath)
dynamic_deps = [dep[6:] for dep in rewriter.dependencies if dep.startswith('@rpath')]
return {'rpaths': rewriter.rpaths, 'libraries': dynamic_deps}
except MachoError:
return {'rpaths': [], 'libraries': []}
def list_files(startpath):
"""
Yield list of all files (including path) for the specified directory recursively.
:param startpath: path to start in
"""
for root, _, files in os.walk(startpath):
for f in files:
yield os.path.join(root, f)
if __name__ == "__main__":
main()