-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathenhanced_cve_wizard.py
488 lines (429 loc) · 16.8 KB
/
enhanced_cve_wizard.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
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
#!/usr/bin/env python3
import subprocess
import shutil
import json
import sys
import time
import logging
import os
# =======================
# CONFIGURATION / LOGS
# =======================
LOG_FILENAME = "cve_wizard.log"
logging.basicConfig(
filename=LOG_FILENAME,
level=logging.INFO,
format="%(asctime)s %(levelname)s: %(message)s"
)
SCRIPT_NAME = "enhanced_cve_wizard.py"
# ================
# INSTALL / UPDATE
# ================
def install_exploitdb_if_missing():
"""
Checks if 'searchsploit' is installed; if missing, installs it via apt-get.
Raises EnvironmentError if apt-get is not found or installation fails.
"""
print("[*] Checking if 'searchsploit' is installed...")
logging.info("Checking if searchsploit is installed.")
if shutil.which("searchsploit") is not None:
print("[+] SearchSploit is already installed.")
logging.info("SearchSploit is installed.")
return
print("[!] SearchSploit is NOT installed. Attempting to install via apt-get...")
logging.info("SearchSploit not found; installing exploitdb.")
if shutil.which("apt-get") is None:
err_msg = (
"Error: 'apt-get' is not available on this system.\n"
"Please install 'exploitdb' manually or via your distro's package manager."
)
logging.error(err_msg)
raise EnvironmentError(err_msg)
try:
subprocess.run(
["sudo", "apt-get", "update"],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
subprocess.run(
["sudo", "apt-get", "-y", "install", "exploitdb"],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
print("[+] Successfully installed 'exploitdb' (SearchSploit).")
logging.info("Successfully installed exploitdb.")
except subprocess.CalledProcessError as e:
err_details = e.stderr.decode(errors="ignore").strip()
logging.error(f"Failed to install exploitdb: {err_details}")
raise EnvironmentError(f"Failed to install 'exploitdb': {err_details}")
def update_exploitdb():
"""
Runs 'searchsploit -u' to update the local Exploit-DB index.
Continues on warnings, but logs them. Raises no exceptions on update failure.
"""
print("[*] Updating the Exploit-DB database...")
logging.info("Updating Exploit-DB database with searchsploit -u.")
try:
subprocess.run(
["searchsploit", "-u"],
capture_output=True,
text=True,
check=True
)
print("[+] Exploit-DB database updated successfully.")
logging.info("Exploit-DB updated successfully.")
except subprocess.CalledProcessError as e:
print("[!] Warning: Failed to update Exploit-DB database.")
err_msg = e.stderr.strip()
print(" Error details:", err_msg)
logging.warning(f"Failed to update Exploit-DB: {err_msg}")
# ==========================
# FETCH DATA FROM NVD API
# ==========================
def fetch_cve_from_nvd(cve_id: str) -> dict:
"""
Fetches JSON data from NVD's public API:
https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=<CVEID>
Returns a dictionary. On failure or missing 'requests', returns {}.
"""
try:
import requests
except ImportError:
logging.warning("requests not installed, skipping NVD fetch.")
return {}
url = f"https://services.nvd.nist.gov/rest/json/cves/2.0?cveId={cve_id}"
logging.info(f"Fetching NVD data for {cve_id} from {url}.")
try:
resp = requests.get(url, timeout=10)
resp.raise_for_status() # raises if bad status code
data = resp.json()
return data # raw JSON from NVD
except Exception as e:
logging.warning(f"Failed to fetch from NVD: {str(e)}")
return {}
# ==================
# MENU OPERATIONS
# ==================
def searchsploit_json_cve(cve_id: str):
"""
Runs 'searchsploit --cve <CVE> -j' and returns the parsed JSON (if valid).
If any error occurs, returns None.
"""
print(f"[*] Searching for exploits matching {cve_id}...")
logging.info(f"Searching for CVE {cve_id} via searchsploit -j.")
cmd = ["searchsploit", "--cve", cve_id, "-j"]
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
data = json.loads(result.stdout)
return data
except subprocess.CalledProcessError as e:
err_msg = e.stderr.strip()
print(f"[!] Error: {err_msg}")
logging.error(f"SearchSploit error: {err_msg}")
return None
except json.JSONDecodeError:
print("[!] Could not parse JSON from searchsploit.")
logging.error(f"Invalid JSON from searchsploit for CVE: {cve_id}")
return None
def parse_exploit_details_and_display(data: dict):
"""
Parses more exploit details: Title, Path, EDB-ID, Author, Date, Platform.
Displays them in a table.
"""
results = data.get("RESULTS_EXPLOIT", [])
if not results:
print("[-] No exploits found for this CVE.")
logging.info("No exploits found to display.")
return
print(f"[+] Found {len(results)} exploit(s). Displaying with more details:\n")
header_format = "{:<60} {:<40} {:<15} {:<15} {:<15}"
print(header_format.format("TITLE", "PATH (EDB-ID)", "AUTHOR", "PLATFORM", "DATE"))
print("-" * 140)
for item in results:
title = item.get("Title", "N/A")
path = item.get("Path", "N/A")
edb_id = item.get("EDB-ID", "N/A")
author = item.get("Author", "N/A")
platform = item.get("Platform", "N/A")
date = item.get("Date", "N/A")
print(header_format.format(
title[:58],
f"{path} ({edb_id})",
author[:13],
platform[:13],
date[:13]
))
print("-" * 140)
def write_json_file_in_cwd(filename: str, data: dict):
"""
Writes a JSON file in the current working directory (Option: store final results).
"""
try:
with open(filename, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
print(f"[+] JSON file created: {filename}")
logging.info(f"JSON file written to {filename}")
except Exception as e:
print(f"[!] Failed to write JSON file {filename}: {str(e)}")
logging.error(f"Failed to write JSON file {filename}: {str(e)}")
def option_search_cve_and_json():
"""
Prompt for a CVE, fetch data from SearchSploit, parse details,
fetch additional data from NVD, and then write a combined JSON file.
"""
try:
cve_id = input("Enter the CVE ID (e.g., CVE-2021-44228): ").strip()
except KeyboardInterrupt:
print("\n[!] Input cancelled by user. Returning to menu.")
logging.info("CVE input cancelled by user.")
return
exploit_data = searchsploit_json_cve(cve_id)
if not exploit_data:
print("[!] No valid data returned from Exploit-DB. Returning to menu.")
exploit_data = {"RESULTS_EXPLOIT": []}
# Show exploit details
parse_exploit_details_and_display(exploit_data)
# Fetch additional data from NVD
nvd_data = fetch_cve_from_nvd(cve_id)
if nvd_data:
print("[+] NVD data fetched. Adding it to final JSON...")
logging.info(f"NVD data successfully fetched for {cve_id}.")
else:
print("[!] No NVD data found or request failed. Continuing...")
# Merge everything
final_data = {
"searchsploit_data": exploit_data,
"nvd_data": nvd_data
}
# Write final JSON in script folder
json_filename = f"{cve_id.replace('/', '_')}_exploits.json"
write_json_file_in_cwd(json_filename, final_data)
def mirror_exploit():
"""
Prompt for an EDB-ID or path to mirror (download) an exploit to the current folder.
"""
try:
edb_id = input("Enter the EDB-ID to mirror (e.g., 50592): ").strip()
except KeyboardInterrupt:
print("\n[!] Input cancelled by user. Returning to menu.")
logging.info("EDB-ID input cancelled by user.")
return
cmd = ["searchsploit", "-m", edb_id]
try:
subprocess.run(cmd, check=True)
print(f"[+] Exploit mirrored successfully for EDB-ID {edb_id}.")
logging.info(f"Mirrored exploit EDB-ID={edb_id}")
except subprocess.CalledProcessError as e:
err_msg = e.stderr.decode(errors="ignore").strip()
print(f"[!] Failed to mirror exploit: {err_msg}")
logging.error(f"Mirror exploit error: {err_msg}")
def examine_exploit():
"""
Prompt for an EDB-ID or path to examine (opens in pager).
"""
try:
edb_id = input("Enter the EDB-ID to examine (e.g., 50592): ").strip()
except KeyboardInterrupt:
print("\n[!] Input cancelled by user. Returning to menu.")
logging.info("EDB-ID input cancelled by user for examination.")
return
cmd = ["searchsploit", "-x", edb_id]
try:
subprocess.run(cmd)
logging.info(f"Examined exploit EDB-ID={edb_id}")
except subprocess.CalledProcessError as e:
err_msg = e.stderr.decode(errors="ignore").strip()
print(f"[!] Failed to examine exploit: {err_msg}")
logging.error(f"Examine exploit error: {err_msg}")
def option_mirror_or_examine():
"""
Sub-menu for Option 2: Mirror or Examine Exploits.
"""
print("\n=== Mirror or Examine Exploits ===")
print("1) Mirror Exploit by EDB-ID")
print("2) Examine Exploit by EDB-ID (opens in pager)")
print("3) Return to Main Menu")
try:
choice = input("Select an option (1-3): ").strip()
except KeyboardInterrupt:
print("\n[!] Input cancelled by user. Returning to main menu.")
logging.info("Mirror/Examine sub-menu choice cancelled by user.")
return
if choice == "1":
mirror_exploit()
elif choice == "2":
examine_exploit()
elif choice == "3":
return
else:
print("[!] Invalid choice. Returning to main menu.")
def parse_nmap_xml():
"""
Option 3: Integrate Nmap XML Parsing (searchsploit --nmap file.xml).
"""
# First, inform the user about what format is expected and show examples
print("=================================================================================")
print("This option expects an Nmap scan file in XML format (e.g., generated with -oX).")
print("Here are some example commands that produce usable XML outputs:")
print(" nmap -sV -oX scan.xml 192.168.1.10")
print(" nmap -A -oX advanced_scan.xml 192.168.1.10")
print(" nmap -Pn -sV -oX no_ping_scan.xml 192.168.1.10")
print(" nmap -p 1-65535 -sV -oX full_tcp.xml 192.168.1.10")
print("Then enter the path to that .xml file below:")
print("=================================================================================")
try:
nmap_xml_file = input("Enter path to Nmap XML file (e.g., scan.xml): ").strip()
except KeyboardInterrupt:
print("\n[!] Input cancelled by user. Returning to menu.")
logging.info("Nmap XML file input cancelled by user.")
return
if not os.path.isfile(nmap_xml_file):
print("[!] File not found. Returning to main menu.")
logging.warning(f"Nmap XML file not found: {nmap_xml_file}")
return
cmd = ["searchsploit", "--nmap", nmap_xml_file]
try:
print(f"[*] Running searchsploit --nmap on {nmap_xml_file} ...")
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
print(result.stdout)
logging.info("Nmap XML parse completed.")
except subprocess.CalledProcessError as e:
err_msg = e.stderr.strip()
print(f"[!] Error: {err_msg}")
logging.error(f"Nmap XML parse error: {err_msg}")
def option_logging_report():
"""
Option 5: Show a mini-report by printing the last 10 lines of the log file.
"""
print(f"\n[*] Displaying last 10 lines of {LOG_FILENAME}:")
if not os.path.isfile(LOG_FILENAME):
print("[-] No log file found yet.")
return
try:
with open(LOG_FILENAME, "r", encoding="utf-8") as f:
lines = f.readlines()
tail = lines[-10:] if len(lines) > 10 else lines
for line in tail:
print(line.rstrip())
except Exception as e:
print(f"[!] Could not read log file: {str(e)}")
logging.error(f"Could not read log file: {str(e)}")
def launch_minimal_gui():
"""
Option 6: Provide a minimal Tkinter GUI as a best-practice approach
while still having a console wizard.
This version gracefully handles missing or non-graphical environments:
- If 'tkinter' isn't installed, mention it and return.
- If no $DISPLAY is set, mention it and return.
- If TclError occurs, mention it and return.
"""
# 1. Check if tkinter is installed
try:
import tkinter as tk
from tkinter import ttk, messagebox
except ImportError:
print("[!] Tkinter is not installed. To install on Debian/Kali: 'sudo apt-get install python3-tk'")
logging.error("Tkinter not installed, skipping GUI.")
return
# 2. Check for a DISPLAY environment (common for X11 or Wayland)
if not os.environ.get('DISPLAY'):
print("[!] No graphical environment detected ($DISPLAY not set).")
print(" Skipping GUI. If you're on SSH, use X Forwarding or run with a desktop session.")
logging.warning("No DISPLAY variable found, skipping GUI.")
return
# 3. Try to create a root window
try:
root = tk.Tk()
except tk.TclError as e:
print(f"[!] Could not start GUI due to Tk/Tcl error: {str(e)}")
print(" Skipping GUI.")
logging.warning(f"TclError: {str(e)} - skipping GUI.")
return
root.title("Minimal Exploit Wizard GUI")
tk.Label(root, text="Enter CVE ID:").grid(row=0, column=0, padx=5, pady=5)
cve_entry = tk.Entry(root, width=30)
cve_entry.grid(row=0, column=1, padx=5, pady=5)
def on_search():
cve_id = cve_entry.get().strip()
if not cve_id:
messagebox.showwarning("Warning", "Please enter a CVE ID.")
return
data = searchsploit_json_cve(cve_id)
if not data:
messagebox.showerror("Error", "No valid data returned or error from searchsploit.")
return
results = data.get("RESULTS_EXPLOIT", [])
msg = f"Found {len(results)} exploit(s) for {cve_id}"
messagebox.showinfo("Search Complete", msg)
search_button = tk.Button(root, text="Search Exploits", command=on_search)
search_button.grid(row=1, column=0, columnspan=2, pady=10)
root.mainloop()
def main_menu():
"""
Main wizard menu showing all your new options (2, 3, 5, 6, 7).
"""
while True:
print("\n=== Enhanced CVE Wizard Menu ===")
print("1) Search CVE & Create JSON (Option 7 details + write JSON) [Now includes NVD data]")
print("2) Mirror or Examine Exploits")
print("3) Parse Nmap XML for Exploits")
print("4) Update Exploit-DB (again)")
print("5) View Logging Report (Last 10 lines)")
print("6) Launch Minimal GUI")
print("7) Exit")
try:
choice = input("Select an option (1-7): ").strip()
except KeyboardInterrupt:
print("\n[!] Input cancelled by user. Exiting.")
logging.info("Main menu choice cancelled by user. Exiting.")
break
if choice == "1":
option_search_cve_and_json()
elif choice == "2":
option_mirror_or_examine()
elif choice == "3":
parse_nmap_xml()
elif choice == "4":
update_exploitdb()
elif choice == "5":
option_logging_report()
elif choice == "6":
launch_minimal_gui()
elif choice == "7":
print("Exiting. Goodbye!")
logging.info("User exited the wizard.")
break
else:
print("[!] Invalid choice. Please try again.")
def main():
"""
0. Install/Verify searchsploit
1. Update exploitdb
2. Launch menu
"""
print("[*] Starting script...")
logging.info("Script started.")
# 0. Install if missing
try:
install_exploitdb_if_missing()
except EnvironmentError as err:
print("[!] Critical: Could not install 'exploitdb' automatically.")
print(" Error details:", err)
logging.error(f"Installation error: {err}")
sys.exit(1)
# 1. Update exploitdb
update_exploitdb()
# 2. Menu
main_menu()
print("\n[+] All done! Exiting script.")
logging.info("Script finished.")
if __name__ == "__main__":
# Top-level catch-all to guard against any unexpected runtime errors
try:
main()
except Exception as e:
logging.exception(f"An unexpected error occurred: {e}")
print("[!] An unexpected error occurred. Please check the log for details.")
sys.exit(1)