-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathlocal_audit_command_generator.py
370 lines (257 loc) · 13.5 KB
/
local_audit_command_generator.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
import pandas as pd
import logging
import argparse
import sys
def gen_ps_args(data_dict: dict) -> dict:
'''
This function generates PowerShell commands based on the provided audit type.
The function takes a dictionary where each key is an audit type and each value is a DataFrame
containing the audit data for that type. The function iterates over each audit type and generates
the appropriate PowerShell commands to audit that type. The commands are saved in a new dictionary
where each key is an audit type and each value is a string of PowerShell commands.
Note: For some audit types, the conditions for generating commands are hardcoded. This may need
to be updated in the future if the audit requirements change.
:param data_dict: A dictionary where each key is an audit type and each value is a DataFrame
containing the audit data for that type.
:return: A dictionary where each key is an audit type and each value is a string of PowerShell
commands to audit that type.
'''
ps_args_dict = {}
for key, df in data_dict.items():
checklist_values = df['Checklist'].values
description_values = df['Description'].values
reg_key_values = df['Reg Key'].values
reg_item_values = df['Reg Item'].values
subcategory_values = df['Audit Policy Subcategory'].values
right_type_values = df['Right type'].values
if key == "REGISTRY_SETTING":
reg_value_args = []
for idx, val in enumerate(checklist_values):
# generate command list for getting regristry value
reg_key = str(reg_key_values[idx])
reg_item = str(reg_item_values[idx])
if reg_key.startswith("HKLM"):
reg_key = reg_key.replace("HKLM", "HKLM:")
elif reg_key.startswith("HKU"):
reg_key = reg_key.replace("HKU", "HKU:")
arg = f"Write-Output '====';\nGet-ItemPropertyValue -Path '{reg_key}' -Name '{reg_item}'"
reg_value_args.append(arg)
ps_args_dict[key] = ';\n'.join(reg_value_args)
elif key == "PASSWORD_POLICY":
pwd_policy_args = []
for idx, val in enumerate(checklist_values):
# if val == 1:
# generate command list for getting password policy value
description = str(description_values[idx])
if "Enforce password history" in description:
subcategory = 'PasswordHistorySize ='
elif "Maximum password age" in description:
subcategory = 'MaximumPasswordAge ='
elif "Minimum password age" in description:
subcategory = 'MinimumPasswordAge ='
elif "Minimum password length" in description:
subcategory = 'MinimumPasswordLength ='
elif "complexity requirements" in description:
subcategory = 'PasswordComplexity ='
elif "reversible encryption" in description:
subcategory = 'ClearTextPassword ='
elif "Administrator account lockout" in description:
subcategory = ''
elif "Force logoff when logon hours expire" in description:
subcategory = 'ForceLogoffWhenHourExpire ='
elif "Enforce user logon restrictions" in description:
subcategory = 'TicketValidateClient ='
elif "Maximum lifetime for service ticket" in description:
subcategory = 'MaxServiceAge ='
elif "Maximum lifetime for user ticket" in description:
subcategory = 'MaxTicketAge ='
elif "Maximum lifetime for user ticket renewal" in description:
subcategory = 'MaxRenewAge ='
elif "Maximum tolerance for computer clock synchronization" in description:
subcategory = 'MaxClockSkew ='
else:
pwd_policy_args.append("\n")
arg = f"Write-Output '====';\nGet-Content -Path C:\\temp\\secpol.cfg | Select-String -Pattern '{subcategory}'"
pwd_policy_args.append(arg)
ps_args_dict[key] = ';\n'.join(pwd_policy_args)
elif key == "LOCKOUT_POLICY":
lockout_policy_args = []
for idx, val in enumerate(checklist_values):
# if val == 1:
# generate command list for getting lockout policy value
description = str(description_values[idx])
if "Account lockout duration" in description:
lockout_policy_args.append(
"Write-Output '====';\nnet accounts | select-string -pattern 'Lockout duration'")
elif "Account lockout threshold" in description:
lockout_policy_args.append(
"Write-Output '====';\nnet accounts | select-string -pattern 'Lockout threshold'")
elif "Reset account lockout counter" in description:
lockout_policy_args.append(
"Write-Output '====';\nnet accounts | select-string -pattern 'Lockout observation window'")
else:
lockout_policy_args.append("Write-Output '====';\n")
ps_args_dict[key] = ';\n'.join(lockout_policy_args)
elif key == "USER_RIGHTS_POLICY":
user_rights_args = []
for idx, val in enumerate(checklist_values):
# if val == 1:
right_type = str(right_type_values[idx])
arg = f"Write-Output '====';\nGet-Content -Path C:\\temp\\secpol.cfg | Select-String -Pattern '{right_type}'"
user_rights_args.append(arg)
ps_args_dict[key] = ';\n'.join(user_rights_args)
elif key == "CHECK_ACCOUNT":
check_account_args = []
for idx, val in enumerate(checklist_values):
# if val == 1:
# generate command list for getting check account value
description = str(description_values[idx])
if "Guest account status" in description:
check_account_args.append(
"Write-Output '====';\nnet user guest | select-string -pattern 'Account active'")
elif "Administrator account status" in description:
check_account_args.append(
"Write-Output '====';\nnet user administrator | select-string -pattern 'Account active'")
elif "Rename administrator account" in description:
check_account_args.append(
"Write-Output '===='\nnet user administrator | select-string -pattern 'User name'")
elif "Rename guest account" in description:
check_account_args.append(
"Write-Output '====';\nnet user guest | select-string -pattern 'User name'")
else:
check_account_args.append("Write-Output '====';\n")
ps_args_dict[key] = ';\n'.join(check_account_args)
elif key == "BANNER_CHECK":
banner_check_args = []
for idx, val in enumerate(checklist_values):
# if val == 1:
# generate command list for getting regristry value
reg_key = str(reg_key_values[idx])
reg_item = str(reg_item_values[idx])
if reg_key.startswith("HKLM"):
reg_key = reg_key.replace("HKLM", "HKLM:")
elif reg_key.startswith("HKU"):
reg_key = reg_key.replace("HKU", "HKU:")
arg = f"Write-Output '====';\nGet-ItemPropertyValue -Path '{reg_key}' -Name '{reg_item}'"
banner_check_args.append(arg)
ps_args_dict[key] = ';\n'.join(banner_check_args)
elif key == "ANONYMOUS_SID_SETTING":
anonymous_sid_args = []
anonymous_sid_args_list = []
for idx, val in enumerate(checklist_values):
# if val == 1:
# generate command list for getting password policy value
description = str(description_values[idx])
if "Allow anonymous SID/Name translation" in description:
subcategory = 'LSAAnonymousNameLookup ='
arg = f"Write-Output '====';\nGet-Content -Path C:\\temp\\secpol.cfg | Select-String -Pattern '{subcategory}'"
anonymous_sid_args.append(arg)
anonymous_sid_args_list.append(';'.join(anonymous_sid_args))
ps_args_dict[key] = ';\n'.join(anonymous_sid_args)
elif key == "AUDIT_POLICY_SUBCATEGORY":
audit_policy_args = []
for idx, val in enumerate(checklist_values):
# if val == 1:
subcategory = str(subcategory_values[idx])
arg = f"Write-Output '====';\nauditpol /get /subcategory:'{subcategory}' | select-string -pattern '{subcategory}'"
audit_policy_args.append(arg)
ps_args_dict[key] = ';\n'.join(audit_policy_args)
elif key == "REG_CHECK":
reg_check_args = []
for idx, val in enumerate(checklist_values):
# if val == 1:
reg_key = reg_key_values[idx]
reg_item = reg_item_values[idx]
if reg_key.startswith("HKLM"):
reg_key = reg_key.replace("HKLM", "HKLM:")
elif reg_key.startswith("HKU"):
reg_key = reg_key.replace("HKU", "HKU:")
arg = f"Write-Output '====';\nGet-ItemPropertyValue -Path '{reg_key}' -Name '{reg_item}'"
reg_check_args.append(arg)
ps_args_dict[key] = ';\n'.join(reg_check_args)
elif key == "WMI_POLICY":
wmi_policy_args = []
for idx, val in enumerate(checklist_values):
# if val == 1:
arg = "Write-Output '====';\n(Get-WmiObject -Class Win32_ComputerSystem).DomainRole"
wmi_policy_args.append(arg)
ps_args_dict[key] = ';\n'.join(wmi_policy_args)
else:
continue
return ps_args_dict
def read_file(fname: str) -> dict:
'''
This function reads an Excel file containing audit data and returns a dictionary based on the audit type.
The function takes a filename as an argument. The file should be an Excel file with each sheet containing
audit data for a different audit type. The function reads the data from each sheet and saves it in a
DataFrame. The DataFrames are stored in a dictionary where each key is the audit type and each value is
the corresponding DataFrame.
:param fname: A string containing the filename of an Excel file.
:return: A dictionary where each key is an audit type and each value is a DataFrame containing the
audit data for that type.
'''
data_dict = {
"PASSWORD_POLICY": [],
"REGISTRY_SETTING": [],
"LOCKOUT_POLICY": [],
"USER_RIGHTS_POLICY": [],
"CHECK_ACCOUNT": [],
"BANNER_CHECK": [],
"ANONYMOUS_SID_SETTING": [],
"AUDIT_POLICY_SUBCATEGORY": [],
"REG_CHECK": [],
"WMI_POLICY": []
}
xl = pd.ExcelFile(fname)
# df = xl.parse(sheet_name=0)
for type in data_dict:
try:
data_dict[type] = xl.parse(sheet_name=type)
except ValueError as e:
logging.error(f"{type} not found")
return data_dict
'''
This is the entry point of the script. It parses the command-line arguments for the path to the audit file,
reads the audit file into a dictionary of DataFrames (one for each audit type), and generates the
corresponding PowerShell commands for each audit type.
The resulting PowerShell commands are then written to a .ps1 file for later execution. The name and location
of the .ps1 file is determined by the name of the input audit file.
Usage:
python local_audit_command_generator.py --audit /path/to/audit/file
:param --audit: A string containing the path to the audit file.
:return: None
'''
if __name__ == '__main__':
my_parser = argparse.ArgumentParser(
description="This is a script for generating PowerShell commands based on audit data.")
# Add the arguments
my_parser.add_argument('-audit',
type=str,
required=True,
help='(REQUIRED) The path to the parsed audit file. This should be a .xlsx file.')
# Execute parse_args()
try:
args = my_parser.parse_args()
except SystemExit:
my_parser.print_help()
sys.exit(1)
print('Aduit file:', args.audit)
# fname = "src\Audit\CIS_MS_Windows_11_Enterprise_Level_1_v1.0.0.xlsx"
fname = args.audit
data_dict = read_file(fname)
ps_args_dict = gen_ps_args(data_dict)
# save file
script_name = 'script\\' + \
fname.split("\\")[-1].replace("xlsx", "ps1")
with open(script_name, 'w') as f:
# get host name
f.write(
r'if (!(Test-Path -Path C:\temp )) { New-Item -ItemType directory -Path C:\temp };secedit /export /cfg C:\temp\secpol.cfg;')
f.write("\nWrite-Output '====';\n")
f.write('[System.Net.Dns]::GetHostName();\n')
for key in ps_args_dict:
for cmd in ps_args_dict[key]:
f.write(cmd)
f.write(";")
# print(cmd)
print("Done! File saved: %s", script_name)