streamlit-launcher 2.2.1__py3-none-any.whl → 2.2.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
streamlit_launcher/mas.py DELETED
@@ -1,1938 +0,0 @@
1
- import tkinter as tk
2
- from tkinter import ttk, messagebox, scrolledtext, filedialog
3
- import requests
4
- from urllib.parse import urlparse, parse_qs, urljoin
5
- import socket
6
- import ssl
7
- import datetime
8
- from bs4 import BeautifulSoup
9
- import threading
10
- import re
11
- import os
12
- from PIL import Image, ImageTk
13
- import sys
14
- import subprocess
15
- import webbrowser
16
- import json
17
- import time
18
- import xml.etree.ElementTree as ET
19
- from http.client import HTTPConnection
20
- import ipaddress
21
- import whois
22
- import nmap
23
- import socket
24
- import dns
25
-
26
- class AdvancedVulnerabilityScanner:
27
- def __init__(self, root):
28
- self.root = root
29
- self.root.title("Advanced Vulnerability Scanner Pro")
30
- self.root.geometry("900x700")
31
- self.root.configure(bg='#f0f0f0')
32
- self.icon_path = os.path.join(os.path.dirname(__file__), "icon.ico")
33
- if os.path.exists(self.icon_path):
34
- self.root.iconbitmap(self.icon_path)
35
-
36
- # Scan configuration
37
- self.scan_config = {
38
- 'timeout': 10,
39
- 'max_pages': 20,
40
- 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
41
- 'threads': 5
42
- }
43
-
44
- # Configure styles
45
- self.style = ttk.Style()
46
- self.style.configure('TFrame', background='#f0f0f0')
47
- self.style.configure('TLabel', background='#f0f0f0', font=('Arial', 9))
48
- self.style.configure('TButton', font=('Arial', 9))
49
- self.style.configure('Treeview', font=('Arial', 9), rowheight=25)
50
- self.style.configure('TNotebook', background='#f0f0f0')
51
- self.style.configure('TNotebook.Tab', padding=[10, 5])
52
-
53
- # Main UI components
54
- self.create_widgets()
55
- self.create_menu()
56
-
57
- # Initialize nmap scanner
58
- self.nm = nmap.PortScanner() if self.check_nmap_installed() else None
59
-
60
- def check_nmap_installed(self):
61
- try:
62
- subprocess.run(["nmap", "--version"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
63
- return True
64
- except:
65
- return False
66
-
67
- def create_menu(self):
68
- menubar = tk.Menu(self.root)
69
-
70
- # File menu
71
- file_menu = tk.Menu(menubar, tearoff=0)
72
- file_menu.add_command(label="Save Report", command=self.save_report)
73
- file_menu.add_command(label="Load Config", command=self.load_config)
74
- file_menu.add_command(label="Save Config", command=self.save_config)
75
- file_menu.add_separator()
76
- file_menu.add_command(label="Exit", command=self.root.quit)
77
- menubar.add_cascade(label="File", menu=file_menu)
78
-
79
- # Tools menu
80
- tools_menu = tk.Menu(menubar, tearoff=0)
81
- tools_menu.add_command(label="Port Scanner", command=self.open_port_scanner)
82
- tools_menu.add_command(label="WHOIS Lookup", command=self.open_whois_tool)
83
- tools_menu.add_command(label="DNS Lookup", command=self.open_dns_tool)
84
- tools_menu.add_command(label="Ping Tool", command=self.open_ping_tool)
85
- menubar.add_cascade(label="Tools", menu=tools_menu)
86
-
87
- # Help menu
88
- help_menu = tk.Menu(menubar, tearoff=0)
89
- help_menu.add_command(label="Documentation", command=self.open_documentation)
90
- help_menu.add_command(label="About", command=self.show_about)
91
- menubar.add_cascade(label="Help", menu=help_menu)
92
-
93
- self.root.config(menu=menubar)
94
-
95
- def create_widgets(self):
96
- # Header
97
- header_frame = ttk.Frame(self.root)
98
- header_frame.pack(pady=5, fill=tk.X)
99
-
100
- try:
101
- icon_path = os.path.join(os.path.dirname(__file__), "icon.ico")
102
- if os.path.exists(icon_path):
103
- img = Image.open(icon_path)
104
- img = img.resize((32, 32), Image.LANCZOS)
105
- self.tk_icon = ImageTk.PhotoImage(img)
106
- icon_label = ttk.Label(header_frame, image=self.tk_icon)
107
- icon_label.pack(side=tk.LEFT, padx=(0, 8))
108
- except Exception:
109
- pass
110
-
111
- ttk.Label(header_frame, text="Advanced Vulnerability Scanner Pro",
112
- font=('Arial', 14, 'bold')).pack(side=tk.LEFT)
113
-
114
- # Input section
115
- input_frame = ttk.Frame(self.root)
116
- input_frame.pack(pady=5, fill=tk.X, padx=10)
117
-
118
- ttk.Label(input_frame, text="Target URL/IP:").grid(row=0, column=0, sticky=tk.W)
119
- self.url_entry = ttk.Entry(input_frame, width=50)
120
- self.url_entry.grid(row=0, column=1, padx=5)
121
- self.url_entry.insert(0, "https://")
122
-
123
- self.scan_btn = ttk.Button(input_frame, text="Scan", command=self.start_scan)
124
- self.scan_btn.grid(row=0, column=2, padx=5)
125
-
126
- # Quick tools buttons
127
- self.wsl_btn = ttk.Button(input_frame, text="WSL", command=self.open_wsl_linux)
128
- self.wsl_btn.grid(row=0, column=3, padx=5)
129
-
130
- self.terminal_btn = ttk.Button(input_frame, text="Terminal", command=self.open_terminal)
131
- self.terminal_btn.grid(row=0, column=4, padx=5)
132
-
133
- # Scan options notebook
134
- options_notebook = ttk.Notebook(self.root)
135
- options_notebook.pack(pady=5, fill=tk.X, padx=10)
136
-
137
- # Basic scan options
138
- basic_frame = ttk.Frame(options_notebook)
139
- self.sql_var = tk.BooleanVar(value=True)
140
- self.xss_var = tk.BooleanVar(value=True)
141
- self.headers_var = tk.BooleanVar(value=True)
142
- self.ssl_var = tk.BooleanVar(value=True)
143
- self.crawl_var = tk.BooleanVar(value=True)
144
-
145
- ttk.Checkbutton(basic_frame, text="SQL Injection", variable=self.sql_var).grid(row=0, column=0, sticky=tk.W)
146
- ttk.Checkbutton(basic_frame, text="XSS", variable=self.xss_var).grid(row=0, column=1, sticky=tk.W)
147
- ttk.Checkbutton(basic_frame, text="Headers", variable=self.headers_var).grid(row=0, column=2, sticky=tk.W)
148
- ttk.Checkbutton(basic_frame, text="SSL/TLS", variable=self.ssl_var).grid(row=1, column=0, sticky=tk.W)
149
- ttk.Checkbutton(basic_frame, text="Crawl Pages", variable=self.crawl_var).grid(row=1, column=1, sticky=tk.W)
150
-
151
- options_notebook.add(basic_frame, text="Basic")
152
-
153
- # Advanced scan options
154
- adv_frame = ttk.Frame(options_notebook)
155
- self.csrf_var = tk.BooleanVar(value=True)
156
- self.dir_trav_var = tk.BooleanVar(value=True)
157
- self.cmdi_var = tk.BooleanVar(value=True)
158
- self.file_exp_var = tk.BooleanVar(value=True)
159
- self.ssrf_var = tk.BooleanVar(value=False)
160
- self.xxe_var = tk.BooleanVar(value=False)
161
- self.idor_var = tk.BooleanVar(value=False)
162
-
163
- ttk.Checkbutton(adv_frame, text="CSRF", variable=self.csrf_var).grid(row=0, column=0, sticky=tk.W)
164
- ttk.Checkbutton(adv_frame, text="Directory Traversal", variable=self.dir_trav_var).grid(row=0, column=1, sticky=tk.W)
165
- ttk.Checkbutton(adv_frame, text="Command Injection", variable=self.cmdi_var).grid(row=0, column=2, sticky=tk.W)
166
- ttk.Checkbutton(adv_frame, text="Exposed Files", variable=self.file_exp_var).grid(row=1, column=0, sticky=tk.W)
167
- ttk.Checkbutton(adv_frame, text="SSRF", variable=self.ssrf_var).grid(row=1, column=1, sticky=tk.W)
168
- ttk.Checkbutton(adv_frame, text="XXE", variable=self.xxe_var).grid(row=1, column=2, sticky=tk.W)
169
- ttk.Checkbutton(adv_frame, text="IDOR", variable=self.idor_var).grid(row=2, column=0, sticky=tk.W)
170
-
171
- options_notebook.add(adv_frame, text="Advanced")
172
-
173
- # Network scan options
174
- net_frame = ttk.Frame(options_notebook)
175
- self.port_scan_var = tk.BooleanVar(value=False)
176
- self.os_detect_var = tk.BooleanVar(value=False)
177
- self.service_scan_var = tk.BooleanVar(value=False)
178
-
179
- ttk.Checkbutton(net_frame, text="Port Scan", variable=self.port_scan_var).grid(row=0, column=0, sticky=tk.W)
180
- ttk.Checkbutton(net_frame, text="OS Detection", variable=self.os_detect_var).grid(row=0, column=1, sticky=tk.W)
181
- ttk.Checkbutton(net_frame, text="Service Scan", variable=self.service_scan_var).grid(row=0, column=2, sticky=tk.W)
182
-
183
- options_notebook.add(net_frame, text="Network")
184
-
185
- # Progress bar
186
- self.progress = ttk.Progressbar(self.root, orient=tk.HORIZONTAL, mode='determinate')
187
- self.progress.pack(pady=5, fill=tk.X, padx=10)
188
- self.status = ttk.Label(self.root, text="Ready to scan")
189
- self.status.pack()
190
-
191
- # Results display
192
- notebook = ttk.Notebook(self.root)
193
- notebook.pack(pady=5, fill=tk.BOTH, expand=True, padx=10)
194
-
195
- # Vulnerabilities tab
196
- vuln_frame = ttk.Frame(notebook)
197
- self.results_tree = ttk.Treeview(vuln_frame, columns=('severity', 'type', 'details'), show='headings')
198
- self.results_tree.heading('severity', text='Severity')
199
- self.results_tree.heading('type', text='Vulnerability')
200
- self.results_tree.heading('details', text='Details')
201
- self.results_tree.column('severity', width=80, anchor=tk.CENTER)
202
- self.results_tree.column('type', width=150)
203
- self.results_tree.column('details', width=500)
204
-
205
- scrollbar = ttk.Scrollbar(vuln_frame, orient=tk.VERTICAL, command=self.results_tree.yview)
206
- self.results_tree.configure(yscroll=scrollbar.set)
207
- scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
208
- self.results_tree.pack(fill=tk.BOTH, expand=True)
209
-
210
- # Add context menu for vulnerabilities
211
- self.vuln_menu = tk.Menu(self.root, tearoff=0)
212
- self.vuln_menu.add_command(label="View Details", command=self.show_vuln_details)
213
- self.vuln_menu.add_command(label="Copy Details", command=self.copy_vuln_details)
214
- self.vuln_menu.add_command(label="Export to Report", command=self.export_vuln_to_report)
215
- self.results_tree.bind("<Button-3>", self.show_vuln_context_menu)
216
-
217
- notebook.add(vuln_frame, text='Vulnerabilities')
218
-
219
- # Details tab
220
- details_frame = ttk.Frame(notebook)
221
- self.details_text = scrolledtext.ScrolledText(details_frame, wrap=tk.WORD, font=('Consolas', 9))
222
- self.details_text.pack(fill=tk.BOTH, expand=True)
223
- notebook.add(details_frame, text='Scan Details')
224
-
225
- # Exposed Files tab
226
- files_frame = ttk.Frame(notebook)
227
- self.files_tree = ttk.Treeview(files_frame, columns=('type', 'url', 'status', 'size'), show='headings')
228
- self.files_tree.heading('type', text='Type')
229
- self.files_tree.heading('url', text='URL')
230
- self.files_tree.heading('status', text='Status')
231
- self.files_tree.heading('size', text='Size')
232
- self.files_tree.column('type', width=100)
233
- self.files_tree.column('url', width=350)
234
- self.files_tree.column('status', width=80)
235
- self.files_tree.column('size', width=80)
236
-
237
- files_scrollbar = ttk.Scrollbar(files_frame, orient=tk.VERTICAL, command=self.files_tree.yview)
238
- self.files_tree.configure(yscroll=files_scrollbar.set)
239
- files_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
240
- self.files_tree.pack(fill=tk.BOTH, expand=True)
241
-
242
- # Add context menu for files tree
243
- self.files_menu = tk.Menu(self.root, tearoff=0)
244
- self.files_menu.add_command(label="Open in Browser", command=self.open_selected_file)
245
- self.files_menu.add_command(label="Copy URL", command=self.copy_file_url)
246
- self.files_menu.add_command(label="Download File", command=self.download_selected_file)
247
- self.files_tree.bind("<Button-3>", self.show_files_context_menu)
248
-
249
- notebook.add(files_frame, text='Exposed Files')
250
-
251
- # Network Info tab
252
- network_frame = ttk.Frame(notebook)
253
- self.network_tree = ttk.Treeview(network_frame, columns=('service', 'port', 'state', 'version'), show='headings')
254
- self.network_tree.heading('service', text='Service')
255
- self.network_tree.heading('port', text='Port')
256
- self.network_tree.heading('state', text='State')
257
- self.network_tree.heading('version', text='Version')
258
- self.network_tree.column('service', width=150)
259
- self.network_tree.column('port', width=80)
260
- self.network_tree.column('state', width=80)
261
- self.network_tree.column('version', width=200)
262
-
263
- network_scrollbar = ttk.Scrollbar(network_frame, orient=tk.VERTICAL, command=self.network_tree.yview)
264
- self.network_tree.configure(yscroll=network_scrollbar.set)
265
- network_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
266
- self.network_tree.pack(fill=tk.BOTH, expand=True)
267
-
268
- notebook.add(network_frame, text='Network Info')
269
-
270
- # Configure tags for severity colors
271
- self.results_tree.tag_configure('critical', background='#ff9999', foreground='#800000')
272
- self.results_tree.tag_configure('high', background='#ffcccc')
273
- self.results_tree.tag_configure('medium', background='#fff3cd')
274
- self.results_tree.tag_configure('low', background='#d4edda')
275
- self.results_tree.tag_configure('info', background='#d1ecf1')
276
-
277
- # Configure tags for network info
278
- self.network_tree.tag_configure('open', background='#ffcccc')
279
- self.network_tree.tag_configure('filtered', background='#fff3cd')
280
- self.network_tree.tag_configure('closed', background='#d4edda')
281
-
282
- def show_vuln_context_menu(self, event):
283
- item = self.results_tree.identify_row(event.y)
284
- if item:
285
- self.results_tree.selection_set(item)
286
- self.vuln_menu.post(event.x_root, event.y_root)
287
-
288
- def show_vuln_details(self):
289
- selected = self.results_tree.selection()
290
- if selected:
291
- details = self.results_tree.item(selected[0])['values'][2]
292
- self.show_details_window("Vulnerability Details", details)
293
-
294
- def copy_vuln_details(self):
295
- selected = self.results_tree.selection()
296
- if selected:
297
- details = self.results_tree.item(selected[0])['values'][2]
298
- self.root.clipboard_clear()
299
- self.root.clipboard_append(details)
300
- messagebox.showinfo("Copied", "Vulnerability details copied to clipboard")
301
-
302
- def export_vuln_to_report(self):
303
- selected = self.results_tree.selection()
304
- if selected:
305
- item = self.results_tree.item(selected[0])
306
- severity, vuln_type, details = item['values']
307
-
308
- report = f"Vulnerability Report\n\n"
309
- report += f"Severity: {severity}\n"
310
- report += f"Type: {vuln_type}\n"
311
- report += f"Details:\n{details}\n"
312
-
313
- filename = filedialog.asksaveasfilename(
314
- defaultextension=".txt",
315
- filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")],
316
- title="Save Report As"
317
- )
318
-
319
- if filename:
320
- with open(filename, 'w') as f:
321
- f.write(report)
322
- messagebox.showinfo("Saved", f"Report saved to {filename}")
323
-
324
- def show_details_window(self, title, content):
325
- window = tk.Toplevel(self.root)
326
- window.title(title)
327
- window.geometry("600x400")
328
-
329
- text = scrolledtext.ScrolledText(window, wrap=tk.WORD, font=('Consolas', 9))
330
- text.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
331
- text.insert(tk.END, content)
332
- text.config(state=tk.DISABLED)
333
-
334
- btn_frame = ttk.Frame(window)
335
- btn_frame.pack(pady=5)
336
-
337
- ttk.Button(btn_frame, text="Copy", command=lambda: self.copy_to_clipboard(content)).pack(side=tk.LEFT, padx=5)
338
- ttk.Button(btn_frame, text="Close", command=window.destroy).pack(side=tk.LEFT, padx=5)
339
-
340
- def copy_to_clipboard(self, text):
341
- self.root.clipboard_clear()
342
- self.root.clipboard_append(text)
343
- messagebox.showinfo("Copied", "Content copied to clipboard")
344
-
345
- def show_files_context_menu(self, event):
346
- item = self.files_tree.identify_row(event.y)
347
- if item:
348
- self.files_tree.selection_set(item)
349
- self.files_menu.post(event.x_root, event.y_root)
350
-
351
- def open_selected_file(self):
352
- selected = self.files_tree.selection()
353
- if selected:
354
- url = self.files_tree.item(selected[0])['values'][1]
355
- webbrowser.open(url)
356
-
357
- def download_selected_file(self):
358
- selected = self.files_tree.selection()
359
- if selected:
360
- url = self.files_tree.item(selected[0])['values'][1]
361
- try:
362
- response = requests.get(url, stream=True, timeout=10)
363
- if response.status_code == 200:
364
- filename = filedialog.asksaveasfilename(
365
- initialfile=os.path.basename(urlparse(url).path),
366
- title="Save File As"
367
- )
368
- if filename:
369
- with open(filename, 'wb') as f:
370
- for chunk in response.iter_content(1024):
371
- f.write(chunk)
372
- messagebox.showinfo("Download Complete", f"File saved to {filename}")
373
- except Exception as e:
374
- messagebox.showerror("Download Error", f"Failed to download file: {str(e)}")
375
-
376
- def copy_file_url(self):
377
- selected = self.files_tree.selection()
378
- if selected:
379
- url = self.files_tree.item(selected[0])['values'][1]
380
- self.root.clipboard_clear()
381
- self.root.clipboard_append(url)
382
- messagebox.showinfo("Copied", "URL copied to clipboard")
383
-
384
- def open_wsl_linux(self):
385
- """Open WSL (Windows Subsystem for Linux) terminal"""
386
- try:
387
- subprocess.Popen("wsl", creationflags=subprocess.CREATE_NEW_CONSOLE)
388
- messagebox.showinfo("WSL", "WSL terminal opened successfully!")
389
- except Exception as e:
390
- messagebox.showerror("Error", f"Failed to open WSL: {str(e)}")
391
-
392
- def open_terminal(self):
393
- """Open system terminal"""
394
- try:
395
- if sys.platform == "win32":
396
- subprocess.Popen("cmd", creationflags=subprocess.CREATE_NEW_CONSOLE)
397
- elif sys.platform == "linux":
398
- subprocess.Popen("x-terminal-emulator")
399
- elif sys.platform == "darwin":
400
- subprocess.Popen("open -a Terminal .")
401
- messagebox.showinfo("Terminal", "Terminal opened successfully!")
402
- except Exception as e:
403
- messagebox.showerror("Error", f"Failed to open terminal: {str(e)}")
404
-
405
- def save_report(self):
406
- """Save scan results to a file"""
407
- try:
408
- filename = filedialog.asksaveasfilename(
409
- defaultextension=".txt",
410
- filetypes=[("Text Files", "*.txt"), ("HTML Files", "*.html"), ("All Files", "*.*")],
411
- title="Save Report As"
412
- )
413
-
414
- if filename:
415
- if filename.endswith('.html'):
416
- self.save_html_report(filename)
417
- else:
418
- self.save_text_report(filename)
419
-
420
- messagebox.showinfo("Saved", f"Report saved to {filename}")
421
- except Exception as e:
422
- messagebox.showerror("Error", f"Failed to save report: {str(e)}")
423
-
424
- def save_text_report(self, filename):
425
- """Save report as plain text"""
426
- with open(filename, 'w') as f:
427
- # Write header
428
- f.write("="*80 + "\n")
429
- f.write("Advanced Vulnerability Scanner Report\n")
430
- f.write(f"Generated: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
431
- f.write(f"Target: {self.url_entry.get()}\n")
432
- f.write("="*80 + "\n\n")
433
-
434
- # Write vulnerabilities
435
- f.write("Vulnerabilities:\n")
436
- f.write("-"*80 + "\n")
437
- for item in self.results_tree.get_children():
438
- severity, vuln_type, details = self.results_tree.item(item)['values']
439
- f.write(f"[{severity}] {vuln_type}:\n{details}\n\n")
440
-
441
- # Write exposed files
442
- f.write("\nExposed Files:\n")
443
- f.write("-"*80 + "\n")
444
- for item in self.files_tree.get_children():
445
- file_type, url, status, size = self.files_tree.item(item)['values']
446
- f.write(f"{file_type}: {url} ({status}, {size})\n")
447
-
448
- # Write network info
449
- if self.network_tree.get_children():
450
- f.write("\nNetwork Information:\n")
451
- f.write("-"*80 + "\n")
452
- for item in self.network_tree.get_children():
453
- service, port, state, version = self.network_tree.item(item)['values']
454
- f.write(f"{service} on port {port} ({state}): {version}\n")
455
-
456
- # Write scan details
457
- f.write("\nScan Details:\n")
458
- f.write("-"*80 + "\n")
459
- f.write(self.details_text.get("1.0", tk.END))
460
-
461
- def save_html_report(self, filename):
462
- """Save report as HTML"""
463
- with open(filename, 'w') as f:
464
- f.write("<!DOCTYPE html>\n<html>\n<head>\n")
465
- f.write("<title>Vulnerability Scan Report</title>\n")
466
- f.write("<style>\n")
467
- f.write("body { font-family: Arial, sans-serif; line-height: 1.6; }\n")
468
- f.write("h1, h2 { color: #333; }\n")
469
- f.write(".critical { background-color: #ff9999; color: #800000; padding: 2px 5px; }\n")
470
- f.write(".high { background-color: #ffcccc; padding: 2px 5px; }\n")
471
- f.write(".medium { background-color: #fff3cd; padding: 2px 5px; }\n")
472
- f.write(".low { background-color: #d4edda; padding: 2px 5px; }\n")
473
- f.write(".info { background-color: #d1ecf1; padding: 2px 5px; }\n")
474
- f.write("pre { background-color: #f5f5f5; padding: 10px; border-radius: 3px; }\n")
475
- f.write("table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }\n")
476
- f.write("th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }\n")
477
- f.write("th { background-color: #f2f2f2; }\n")
478
- f.write("</style>\n</head>\n<body>\n")
479
-
480
- # Write header
481
- f.write("<h1>Advanced Vulnerability Scanner Report</h1>\n")
482
- f.write(f"<p><strong>Generated:</strong> {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>\n")
483
- f.write(f"<p><strong>Target:</strong> {self.url_entry.get()}</p>\n")
484
- f.write("<hr>\n")
485
-
486
- # Write vulnerabilities
487
- f.write("<h2>Vulnerabilities</h2>\n")
488
- f.write("<table>\n")
489
- f.write("<tr><th>Severity</th><th>Type</th><th>Details</th></tr>\n")
490
- for item in self.results_tree.get_children():
491
- severity, vuln_type, details = self.results_tree.item(item)['values']
492
- f.write(f"<tr><td><span class='{severity.lower()}'>{severity}</span></td>")
493
- f.write(f"<td>{vuln_type}</td><td><pre>{details}</pre></td></tr>\n")
494
- f.write("</table>\n")
495
-
496
- # Write exposed files
497
- f.write("<h2>Exposed Files</h2>\n")
498
- f.write("<table>\n")
499
- f.write("<tr><th>Type</th><th>URL</th><th>Status</th><th>Size</th></tr>\n")
500
- for item in self.files_tree.get_children():
501
- file_type, url, status, size = self.files_tree.item(item)['values']
502
- f.write(f"<tr><td>{file_type}</td><td><a href='{url}' target='_blank'>{url}</a></td>")
503
- f.write(f"<td>{status}</td><td>{size}</td></tr>\n")
504
- f.write("</table>\n")
505
-
506
- # Write network info
507
- if self.network_tree.get_children():
508
- f.write("<h2>Network Information</h2>\n")
509
- f.write("<table>\n")
510
- f.write("<tr><th>Service</th><th>Port</th><th>State</th><th>Version</th></tr>\n")
511
- for item in self.network_tree.get_children():
512
- service, port, state, version = self.network_tree.item(item)['values']
513
- f.write(f"<tr><td>{service}</td><td>{port}</td><td>{state}</td><td>{version}</td></tr>\n")
514
- f.write("</table>\n")
515
-
516
- # Write scan details
517
- f.write("<h2>Scan Details</h2>\n")
518
- f.write(f"<pre>{self.details_text.get('1.0', tk.END)}</pre>\n")
519
-
520
- f.write("</body>\n</html>")
521
-
522
- def load_config(self):
523
- """Load scan configuration from file"""
524
- try:
525
- filename = filedialog.askopenfilename(
526
- filetypes=[("JSON Files", "*.json"), ("All Files", "*.*")],
527
- title="Load Configuration"
528
- )
529
-
530
- if filename:
531
- with open(filename, 'r') as f:
532
- config = json.load(f)
533
-
534
- # Update UI with loaded configuration
535
- self.url_entry.delete(0, tk.END)
536
- self.url_entry.insert(0, config.get('target_url', ''))
537
-
538
- # Update scan options
539
- self.sql_var.set(config.get('sql_injection', True))
540
- self.xss_var.set(config.get('xss', True))
541
- self.headers_var.set(config.get('headers', True))
542
- self.ssl_var.set(config.get('ssl', True))
543
- self.crawl_var.set(config.get('crawl', True))
544
- self.csrf_var.set(config.get('csrf', True))
545
- self.dir_trav_var.set(config.get('dir_traversal', True))
546
- self.cmdi_var.set(config.get('command_injection', True))
547
- self.file_exp_var.set(config.get('exposed_files', True))
548
- self.ssrf_var.set(config.get('ssrf', False))
549
- self.xxe_var.set(config.get('xxe', False))
550
- self.idor_var.set(config.get('idor', False))
551
- self.port_scan_var.set(config.get('port_scan', False))
552
- self.os_detect_var.set(config.get('os_detection', False))
553
- self.service_scan_var.set(config.get('service_scan', False))
554
-
555
- # Update scan configuration
556
- self.scan_config.update(config.get('scan_config', {}))
557
-
558
- messagebox.showinfo("Loaded", "Configuration loaded successfully!")
559
- except Exception as e:
560
- messagebox.showerror("Error", f"Failed to load configuration: {str(e)}")
561
-
562
- def save_config(self):
563
- """Save current configuration to file"""
564
- try:
565
- filename = filedialog.asksaveasfilename(
566
- defaultextension=".json",
567
- filetypes=[("JSON Files", "*.json"), ("All Files", "*.*")],
568
- title="Save Configuration As"
569
- )
570
-
571
- if filename:
572
- config = {
573
- 'target_url': self.url_entry.get(),
574
- 'scan_config': self.scan_config,
575
- 'sql_injection': self.sql_var.get(),
576
- 'xss': self.xss_var.get(),
577
- 'headers': self.headers_var.get(),
578
- 'ssl': self.ssl_var.get(),
579
- 'crawl': self.crawl_var.get(),
580
- 'csrf': self.csrf_var.get(),
581
- 'dir_traversal': self.dir_trav_var.get(),
582
- 'command_injection': self.cmdi_var.get(),
583
- 'exposed_files': self.file_exp_var.get(),
584
- 'ssrf': self.ssrf_var.get(),
585
- 'xxe': self.xxe_var.get(),
586
- 'idor': self.idor_var.get(),
587
- 'port_scan': self.port_scan_var.get(),
588
- 'os_detection': self.os_detect_var.get(),
589
- 'service_scan': self.service_scan_var.get()
590
- }
591
-
592
- with open(filename, 'w') as f:
593
- json.dump(config, f, indent=4)
594
-
595
- messagebox.showinfo("Saved", f"Configuration saved to {filename}")
596
- except Exception as e:
597
- messagebox.showerror("Error", f"Failed to save configuration: {str(e)}")
598
-
599
- def open_port_scanner(self):
600
- """Open port scanner tool"""
601
- port_window = tk.Toplevel(self.root)
602
- port_window.title("Port Scanner")
603
- port_window.geometry("500x400")
604
-
605
- ttk.Label(port_window, text="Target Host:").pack(pady=(10, 0))
606
- host_entry = ttk.Entry(port_window, width=30)
607
- host_entry.pack()
608
- host_entry.insert(0, self.url_entry.get())
609
-
610
- ttk.Label(port_window, text="Port Range (e.g., 1-1024):").pack(pady=(10, 0))
611
- port_entry = ttk.Entry(port_window, width=30)
612
- port_entry.pack()
613
- port_entry.insert(0, "1-1024")
614
-
615
- result_text = scrolledtext.ScrolledText(port_window, wrap=tk.WORD, height=15)
616
- result_text.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
617
-
618
- def scan_ports():
619
- host = host_entry.get().strip()
620
- port_range = port_entry.get().strip()
621
-
622
- if not host:
623
- messagebox.showerror("Error", "Please enter a target host")
624
- return
625
-
626
- try:
627
- # Validate port range
628
- if '-' in port_range:
629
- start, end = map(int, port_range.split('-'))
630
- else:
631
- start = end = int(port_range)
632
-
633
- result_text.delete(1.0, tk.END)
634
- result_text.insert(tk.END, f"Scanning {host} ports {start}-{end}...\n\n")
635
- port_window.update()
636
-
637
- open_ports = []
638
-
639
- for port in range(start, end + 1):
640
- try:
641
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
642
- sock.settimeout(1)
643
- result = sock.connect_ex((host, port))
644
- if result == 0:
645
- service = socket.getservbyport(port, 'tcp') if port <= 65535 else 'unknown'
646
- open_ports.append((port, service))
647
- result_text.insert(tk.END, f"Port {port} ({service}) is open\n")
648
- sock.close()
649
- except:
650
- result_text.insert(tk.END, f"Port {port}: Error scanning\n")
651
-
652
- port_window.update()
653
-
654
- result_text.insert(tk.END, f"\nScan complete. Found {len(open_ports)} open ports.\n")
655
-
656
- if open_ports:
657
- result_text.insert(tk.END, "\nOpen ports:\n")
658
- for port, service in open_ports:
659
- result_text.insert(tk.END, f"- {port}: {service}\n")
660
-
661
- except ValueError:
662
- messagebox.showerror("Error", "Invalid port range format")
663
- except Exception as e:
664
- messagebox.showerror("Error", f"Port scan failed: {str(e)}")
665
-
666
- ttk.Button(port_window, text="Scan Ports", command=scan_ports).pack(pady=5)
667
-
668
- def open_whois_tool(self):
669
- """Open WHOIS lookup tool"""
670
- whois_window = tk.Toplevel(self.root)
671
- whois_window.title("WHOIS Lookup")
672
- whois_window.geometry("600x400")
673
-
674
- ttk.Label(whois_window, text="Domain or IP:").pack(pady=(10, 0))
675
- domain_entry = ttk.Entry(whois_window, width=30)
676
- domain_entry.pack()
677
- domain_entry.insert(0, self.url_entry.get())
678
-
679
- result_text = scrolledtext.ScrolledText(whois_window, wrap=tk.WORD, height=20)
680
- result_text.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
681
-
682
- def perform_whois():
683
- domain = domain_entry.get().strip()
684
- if not domain:
685
- messagebox.showerror("Error", "Please enter a domain or IP")
686
- return
687
-
688
- try:
689
- result_text.delete(1.0, tk.END)
690
- result_text.insert(tk.END, f"Performing WHOIS lookup for {domain}...\n\n")
691
- whois_window.update()
692
-
693
- # Extract domain from URL if needed
694
- if domain.startswith(('http://', 'https://')):
695
- domain = urlparse(domain).netloc
696
-
697
- # Perform WHOIS lookup
698
- w = whois.whois(domain)
699
-
700
- result_text.insert(tk.END, f"WHOIS Results for {domain}:\n\n")
701
-
702
- # Display basic info
703
- result_text.insert(tk.END, f"Domain Name: {w.domain_name}\n")
704
- result_text.insert(tk.END, f"Registrar: {w.registrar}\n")
705
- result_text.insert(tk.END, f"Creation Date: {w.creation_date}\n")
706
- result_text.insert(tk.END, f"Expiration Date: {w.expiration_date}\n")
707
- result_text.insert(tk.END, f"Name Servers: {', '.join(w.name_servers) if w.name_servers else 'N/A'}\n\n")
708
-
709
- # Display raw WHOIS data
710
- result_text.insert(tk.END, "Raw WHOIS Data:\n")
711
- result_text.insert(tk.END, "-"*50 + "\n")
712
- result_text.insert(tk.END, str(w.text) + "\n")
713
-
714
- except Exception as e:
715
- messagebox.showerror("Error", f"WHOIS lookup failed: {str(e)}")
716
-
717
- ttk.Button(whois_window, text="Lookup", command=perform_whois).pack(pady=5)
718
-
719
- def open_dns_tool(self):
720
- """Open DNS lookup tool"""
721
- dns_window = tk.Toplevel(self.root)
722
- dns_window.title("DNS Lookup")
723
- dns_window.geometry("600x400")
724
-
725
- ttk.Label(dns_window, text="Domain:").pack(pady=(10, 0))
726
- domain_entry = ttk.Entry(dns_window, width=30)
727
- domain_entry.pack()
728
- domain_entry.insert(0, self.url_entry.get())
729
-
730
- result_text = scrolledtext.ScrolledText(dns_window, wrap=tk.WORD, height=20)
731
- result_text.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
732
-
733
- def perform_dns_lookup():
734
- domain = domain_entry.get().strip()
735
- if not domain:
736
- messagebox.showerror("Error", "Please enter a domain")
737
- return
738
-
739
- try:
740
- result_text.delete(1.0, tk.END)
741
- result_text.insert(tk.END, f"Performing DNS lookup for {domain}...\n\n")
742
- dns_window.update()
743
-
744
- # Extract domain from URL if needed
745
- if domain.startswith(('http://', 'https://')):
746
- domain = urlparse(domain).netloc
747
-
748
- # Perform DNS lookups
749
- record_types = ['A', 'AAAA', 'MX', 'NS', 'TXT', 'CNAME', 'SOA']
750
-
751
- for record_type in record_types:
752
- try:
753
- result_text.insert(tk.END, f"{record_type} Records:\n")
754
- answers = dns.resolver.resolve(domain, record_type)
755
- for rdata in answers:
756
- result_text.insert(tk.END, f"- {rdata}\n")
757
- result_text.insert(tk.END, "\n")
758
- except dns.resolver.NoAnswer:
759
- result_text.insert(tk.END, f"No {record_type} records found\n\n")
760
- except dns.resolver.NXDOMAIN:
761
- result_text.insert(tk.END, f"Domain {domain} does not exist\n\n")
762
- break
763
- except Exception as e:
764
- result_text.insert(tk.END, f"Error querying {record_type} records: {str(e)}\n\n")
765
-
766
- dns_window.update()
767
-
768
- result_text.insert(tk.END, "DNS lookup complete.\n")
769
-
770
- except Exception as e:
771
- messagebox.showerror("Error", f"DNS lookup failed: {str(e)}")
772
-
773
- ttk.Button(dns_window, text="Lookup", command=perform_dns_lookup).pack(pady=5)
774
-
775
- def open_ping_tool(self):
776
- """Open ping tool"""
777
- ping_window = tk.Toplevel(self.root)
778
- ping_window.title("Ping Tool")
779
- ping_window.geometry("500x300")
780
-
781
- ttk.Label(ping_window, text="Host:").pack(pady=(10, 0))
782
- host_entry = ttk.Entry(ping_window, width=30)
783
- host_entry.pack()
784
- host_entry.insert(0, self.url_entry.get())
785
-
786
- ttk.Label(ping_window, text="Count (1-10):").pack(pady=(10, 0))
787
- count_entry = ttk.Entry(ping_window, width=10)
788
- count_entry.pack()
789
- count_entry.insert(0, "4")
790
-
791
- result_text = scrolledtext.ScrolledText(ping_window, wrap=tk.WORD, height=10)
792
- result_text.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
793
-
794
- def perform_ping():
795
- host = host_entry.get().strip()
796
- if not host:
797
- messagebox.showerror("Error", "Please enter a host")
798
- return
799
-
800
- try:
801
- count = int(count_entry.get())
802
- if count < 1 or count > 10:
803
- raise ValueError
804
- except ValueError:
805
- messagebox.showerror("Error", "Please enter a count between 1 and 10")
806
- return
807
-
808
- try:
809
- result_text.delete(1.0, tk.END)
810
- result_text.insert(tk.END, f"Pinging {host} {count} times...\n\n")
811
- ping_window.update()
812
-
813
- # Extract host from URL if needed
814
- if host.startswith(('http://', 'https://')):
815
- host = urlparse(host).netloc
816
-
817
- # Platform-specific ping command
818
- if sys.platform == "win32":
819
- cmd = ["ping", "-n", str(count), host]
820
- else:
821
- cmd = ["ping", "-c", str(count), host]
822
-
823
- process = subprocess.Popen(
824
- cmd,
825
- stdout=subprocess.PIPE,
826
- stderr=subprocess.PIPE,
827
- universal_newlines=True
828
- )
829
-
830
- for line in process.stdout:
831
- result_text.insert(tk.END, line)
832
- ping_window.update()
833
-
834
- result_text.insert(tk.END, "\nPing complete.\n")
835
-
836
- except Exception as e:
837
- messagebox.showerror("Error", f"Ping failed: {str(e)}")
838
-
839
- ttk.Button(ping_window, text="Ping", command=perform_ping).pack(pady=5)
840
-
841
- def open_documentation(self):
842
- """Open documentation in browser"""
843
- try:
844
- webbrowser.open("https://github.com/yourusername/vulnerability-scanner/docs")
845
- except:
846
- messagebox.showerror("Error", "Could not open documentation")
847
-
848
- def show_about(self):
849
- """Show about dialog"""
850
- about_window = tk.Toplevel(self.root)
851
- about_window.title("About")
852
- about_window.geometry("400x300")
853
-
854
- try:
855
- icon_path = os.path.join(os.path.dirname(__file__), "icon.ico")
856
- if os.path.exists(icon_path):
857
- img = Image.open(icon_path)
858
- img = img.resize((64, 64), Image.LANCZOS)
859
- tk_icon = ImageTk.PhotoImage(img)
860
- icon_label = ttk.Label(about_window, image=tk_icon)
861
- icon_label.image = tk_icon # Keep a reference
862
- icon_label.pack(pady=10)
863
- except Exception:
864
- pass
865
-
866
- ttk.Label(about_window, text="Advanced Vulnerability Scanner Pro",
867
- font=('Arial', 12, 'bold')).pack()
868
-
869
- ttk.Label(about_window, text="Version 2.0").pack()
870
- ttk.Label(about_window, text="\nA comprehensive security scanning tool").pack()
871
- ttk.Label(about_window, text="for identifying vulnerabilities in web applications.").pack()
872
-
873
- ttk.Label(about_window, text="\n© 2023 Security Tools Inc.").pack()
874
-
875
- btn_frame = ttk.Frame(about_window)
876
- btn_frame.pack(pady=10)
877
-
878
- ttk.Button(btn_frame, text="Close", command=about_window.destroy).pack()
879
-
880
- def start_scan(self):
881
- target = self.url_entry.get().strip()
882
- if not target:
883
- messagebox.showerror("Error", "Please enter a target URL or IP")
884
- return
885
-
886
- # Clear previous results
887
- for item in self.results_tree.get_children():
888
- self.results_tree.delete(item)
889
- for item in self.files_tree.get_children():
890
- self.files_tree.delete(item)
891
- for item in self.network_tree.get_children():
892
- self.network_tree.delete(item)
893
- self.details_text.delete(1.0, tk.END)
894
-
895
- self.scan_btn.config(state=tk.DISABLED)
896
-
897
- scan_thread = threading.Thread(target=self.run_scan, args=(target,), daemon=True)
898
- scan_thread.start()
899
-
900
- def run_scan(self, target):
901
- try:
902
- # Check if target is an IP address
903
- try:
904
- ipaddress.ip_address(target)
905
- is_ip = True
906
- except ValueError:
907
- is_ip = False
908
-
909
- if not is_ip and not target.startswith(('http://', 'https://')):
910
- target = f"http://{target}"
911
-
912
- parsed_url = urlparse(target) if not is_ip else None
913
- domain = parsed_url.netloc if not is_ip else target
914
- base_url = f"{parsed_url.scheme}://{domain}" if not is_ip else None
915
-
916
- # Calculate total steps for progress
917
- total_steps = sum([
918
- self.sql_var.get(),
919
- self.xss_var.get(),
920
- self.headers_var.get(),
921
- self.ssl_var.get(),
922
- self.csrf_var.get(),
923
- self.dir_trav_var.get(),
924
- self.cmdi_var.get(),
925
- self.crawl_var.get(),
926
- self.file_exp_var.get(),
927
- self.ssrf_var.get(),
928
- self.xxe_var.get(),
929
- self.idor_var.get(),
930
- self.port_scan_var.get(),
931
- self.os_detect_var.get(),
932
- self.service_scan_var.get()
933
- ])
934
- current_step = 0
935
-
936
- # Network scans (work for both IP and URL)
937
- if self.port_scan_var.get() and self.nm:
938
- self.update_status("Performing port scan...")
939
- self.port_scan(domain)
940
- current_step += 1
941
- self.update_progress(current_step, total_steps)
942
-
943
- if self.os_detect_var.get() and self.nm:
944
- self.update_status("Detecting OS...")
945
- self.os_detection(domain)
946
- current_step += 1
947
- self.update_progress(current_step, total_steps)
948
-
949
- if self.service_scan_var.get() and self.nm:
950
- self.update_status("Scanning services...")
951
- self.service_scan(domain)
952
- current_step += 1
953
- self.update_progress(current_step, total_steps)
954
-
955
- # Web application scans (only for URLs)
956
- if not is_ip:
957
- # SSL/TLS Check
958
- if self.ssl_var.get():
959
- self.update_status("Checking SSL/TLS configuration...")
960
- self.check_ssl(domain)
961
- current_step += 1
962
- self.update_progress(current_step, total_steps)
963
-
964
- # Headers Analysis
965
- if self.headers_var.get():
966
- self.update_status("Analyzing HTTP headers...")
967
- self.analyze_headers(target)
968
- current_step += 1
969
- self.update_progress(current_step, total_steps)
970
-
971
- # SQL Injection Test
972
- if self.sql_var.get():
973
- self.update_status("Testing for SQL Injection...")
974
- self.test_sql_injection(target)
975
- current_step += 1
976
- self.update_progress(current_step, total_steps)
977
-
978
- # XSS Test
979
- if self.xss_var.get():
980
- self.update_status("Testing for XSS vulnerabilities...")
981
- self.test_xss(target)
982
- current_step += 1
983
- self.update_progress(current_step, total_steps)
984
-
985
- # CSRF Test
986
- if self.csrf_var.get():
987
- self.update_status("Checking for CSRF vulnerabilities...")
988
- self.test_csrf(target)
989
- current_step += 1
990
- self.update_progress(current_step, total_steps)
991
-
992
- # Directory Traversal Test
993
- if self.dir_trav_var.get():
994
- self.update_status("Testing for Directory Traversal...")
995
- self.test_directory_traversal(target)
996
- current_step += 1
997
- self.update_progress(current_step, total_steps)
998
-
999
- # Command Injection Test
1000
- if self.cmdi_var.get():
1001
- self.update_status("Testing for Command Injection...")
1002
- self.test_command_injection(target)
1003
- current_step += 1
1004
- self.update_progress(current_step, total_steps)
1005
-
1006
- # SSRF Test
1007
- if self.ssrf_var.get():
1008
- self.update_status("Testing for SSRF vulnerabilities...")
1009
- self.test_ssrf(target)
1010
- current_step += 1
1011
- self.update_progress(current_step, total_steps)
1012
-
1013
- # XXE Test
1014
- if self.xxe_var.get():
1015
- self.update_status("Testing for XXE vulnerabilities...")
1016
- self.test_xxe(target)
1017
- current_step += 1
1018
- self.update_progress(current_step, total_steps)
1019
-
1020
- # IDOR Test
1021
- if self.idor_var.get():
1022
- self.update_status("Testing for IDOR vulnerabilities...")
1023
- self.test_idor(target)
1024
- current_step += 1
1025
- self.update_progress(current_step, total_steps)
1026
-
1027
- # Crawl Pages
1028
- if self.crawl_var.get():
1029
- self.update_status("Crawling website for links...")
1030
- self.crawl_pages(base_url)
1031
- current_step += 1
1032
- self.update_progress(current_step, total_steps)
1033
-
1034
- # Check for exposed files
1035
- if self.file_exp_var.get():
1036
- self.update_status("Checking for exposed files...")
1037
- self.check_exposed_files(base_url)
1038
- current_step += 1
1039
- self.update_progress(current_step, total_steps)
1040
-
1041
- self.update_status("Scan completed successfully!")
1042
- messagebox.showinfo("Scan Complete", "Vulnerability scan completed!")
1043
-
1044
- except Exception as e:
1045
- self.add_result("High", "Scan Error", f"Error during scan: {str(e)}")
1046
- self.update_status(f"Error: {str(e)}")
1047
-
1048
- finally:
1049
- self.scan_btn.config(state=tk.NORMAL)
1050
-
1051
- def port_scan(self, host):
1052
- """Perform port scan using nmap"""
1053
- try:
1054
- if not self.nm:
1055
- self.add_result("Medium", "Port Scan", "Nmap is not installed. Port scanning disabled.")
1056
- return
1057
-
1058
- self.nm.scan(hosts=host, arguments='-F') # Fast scan (top 100 ports)
1059
-
1060
- if host not in self.nm.all_hosts():
1061
- self.add_result("Medium", "Port Scan", "Host not reachable for port scanning")
1062
- return
1063
-
1064
- open_ports = []
1065
-
1066
- for proto in self.nm[host].all_protocols():
1067
- ports = self.nm[host][proto].keys()
1068
- for port in ports:
1069
- port_info = self.nm[host][proto][port]
1070
- state = port_info['state']
1071
- service = port_info['name']
1072
- version = port_info.get('version', 'unknown')
1073
-
1074
- if state == 'open':
1075
- open_ports.append((service, port, state, version))
1076
- self.network_tree.insert('', tk.END,
1077
- values=(service, port, state, version),
1078
- tags=(state,))
1079
-
1080
- if open_ports:
1081
- self.add_result("Medium", "Open Ports",
1082
- f"Found {len(open_ports)} open ports on {host}")
1083
- else:
1084
- self.add_result("Low", "Port Scan", "No open ports found")
1085
-
1086
- self.details_text.insert(tk.END, f"\nPort Scan Results for {host}:\n")
1087
- self.details_text.insert(tk.END, f"Scanned {len(self.nm[host].all_ports())} ports\n")
1088
- self.details_text.insert(tk.END, f"Found {len(open_ports)} open ports\n\n")
1089
-
1090
- except Exception as e:
1091
- self.add_result("Medium", "Port Scan Error", f"Port scan failed: {str(e)}")
1092
-
1093
- def os_detection(self, host):
1094
- """Perform OS detection using nmap"""
1095
- try:
1096
- if not self.nm:
1097
- self.add_result("Medium", "OS Detection", "Nmap is not installed. OS detection disabled.")
1098
- return
1099
-
1100
- self.nm.scan(hosts=host, arguments='-O') # OS detection scan
1101
-
1102
- if host not in self.nm.all_hosts():
1103
- self.add_result("Medium", "OS Detection", "Host not reachable for OS detection")
1104
- return
1105
-
1106
- os_info = self.nm[host].get('osmatch', [])
1107
-
1108
- if os_info:
1109
- best_guess = os_info[0]
1110
- os_name = best_guess['name']
1111
- accuracy = best_guess['accuracy']
1112
-
1113
- self.add_result("Low", "OS Detection",
1114
- f"Likely OS: {os_name} (Accuracy: {accuracy}%)")
1115
-
1116
- self.details_text.insert(tk.END, f"\nOS Detection Results for {host}:\n")
1117
- self.details_text.insert(tk.END, f"Best guess: {os_name} ({accuracy}% accuracy)\n")
1118
-
1119
- if len(os_info) > 1:
1120
- self.details_text.insert(tk.END, "\nOther possible matches:\n")
1121
- for match in os_info[1:]:
1122
- self.details_text.insert(tk.END, f"- {match['name']} ({match['accuracy']}%)\n")
1123
- else:
1124
- self.add_result("Low", "OS Detection", "Could not determine operating system")
1125
- self.details_text.insert(tk.END, f"\nOS Detection Results for {host}:\n")
1126
- self.details_text.insert(tk.END, "Could not determine operating system\n")
1127
-
1128
- except Exception as e:
1129
- self.add_result("Medium", "OS Detection Error", f"OS detection failed: {str(e)}")
1130
-
1131
- def service_scan(self, host):
1132
- """Perform service version detection using nmap"""
1133
- try:
1134
- if not self.nm:
1135
- self.add_result("Medium", "Service Scan", "Nmap is not installed. Service scan disabled.")
1136
- return
1137
-
1138
- self.nm.scan(hosts=host, arguments='-sV') # Service version scan
1139
-
1140
- if host not in self.nm.all_hosts():
1141
- self.add_result("Medium", "Service Scan", "Host not reachable for service scan")
1142
- return
1143
-
1144
- service_info = []
1145
-
1146
- for proto in self.nm[host].all_protocols():
1147
- ports = self.nm[host][proto].keys()
1148
- for port in ports:
1149
- port_data = self.nm[host][proto][port]
1150
- if port_data['state'] == 'open':
1151
- service_info.append((
1152
- port_data['name'],
1153
- port,
1154
- port_data['state'],
1155
- port_data.get('product', '') + ' ' + port_data.get('version', '')
1156
- ))
1157
-
1158
- if service_info:
1159
- self.add_result("Low", "Service Scan",
1160
- f"Found {len(service_info)} services on {host}")
1161
-
1162
- self.details_text.insert(tk.END, f"\nService Scan Results for {host}:\n")
1163
- for service in service_info:
1164
- self.details_text.insert(tk.END,
1165
- f"Port {service[1]}: {service[0]} - {service[3]}\n")
1166
- else:
1167
- self.add_result("Low", "Service Scan", "No services detected")
1168
- self.details_text.insert(tk.END, f"\nService Scan Results for {host}:\n")
1169
- self.details_text.insert(tk.END, "No services detected\n")
1170
-
1171
- except Exception as e:
1172
- self.add_result("Medium", "Service Scan Error", f"Service scan failed: {str(e)}")
1173
-
1174
- def check_exposed_files(self, base_url):
1175
- """Check for exposed files on hosting platforms like Vercel, Netlify, etc."""
1176
- try:
1177
- parsed = urlparse(base_url)
1178
- domain = parsed.netloc
1179
- scheme = parsed.scheme
1180
-
1181
- # Common exposed files and directories
1182
- common_files = [
1183
- # Configuration files
1184
- "_config.yml", "config.yml", "config.json", "package.json",
1185
- "composer.json", "package-lock.json", "yarn.lock",
1186
- "dockerfile", "docker-compose.yml", ".env", ".env.example",
1187
- ".gitignore", ".htaccess", "robots.txt", "sitemap.xml",
1188
-
1189
- # Platform specific files
1190
- "vercel.json", "netlify.toml", "now.json", "firebase.json",
1191
- "_redirects", "_headers",
1192
-
1193
- # Source code files
1194
- "index.php", "index.html", "main.js", "app.js", "server.js",
1195
- "style.css", "main.css", "app.css", "README.md", "LICENSE",
1196
-
1197
- # Backup files
1198
- "backup.zip", "backup.tar.gz", "backup.sql", "dump.sql",
1199
- "database.sql", "backup.rar", "backup.db",
1200
-
1201
- # Admin interfaces
1202
- "admin.php", "admin.html", "wp-admin", "administrator",
1203
- "login.php", "login.html", "wp-login.php",
1204
-
1205
- # API endpoints
1206
- "api/v1", "graphql", "graphiql", "api.json", "swagger.json",
1207
- "openapi.json", "api.php", "api.js",
1208
-
1209
- # Log files
1210
- "logs", "error.log", "access.log", "debug.log"
1211
- ]
1212
-
1213
- # Platform-specific file patterns
1214
- platform_patterns = {
1215
- "vercel": [
1216
- "/_next/static/chunks/pages/",
1217
- "/_next/static/development/",
1218
- "/_next/static/css/",
1219
- "/api/",
1220
- "/public/"
1221
- ],
1222
- "netlify": [
1223
- "/.netlify/functions/",
1224
- "/public/",
1225
- "/static/",
1226
- "/dist/"
1227
- ],
1228
- "github": [
1229
- "/.github/workflows/",
1230
- "/.github/",
1231
- "/actions/"
1232
- ],
1233
- "firebase": [
1234
- "/__/firebase/",
1235
- "/__/auth/",
1236
- "/__/database/"
1237
- ]
1238
- }
1239
-
1240
- # Check if the domain matches known hosting platforms
1241
- platform = None
1242
- if "vercel.app" in domain:
1243
- platform = "vercel"
1244
- elif "netlify.app" in domain:
1245
- platform = "netlify"
1246
- elif "github.io" in domain:
1247
- platform = "github"
1248
- elif "firebaseapp.com" in domain or "web.app" in domain:
1249
- platform = "firebase"
1250
-
1251
- found_files = []
1252
-
1253
- # Check common files
1254
- for file in common_files:
1255
- test_url = f"{scheme}://{domain}/{file}"
1256
- try:
1257
- response = requests.head(test_url, timeout=5, allow_redirects=False)
1258
- if response.status_code == 200:
1259
- size = response.headers.get('content-length', 'unknown')
1260
- found_files.append(("file", test_url, "200 OK", size))
1261
- self.files_tree.insert('', tk.END,
1262
- values=("File", test_url, "200 OK", size))
1263
- except:
1264
- continue
1265
-
1266
- # Check platform-specific patterns if platform is detected
1267
- if platform:
1268
- for pattern in platform_patterns.get(platform, []):
1269
- test_url = f"{scheme}://{domain}{pattern}"
1270
- try:
1271
- response = requests.head(test_url, timeout=5, allow_redirects=False)
1272
- if response.status_code == 200:
1273
- size = response.headers.get('content-length', 'unknown')
1274
- found_files.append(("directory", test_url, "200 OK", size))
1275
- self.files_tree.insert('', tk.END,
1276
- values=("Directory", test_url, "200 OK", size))
1277
- except:
1278
- continue
1279
-
1280
- # Special check for .git directory
1281
- test_url = f"{scheme}://{domain}/.git/"
1282
- try:
1283
- response = requests.head(test_url, timeout=5, allow_redirects=False)
1284
- if response.status_code in (200, 403):
1285
- size = response.headers.get('content-length', 'unknown')
1286
- found_files.append(("directory", test_url, f"{response.status_code}", size))
1287
- self.files_tree.insert('', tk.END,
1288
- values=("Git", test_url, f"{response.status_code}", size))
1289
- except:
1290
- pass
1291
-
1292
- # Check for exposed source code
1293
- self.check_exposed_source_code(base_url)
1294
-
1295
- if found_files:
1296
- self.add_result("Medium", "Exposed Files", f"Found {len(found_files)} exposed files/directories")
1297
- else:
1298
- self.add_result("Low", "Exposed Files", "No obvious exposed files found")
1299
-
1300
- self.details_text.insert(tk.END, f"\nExposed Files Check:\nFound {len(found_files)} files/directories\n")
1301
-
1302
- except Exception as e:
1303
- self.add_result("Medium", "Exposed Files Error", f"Exposed files check failed: {str(e)}")
1304
-
1305
- def check_exposed_source_code(self, base_url):
1306
- """Check for exposed source code files on platforms like Vercel, Netlify"""
1307
- try:
1308
- parsed = urlparse(base_url)
1309
- domain = parsed.netloc
1310
- scheme = parsed.scheme
1311
-
1312
- # Common source code patterns for Vercel/Next.js
1313
- vercel_patterns = [
1314
- "/_next/static/chunks/pages/_app.js",
1315
- "/_next/static/chunks/main.js",
1316
- "/_next/static/chunks/webpack.js",
1317
- "/_next/static/css/styles.chunk.css"
1318
- ]
1319
-
1320
- # Common source code patterns for Netlify
1321
- netlify_patterns = [
1322
- "/static/js/main.chunk.js",
1323
- "/static/js/runtime-main.js",
1324
- "/static/css/main.chunk.css"
1325
- ]
1326
-
1327
- # Check if the domain matches known hosting platforms
1328
- if "vercel.app" in domain:
1329
- for pattern in vercel_patterns:
1330
- test_url = f"{scheme}://{domain}{pattern}"
1331
- try:
1332
- response = requests.head(test_url, timeout=5, allow_redirects=False)
1333
- if response.status_code == 200:
1334
- size = response.headers.get('content-length', 'unknown')
1335
- self.files_tree.insert('', tk.END,
1336
- values=("Source", test_url, "200 OK", size))
1337
- except:
1338
- continue
1339
-
1340
- elif "netlify.app" in domain:
1341
- for pattern in netlify_patterns:
1342
- test_url = f"{scheme}://{domain}{pattern}"
1343
- try:
1344
- response = requests.head(test_url, timeout=5, allow_redirects=False)
1345
- if response.status_code == 200:
1346
- size = response.headers.get('content-length', 'unknown')
1347
- self.files_tree.insert('', tk.END,
1348
- values=("Source", test_url, "200 OK", size))
1349
- except:
1350
- continue
1351
-
1352
- # Special check for source map files
1353
- source_map_patterns = [
1354
- "/static/js/main.js.map",
1355
- "/static/js/bundle.js.map",
1356
- "/static/js/vendor.js.map",
1357
- "/app.js.map",
1358
- "/main.js.map"
1359
- ]
1360
-
1361
- for pattern in source_map_patterns:
1362
- test_url = f"{scheme}://{domain}{pattern}"
1363
- try:
1364
- response = requests.head(test_url, timeout=5, allow_redirects=False)
1365
- if response.status_code == 200:
1366
- size = response.headers.get('content-length', 'unknown')
1367
- self.files_tree.insert('', tk.END,
1368
- values=("Source Map", test_url, "200 OK", size))
1369
- except:
1370
- continue
1371
-
1372
- except Exception as e:
1373
- self.add_result("Medium", "Source Code Check Error", f"Source code check failed: {str(e)}")
1374
-
1375
- def update_status(self, message):
1376
- self.status.config(text=message)
1377
- self.root.update()
1378
-
1379
- def update_progress(self, current, total):
1380
- progress = (current / total) * 100
1381
- self.progress['value'] = progress
1382
- self.root.update()
1383
-
1384
- def add_result(self, severity, vuln_type, details):
1385
- tag = severity.lower()
1386
- self.results_tree.insert('', tk.END, values=(severity, vuln_type, details), tags=(tag,))
1387
- self.details_text.insert(tk.END, f"[{severity}] {vuln_type}: {details}\n\n")
1388
- self.details_text.see(tk.END)
1389
- self.root.update()
1390
-
1391
- def check_ssl(self, domain):
1392
- try:
1393
- context = ssl.create_default_context()
1394
- with socket.create_connection((domain, 443)) as sock:
1395
- with context.wrap_socket(sock, server_hostname=domain) as ssock:
1396
- cert = ssock.getpeercert()
1397
-
1398
- # Check certificate expiration
1399
- expiry_date = datetime.datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z')
1400
- days_remaining = (expiry_date - datetime.datetime.now()).days
1401
-
1402
- details = "SSL Certificate:\nIssuer: {}\n".format(cert['issuer'][1][0][1])
1403
- details += "Subject: {}\n".format(cert['subject'][1][0][1])
1404
- details += "Serial Number: {}\n".format(cert['serialNumber'])
1405
- details += "Valid From: {}\n".format(cert['notBefore'])
1406
- details += "Valid Until: {} ({} days remaining)\n".format(cert['notAfter'], days_remaining)
1407
-
1408
- # Check certificate algorithms
1409
- details += "\nCertificate Algorithms:\n"
1410
- details += "Signature Algorithm: {}\n".format(cert['signatureAlgorithm'])
1411
-
1412
- # Check for weak protocols
1413
- weak_protocols = self.detect_weak_ssl_protocols(domain)
1414
- if weak_protocols:
1415
- details += "\nWeak Protocols: {}\n".format(', '.join(weak_protocols))
1416
- self.add_result("High", "Weak SSL Protocols", f"Server supports: {', '.join(weak_protocols)}")
1417
-
1418
- # Check for weak ciphers
1419
- weak_ciphers = self.detect_weak_ciphers(domain)
1420
- if weak_ciphers:
1421
- details += "\nWeak Ciphers:\n"
1422
- for cipher in weak_ciphers:
1423
- details += f"- {cipher}\n"
1424
- self.add_result("High", "Weak SSL Ciphers", f"Server supports weak ciphers: {', '.join(weak_ciphers[:3])}...")
1425
-
1426
- if days_remaining < 30:
1427
- self.add_result("High", "SSL Expiry", f"Certificate expires in {days_remaining} days")
1428
- elif days_remaining < 90:
1429
- self.add_result("Medium", "SSL Expiry", f"Certificate expires in {days_remaining} days")
1430
-
1431
- self.details_text.insert(tk.END, details + "\n")
1432
-
1433
- except Exception as e:
1434
- self.add_result("High", "SSL Error", f"SSL verification failed: {str(e)}")
1435
-
1436
- def detect_weak_ssl_protocols(self, domain):
1437
- weak_protocols = []
1438
- protocols = {
1439
- 'SSLv2': ssl.PROTOCOL_SSLv2,
1440
- 'SSLv3': ssl.PROTOCOL_SSLv3,
1441
- 'TLSv1': ssl.PROTOCOL_TLSv1,
1442
- 'TLSv1.1': ssl.PROTOCOL_TLSv1_1
1443
- }
1444
-
1445
- for name, proto in protocols.items():
1446
- try:
1447
- context = ssl.SSLContext(proto)
1448
- with socket.create_connection((domain, 443)) as sock:
1449
- with context.wrap_socket(sock, server_hostname=domain):
1450
- weak_protocols.append(name)
1451
- except:
1452
- continue
1453
-
1454
- return weak_protocols
1455
-
1456
- def detect_weak_ciphers(self, domain):
1457
- weak_ciphers = [
1458
- 'DES', '3DES', 'RC4', 'RC2', 'IDEA', 'SEED',
1459
- 'MD5', 'SHA1', 'NULL', 'ANON', 'ADH', 'EXP',
1460
- 'CBC', 'CAMELLIA', 'PSK', 'SRP'
1461
- ]
1462
-
1463
- detected = []
1464
-
1465
- try:
1466
- context = ssl.create_default_context()
1467
- with socket.create_connection((domain, 443)) as sock:
1468
- with context.wrap_socket(sock, server_hostname=domain) as ssock:
1469
- cipher = ssock.cipher()
1470
- if cipher:
1471
- for weak in weak_ciphers:
1472
- if weak in cipher[0]:
1473
- detected.append(cipher[0])
1474
- break
1475
- except:
1476
- pass
1477
-
1478
- return detected
1479
-
1480
- def analyze_headers(self, url):
1481
- try:
1482
- response = requests.get(url, timeout=10, allow_redirects=True)
1483
- headers = response.headers
1484
-
1485
- details = "Security Headers Analysis:\n"
1486
- missing_headers = []
1487
- security_headers = {
1488
- 'X-XSS-Protection': '1; mode=block',
1489
- 'X-Content-Type-Options': 'nosniff',
1490
- 'X-Frame-Options': ['DENY', 'SAMEORIGIN'],
1491
- 'Content-Security-Policy': '',
1492
- 'Strict-Transport-Security': '',
1493
- 'Referrer-Policy': 'no-referrer',
1494
- 'Feature-Policy': '',
1495
- 'Permissions-Policy': ''
1496
- }
1497
-
1498
- for header, expected in security_headers.items():
1499
- if header not in headers:
1500
- missing_headers.append(header)
1501
- elif expected and isinstance(expected, list) and headers[header] not in expected:
1502
- self.add_result("Medium", f"Misconfigured {header}",
1503
- f"Expected one of {expected}, got {headers[header]}")
1504
- elif expected and isinstance(expected, str) and headers[header] != expected:
1505
- self.add_result("Medium", f"Misconfigured {header}",
1506
- f"Expected {expected}, got {headers[header]}")
1507
-
1508
- if missing_headers:
1509
- self.add_result("Medium", "Missing Security Headers",
1510
- f"Missing: {', '.join(missing_headers)}")
1511
-
1512
- # Check for server information disclosure
1513
- if 'server' in headers:
1514
- self.add_result("Low", "Server Disclosure", f"Server header: {headers['server']}")
1515
-
1516
- # Check for CORS misconfiguration
1517
- if 'access-control-allow-origin' in headers and headers['access-control-allow-origin'] == '*':
1518
- self.add_result("Medium", "Permissive CORS", "Access-Control-Allow-Origin is set to '*'")
1519
-
1520
- # Check for clickjacking protection
1521
- if 'x-frame-options' not in headers:
1522
- self.add_result("Medium", "Clickjacking", "Missing X-Frame-Options header")
1523
-
1524
- # Check for content type sniffing protection
1525
- if 'x-content-type-options' not in headers:
1526
- self.add_result("Low", "Content Type Sniffing", "Missing X-Content-Type-Options header")
1527
-
1528
- self.details_text.insert(tk.END, details + "\n")
1529
-
1530
- except Exception as e:
1531
- self.add_result("Medium", "Headers Error", f"Header analysis failed: {str(e)}")
1532
-
1533
- def test_sql_injection(self, url):
1534
- payloads = [
1535
- "'", "\"", "' OR '1'='1", "' OR 1=1--",
1536
- "' OR 1=1#", "' OR 1=1/*", "' UNION SELECT null,version()--",
1537
- "' UNION SELECT username,password FROM users--",
1538
- "1 AND 1=1", "1 AND 1=2", "1' AND SLEEP(5)--",
1539
- "1' OR IF(1=1,SLEEP(5),0)--"
1540
- ]
1541
-
1542
- try:
1543
- parsed = urlparse(url)
1544
- params = parse_qs(parsed.query)
1545
-
1546
- if not params:
1547
- self.add_result("Info", "SQLi Test", "No parameters to test")
1548
- return
1549
-
1550
- vulnerable = False
1551
- details = "SQL Injection Tests:\n"
1552
-
1553
- for param in params:
1554
- for payload in payloads:
1555
- test_url = url.replace(f"{param}={params[param][0]}", f"{param}={payload}")
1556
- try:
1557
- start_time = time.time()
1558
- response = requests.get(test_url, timeout=5)
1559
- elapsed = time.time() - start_time
1560
-
1561
- if self.detect_sql_errors(response.text):
1562
- vulnerable = True
1563
- details += f"Potential SQLi in {param} with payload: {payload} (Error-based)\n"
1564
- break
1565
- elif elapsed > 4: # Time-based detection
1566
- vulnerable = True
1567
- details += f"Potential SQLi in {param} with payload: {payload} (Time-based, {elapsed:.2f}s)\n"
1568
- break
1569
- elif payload in response.text: # Boolean-based detection
1570
- vulnerable = True
1571
- details += f"Potential SQLi in {param} with payload: {payload} (Boolean-based)\n"
1572
- break
1573
- except:
1574
- continue
1575
-
1576
- if vulnerable:
1577
- self.add_result("High", "SQL Injection", "Potential SQLi vulnerabilities detected")
1578
- else:
1579
- self.add_result("Low", "SQL Injection", "No obvious SQLi vulnerabilities found")
1580
-
1581
- self.details_text.insert(tk.END, details + "\n")
1582
-
1583
- except Exception as e:
1584
- self.add_result("Medium", "SQLi Error", f"SQLi test failed: {str(e)}")
1585
-
1586
- def detect_sql_errors(self, content):
1587
- errors = [
1588
- "SQL syntax", "MySQL server", "ORA-", "syntax error",
1589
- "unclosed quotation mark", "Microsoft OLE DB Provider",
1590
- "ODBC Driver", "PostgreSQL", "SQLite", "MariaDB",
1591
- "SQL error", "database error", "query failed",
1592
- "syntax error", "unknown column", "table not found"
1593
- ]
1594
- return any(error.lower() in content.lower() for error in errors)
1595
-
1596
- def test_xss(self, url):
1597
- payloads = [
1598
- "<script>alert(1)</script>",
1599
- "<img src=x onerror=alert(1)>",
1600
- "\"><script>alert(1)</script>",
1601
- "javascript:alert(1)",
1602
- "onmouseover=alert(1)",
1603
- "onload=alert(1)",
1604
- "onfocus=alert(1)",
1605
- "svg/onload=alert(1)",
1606
- "alert`1`",
1607
- "eval('alert(1)')"
1608
- ]
1609
-
1610
- try:
1611
- parsed = urlparse(url)
1612
- params = parse_qs(parsed.query)
1613
-
1614
- if not params:
1615
- self.add_result("Info", "XSS Test", "No parameters to test")
1616
- return
1617
-
1618
- vulnerable = False
1619
- details = "XSS Tests:\n"
1620
-
1621
- for param in params:
1622
- for payload in payloads:
1623
- test_url = url.replace(f"{param}={params[param][0]}", f"{param}={payload}")
1624
- try:
1625
- response = requests.get(test_url, timeout=5)
1626
- if payload in response.text:
1627
- vulnerable = True
1628
- details += f"Potential XSS in {param} with payload: {payload}\n"
1629
- break
1630
- except:
1631
- continue
1632
-
1633
- if vulnerable:
1634
- self.add_result("High", "XSS", "Potential XSS vulnerabilities detected")
1635
- else:
1636
- self.add_result("Low", "XSS", "No obvious XSS vulnerabilities found")
1637
-
1638
- self.details_text.insert(tk.END, details + "\n")
1639
-
1640
- except Exception as e:
1641
- self.add_result("Medium", "XSS Error", f"XSS test failed: {str(e)}")
1642
-
1643
- def test_csrf(self, url):
1644
- try:
1645
- response = requests.get(url, timeout=10)
1646
- soup = BeautifulSoup(response.text, 'html.parser')
1647
- forms = soup.find_all('form')
1648
-
1649
- vulnerable = False
1650
- details = "CSRF Tests:\n"
1651
-
1652
- for form in forms:
1653
- if not form.find('input', {'name': 'csrf_token'}) and \
1654
- not form.find('input', {'name': 'csrfmiddlewaretoken'}) and \
1655
- not form.find('input', {'name': '_token'}):
1656
- vulnerable = True
1657
- action = form.get('action', 'current URL')
1658
- method = form.get('method', 'GET').upper()
1659
- details += f"Form without CSRF protection found (Action: {action}, Method: {method})\n"
1660
- break
1661
-
1662
- if vulnerable:
1663
- self.add_result("Medium", "CSRF", "Forms without CSRF protection detected")
1664
- else:
1665
- self.add_result("Low", "CSRF", "No obvious CSRF vulnerabilities found")
1666
-
1667
- self.details_text.insert(tk.END, details + "\n")
1668
-
1669
- except Exception as e:
1670
- self.add_result("Medium", "CSRF Error", f"CSRF test failed: {str(e)}")
1671
-
1672
- def test_directory_traversal(self, url):
1673
- payloads = [
1674
- "../../../../etc/passwd",
1675
- "..%2F..%2F..%2Fetc%2Fpasswd",
1676
- "....//....//etc/passwd",
1677
- "%2e%2e%2fetc%2fpasswd",
1678
- "..\\..\\..\\windows\\win.ini",
1679
- "%2e%2e%5cwindows%5cwin.ini"
1680
- ]
1681
-
1682
- try:
1683
- parsed = urlparse(url)
1684
- base_url = f"{parsed.scheme}://{parsed.netloc}"
1685
- paths = [p for p in parsed.path.split('/') if p]
1686
-
1687
- vulnerable = False
1688
- details = "Directory Traversal Tests:\n"
1689
-
1690
- for payload in payloads:
1691
- test_url = f"{base_url}/{payload}"
1692
- try:
1693
- response = requests.get(test_url, timeout=5)
1694
- if "root:" in response.text or "bin:" in response.text or "[fonts]" in response.text:
1695
- vulnerable = True
1696
- details += f"Potential directory traversal with payload: {payload}\n"
1697
- break
1698
- except:
1699
- continue
1700
-
1701
- if vulnerable:
1702
- self.add_result("High", "Directory Traversal", "Potential directory traversal vulnerabilities detected")
1703
- else:
1704
- self.add_result("Low", "Directory Traversal", "No obvious directory traversal vulnerabilities found")
1705
-
1706
- self.details_text.insert(tk.END, details + "\n")
1707
-
1708
- except Exception as e:
1709
- self.add_result("Medium", "Dir Traversal Error", f"Directory traversal test failed: {str(e)}")
1710
-
1711
- def test_command_injection(self, url):
1712
- payloads = [
1713
- ";id", "|id", "`id`", "$(id)",
1714
- "|| ping -c 1 localhost", "&& ping -c 1 localhost",
1715
- "| dir", "&& dir", "; dir",
1716
- "| ls", "&& ls", "; ls"
1717
- ]
1718
-
1719
- try:
1720
- parsed = urlparse(url)
1721
- params = parse_qs(parsed.query)
1722
-
1723
- if not params:
1724
- self.add_result("Info", "Command Injection", "No parameters to test")
1725
- return
1726
-
1727
- vulnerable = False
1728
- details = "Command Injection Tests:\n"
1729
-
1730
- for param in params:
1731
- for payload in payloads:
1732
- test_url = url.replace(f"{param}={params[param][0]}", f"{param}={params[param][0]}{payload}")
1733
- try:
1734
- response = requests.get(test_url, timeout=5)
1735
- if "uid=" in response.text or "bytes from" in response.text or "Volume Serial" in response.text:
1736
- vulnerable = True
1737
- details += f"Potential command injection in {param} with payload: {payload}\n"
1738
- break
1739
- except:
1740
- continue
1741
-
1742
- if vulnerable:
1743
- self.add_result("High", "Command Injection", "Potential command injection vulnerabilities detected")
1744
- else:
1745
- self.add_result("Low", "Command Injection", "No obvious command injection vulnerabilities found")
1746
-
1747
- self.details_text.insert(tk.END, details + "\n")
1748
-
1749
- except Exception as e:
1750
- self.add_result("Medium", "CMD Injection Error", f"Command injection test failed: {str(e)}")
1751
-
1752
- def test_ssrf(self, url):
1753
- """Test for Server-Side Request Forgery vulnerabilities"""
1754
- test_servers = [
1755
- "http://169.254.169.254/latest/meta-data/", # AWS metadata
1756
- "http://metadata.google.internal/computeMetadata/v1/", # GCP metadata
1757
- "http://169.254.169.253/latest/meta-data/", # Azure metadata
1758
- "http://localhost:80",
1759
- "http://127.0.0.1:80"
1760
- ]
1761
-
1762
- try:
1763
- parsed = urlparse(url)
1764
- params = parse_qs(parsed.query)
1765
-
1766
- if not params:
1767
- self.add_result("Info", "SSRF Test", "No parameters to test")
1768
- return
1769
-
1770
- vulnerable = False
1771
- details = "SSRF Tests:\n"
1772
-
1773
- for param in params:
1774
- for server in test_servers:
1775
- test_url = url.replace(f"{param}={params[param][0]}", f"{param}={server}")
1776
- try:
1777
- response = requests.get(test_url, timeout=5)
1778
- if response.status_code == 200 and any(
1779
- keyword in response.text.lower()
1780
- for keyword in ["instance-id", "ami-id", "compute", "metadata"]
1781
- ):
1782
- vulnerable = True
1783
- details += f"Potential SSRF in {param} with payload: {server}\n"
1784
- break
1785
- except:
1786
- continue
1787
-
1788
- if vulnerable:
1789
- self.add_result("High", "SSRF", "Potential SSRF vulnerabilities detected")
1790
- else:
1791
- self.add_result("Low", "SSRF", "No obvious SSRF vulnerabilities found")
1792
-
1793
- self.details_text.insert(tk.END, details + "\n")
1794
-
1795
- except Exception as e:
1796
- self.add_result("Medium", "SSRF Error", f"SSRF test failed: {str(e)}")
1797
-
1798
- def test_xxe(self, url):
1799
- """Test for XML External Entity vulnerabilities"""
1800
- xxe_payload = """<?xml version="1.0" encoding="ISO-8859-1"?>
1801
- <!DOCTYPE foo [ <!ELEMENT foo ANY >
1802
- <!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
1803
- <foo>&xxe;</foo>"""
1804
-
1805
- try:
1806
- # First check if the endpoint accepts XML
1807
- response = requests.get(url, timeout=5)
1808
- if "xml" not in response.headers.get('content-type', '').lower():
1809
- self.add_result("Info", "XXE Test", "Endpoint doesn't appear to accept XML")
1810
- return
1811
-
1812
- # Try XXE injection
1813
- headers = {'Content-Type': 'application/xml'}
1814
- response = requests.post(url, data=xxe_payload, headers=headers, timeout=5)
1815
-
1816
- if "root:" in response.text:
1817
- self.add_result("Critical", "XXE", "XML External Entity injection vulnerability detected")
1818
- self.details_text.insert(tk.END, "XXE Test:\nVulnerable to XXE injection\n")
1819
- else:
1820
- self.add_result("Low", "XXE", "No obvious XXE vulnerabilities found")
1821
- self.details_text.insert(tk.END, "XXE Test:\nNo XXE vulnerability detected\n")
1822
-
1823
- except Exception as e:
1824
- self.add_result("Medium", "XXE Error", f"XXE test failed: {str(e)}")
1825
-
1826
- def test_idor(self, url):
1827
- """Test for Insecure Direct Object References"""
1828
- try:
1829
- # This is a simple test that checks for sequential IDs
1830
- # In a real scanner, you'd need more sophisticated tests
1831
- parsed = urlparse(url)
1832
- params = parse_qs(parsed.query)
1833
-
1834
- id_params = [p for p in params if 'id' in p.lower()]
1835
-
1836
- if not id_params:
1837
- self.add_result("Info", "IDOR Test", "No ID parameters to test")
1838
- return
1839
-
1840
- vulnerable = False
1841
- details = "IDOR Tests:\n"
1842
-
1843
- for param in id_params:
1844
- original_id = params[param][0]
1845
- if original_id.isdigit():
1846
- test_id = str(int(original_id) + 1)
1847
- test_url = url.replace(f"{param}={original_id}", f"{param}={test_id}")
1848
-
1849
- try:
1850
- original_response = requests.get(url, timeout=5)
1851
- test_response = requests.get(test_url, timeout=5)
1852
-
1853
- if test_response.status_code == 200 and \
1854
- test_response.text != original_response.text and \
1855
- len(test_response.text) > 100: # Basic check to avoid 404 pages
1856
- vulnerable = True
1857
- details += f"Potential IDOR in {param} by changing {original_id} to {test_id}\n"
1858
- except:
1859
- continue
1860
-
1861
- if vulnerable:
1862
- self.add_result("Medium", "IDOR", "Potential IDOR vulnerabilities detected")
1863
- else:
1864
- self.add_result("Low", "IDOR", "No obvious IDOR vulnerabilities found")
1865
-
1866
- self.details_text.insert(tk.END, details + "\n")
1867
-
1868
- except Exception as e:
1869
- self.add_result("Medium", "IDOR Error", f"IDOR test failed: {str(e)}")
1870
-
1871
- def crawl_pages(self, base_url):
1872
- try:
1873
- visited = set()
1874
- to_visit = {base_url}
1875
- max_pages = self.scan_config['max_pages']
1876
-
1877
- details = "Crawling Results:\n"
1878
-
1879
- while to_visit and len(visited) < max_pages:
1880
- url = to_visit.pop()
1881
- if url in visited:
1882
- continue
1883
-
1884
- try:
1885
- response = requests.get(url, timeout=5)
1886
- visited.add(url)
1887
- details += f"Found: {url} ({response.status_code})\n"
1888
-
1889
- soup = BeautifulSoup(response.text, 'html.parser')
1890
-
1891
- # Find links
1892
- for link in soup.find_all('a', href=True):
1893
- href = link['href']
1894
- if href.startswith('http') and base_url in href and href not in visited:
1895
- to_visit.add(href)
1896
- elif href.startswith('/'):
1897
- absolute = urljoin(base_url, href)
1898
- if absolute not in visited:
1899
- to_visit.add(absolute)
1900
-
1901
- # Check for forms
1902
- forms = soup.find_all('form')
1903
- if forms:
1904
- details += f"Found {len(forms)} forms on {url}\n"
1905
-
1906
- # Check for sensitive content
1907
- sensitive_keywords = [
1908
- 'password', 'secret', 'key', 'token',
1909
- 'admin', 'login', 'credentials', 'backup'
1910
- ]
1911
- page_text = soup.get_text().lower()
1912
- found_keywords = [kw for kw in sensitive_keywords if kw in page_text]
1913
- if found_keywords:
1914
- details += f"Found sensitive keywords: {', '.join(found_keywords)}\n"
1915
-
1916
- self.root.update()
1917
- except:
1918
- continue
1919
-
1920
- details += f"\nCrawled {len(visited)} pages\n"
1921
- self.details_text.insert(tk.END, details + "\n")
1922
-
1923
- if len(visited) >= max_pages:
1924
- self.add_result("Info", "Crawl Limit", f"Limited to {max_pages} pages for demo")
1925
-
1926
- except Exception as e:
1927
- self.add_result("Medium", "Crawl Error", f"Crawling failed: {str(e)}")
1928
-
1929
- if __name__ == "__main__":
1930
- root = tk.Tk()
1931
-
1932
- # Set Windows style if available
1933
- if sys.platform == "win32":
1934
- from ctypes import windll
1935
- windll.shcore.SetProcessDpiAwareness(1)
1936
-
1937
- app = AdvancedVulnerabilityScanner(root)
1938
- root.mainloop()