-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathprocscan.py
executable file
·148 lines (129 loc) · 6.68 KB
/
procscan.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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/env python3
from procmon_parser import ProcmonLogsReader
import logging
import argparse
def is_authority(username):
if username == "NT AUTHORITY\\LOCAL SERVICE" \
or username == "NT AUTHORITY\\NETWORK SERVICE" \
or username == "NT AUTHORITY\\SYSTEM":
return True
return False
def print_banner():
print("""
___ ___ ___ ___ ___ ___ ___ ___
/\ \ /\ \ /\ \ /\ \ /\ \ /\ \ /\ \ /\__\
/::\ \ /::\ \ /::\ \ /::\ \ /::\ \ /::\ \ /::\ \ /::| |
/:/\:\ \ /:/\:\ \ /:/\:\ \ /:/\:\ \ /:/\ \ \ /:/\:\ \ /:/\:\ \ /:|:| |
/::\~\:\ \ /::\~\:\ \ /:/ \:\ \ /:/ \:\ \ _\:\~\ \ \ /:/ \:\ \ /::\~\:\ \ /:/|:| |__
/:/\:\ \:\__\ /:/\:\ \:\__\ /:/__/ \:\__\ /:/__/ \:\__\ /\ \:\ \ \__\ /:/__/ \:\__\ /:/\:\ \:\__\ /:/ |:| /\__\\
\/__\:\/:/ / \/_|::\/:/ / \:\ \ /:/ / \:\ \ \/__/ \:\ \:\ \/__/ \:\ \ \/__/ \/__\:\/:/ / \/__|:|/:/ /
\::/ / |:|::/ / \:\ /:/ / \:\ \ \:\ \:\__\ \:\ \ \::/ / |:/:/ /
\/__/ |:|\/__/ \:\/:/ / \:\ \ \:\/:/ / \:\ \ /:/ / |::/ /
|:| | \::/ / \:\__\ \::/ / \:\__\ /:/ / /:/ /
\|__| \/__/ \/__/ \/__/ \/__/ \/__/ \/__/
""")
parser = argparse.ArgumentParser(
description='Scan a procmon PML file for potentially dangerous patterns.')
parser.add_argument('--log', default='./procscan.log', metavar="LOG_FILE",
help='log file path')
parser.add_argument('--verbose', '-v', action='count', default=0,
help='increase verbosity')
parser.add_argument('--pml', required=True, metavar="PML_FILE",
help='procscan PML file')
parser.add_argument('--ac', required=True, metavar="ACCESSCHK_FILE",
help='"accesschk.exe -swu low_priv_username C:\\" output file')
args = parser.parse_args()
if args.verbose > 1:
logging.basicConfig(level=logging.DEBUG, filename=args.log)
elif args.verbose == 1:
logging.basicConfig(level=logging.INFO, filename=args.log)
else:
logging.basicConfig(level=logging.WARN, filename=args.log)
f = open(args.pml, "rb")
af = open(args.ac, "r")
print(f"Processing {args.ac} records")
WRITABLE_PATHS = {}
while True:
try:
i = af.readline().strip()
if not i:
break
if not i.startswith("RW "):
continue
WRITABLE_PATHS[i[3:]] = True
except UnicodeDecodeError:
continue
af.close()
logging.debug(f"{v}\n" for v in WRITABLE_PATHS.values())
print_banner()
print(f"Loading {args.pml} records")
pml_reader = ProcmonLogsReader(f)
print(f"Processing {len(pml_reader)} records") # number of logs
dll_hijack_candidates = {}
def processEvent(event):
logging.debug(event)
# Writable executable
if event.operation == "Load_Image" \
and event.path.endswith(".exe") \
and WRITABLE_PATHS.get(event.path, False):
if is_authority(event.process.user):
logging.critical(
f"{event.process.process_name} running as {event.process.user} loaded a writable PE located at {event.path}!")
# DLL hijacking
if event.operation == "CreateFile" \
and (event.result == 0xc0000034
or event.result == 0xc000003a) \
and event.path.endswith(".dll") \
and WRITABLE_PATHS.get("\\".join(event.path.split('\\')[:-1]), False):
logging.warning(
f"{event.process.process_name} tried to read a nonexistent DLL at {event.path}")
filename = (event.path.split('\\')[-1]).lower()
if dll_hijack_candidates.get(event.process.process_name, False):
dll_hijack_candidates[event.process.process_name].append(filename)
else:
dll_hijack_candidates[event.process.process_name] = [filename]
# DLL hijacking confirmation
if event.operation == "Load_Image" \
and event.path.endswith(".dll"):
# Writable DLL loaded by privileged process
if WRITABLE_PATHS.get(event.path, False):
if is_authority(event.process.user):
logging.critical(
f"{event.process.process_name} running as {event.process.user} loaded a writable DLL located at {event.path}!")
# DLL hijacking confirmation
logging.debug(dll_hijack_candidates)
if dll_hijack_candidates.get(event.process.process_name, False):
filename = (event.path.split('\\')[-1]).lower()
if filename in dll_hijack_candidates[event.process.process_name]:
if is_authority(event.process.user):
logging.critical(
f"{event.process.process_name} running as {event.process.user} eventually loaded {filename} from {event.path}!")
else:
logging.error(
f"{event.process.process_name} running as {event.process.user} eventually loaded {filename} from {event.path}!")
# Arbitrary file write
if event.operation == "WriteFile" \
and is_authority(event.process.user) \
and WRITABLE_PATHS.get("\\".join(event.path.split('\\')[:-1]), False):
logging.warning(
f"{event.process.process_name} running as {event.process.user} wrote to {event.path}")
# Arbitrary file delete
if event.operation.startswith("SetDispositionInformation") \
and is_authority(event.process.user) \
and WRITABLE_PATHS.get("\\".join(event.path.split('\\')[:-1]), False):
logging.warning(
f"{event.process.process_name} running as {event.process.user} called SetDispositionInformation* for {event.path}")
# Arbitrary file move
if event.operation.startswith("SetRenameInformation") \
and is_authority(event.process.user) \
and WRITABLE_PATHS.get("\\".join(event.path.split('\\')[:-1]), False):
logging.warning(
f"{event.process.process_name} running as {event.process.user} called SetRenameInformation* for {event.path}")
# Arbitrary file permission grant
if event.operation == "SetSecurityFile" \
and is_authority(event.process.user) \
and WRITABLE_PATHS.get("\\".join(event.path.split('\\')[:-1]), False):
logging.warning(
f"{event.process.process_name} running as {event.process.user} called SetSecurityFile for {event.path}")
for event in pml_reader:
processEvent(event)