From aab4288f670cabe1ffc55f194cf5681a3769637c Mon Sep 17 00:00:00 2001 From: Smittix Date: Thu, 8 Jan 2026 13:50:14 +0000 Subject: [PATCH] Reorganize WiFi panels, remove PMKID, add aircrack integration Layout changes: - Security overview now next to network radar - Channel utilization (2.4 GHz and 5 GHz) side by side - Removed network topology panel - Removed PMKID capture panel and functionality Handshake improvements: - Added "Crack with Aircrack-ng" button when handshake is captured - Added /wifi/handshake/crack backend route - Prompts for wordlist path with common defaults - Shows password when found with notification - 5 minute timeout with helpful error message Co-Authored-By: Claude Opus 4.5 --- routes/wifi.py | 77 ++++++++++++++++++ templates/index.html | 188 ++++++++++++++----------------------------- 2 files changed, 139 insertions(+), 126 deletions(-) diff --git a/routes/wifi.py b/routes/wifi.py index d4392ab..e1316cb 100644 --- a/routes/wifi.py +++ b/routes/wifi.py @@ -973,6 +973,83 @@ def stop_pmkid(): return jsonify({'status': 'stopped'}) +@wifi_bp.route('/handshake/crack', methods=['POST']) +def crack_handshake(): + """Crack a captured handshake using aircrack-ng.""" + data = request.json + capture_file = data.get('capture_file', '') + target_bssid = data.get('bssid', '') + wordlist = data.get('wordlist', '') + + # Validate paths to prevent path traversal + if not capture_file.startswith('/tmp/intercept_handshake_') or '..' in capture_file: + return jsonify({'status': 'error', 'message': 'Invalid capture file path'}), 400 + + if '..' in wordlist: + return jsonify({'status': 'error', 'message': 'Invalid wordlist path'}), 400 + + if not os.path.exists(capture_file): + return jsonify({'status': 'error', 'message': 'Capture file not found'}), 404 + + if not os.path.exists(wordlist): + return jsonify({'status': 'error', 'message': 'Wordlist file not found'}), 404 + + if target_bssid and not is_valid_mac(target_bssid): + return jsonify({'status': 'error', 'message': 'Invalid BSSID format'}), 400 + + aircrack_path = get_tool_path('aircrack-ng') + if not aircrack_path: + return jsonify({'status': 'error', 'message': 'aircrack-ng not found'}), 500 + + try: + cmd = [aircrack_path, '-a', '2', '-w', wordlist] + if target_bssid: + cmd.extend(['-b', target_bssid]) + cmd.append(capture_file) + + logger.info(f"Starting aircrack-ng: {' '.join(cmd)}") + + # Run aircrack-ng with a timeout (this could take a while) + result = subprocess.run( + cmd, + capture_output=True, + text=True, + timeout=300 # 5 minute timeout + ) + + output = result.stdout + result.stderr + + # Check if password was found + # Aircrack-ng outputs "KEY FOUND! [ password ]" when successful + if 'KEY FOUND!' in output: + # Extract the password + import re + match = re.search(r'KEY FOUND!\s*\[\s*(.+?)\s*\]', output) + if match: + password = match.group(1) + logger.info(f"Password cracked for {target_bssid}: {password}") + return jsonify({ + 'status': 'success', + 'password': password, + 'bssid': target_bssid + }) + + # Password not found + return jsonify({ + 'status': 'not_found', + 'message': 'Password not in wordlist' + }) + + except subprocess.TimeoutExpired: + return jsonify({ + 'status': 'timeout', + 'message': 'Cracking timed out after 5 minutes. Try a smaller wordlist or use hashcat.' + }) + except Exception as e: + logger.error(f"Crack error: {e}") + return jsonify({'status': 'error', 'message': str(e)}), 500 + + @wifi_bp.route('/networks') def get_wifi_networks(): """Get current list of discovered networks.""" diff --git a/templates/index.html b/templates/index.html index 9a987ae..7cbdfd5 100644 --- a/templates/index.html +++ b/templates/index.html @@ -611,26 +611,6 @@ - - - +
Network Radar
+
+
Security Overview
+
+
+ +
+
+
WPA3: 0
+
WPA2: 0
+
WEP: 0
+
Open: 0
+
+
+
+
Channel Utilization (2.4 GHz)
@@ -1253,31 +1249,7 @@
165
-
-
Security Overview
-
-
- -
-
-
WPA3: 0
-
WPA2: 0
-
WEP: 0
-
Open: 0
-
-
-
- -
-

πŸ•ΈοΈ Network Topology

- -
-
Access Point
-
Client
-
Drone
-
-
- +

πŸ’‘ Channel Recommendation

@@ -5114,8 +5086,7 @@
- - +
`; @@ -5233,6 +5204,24 @@ clearInterval(activeCapture.pollInterval); } document.getElementById('handshakeCount').style.animation = ''; + + // Show crack button in the capture panel + const panel = document.getElementById('captureStatusPanel'); + const existingCrackBtn = panel.querySelector('.crack-btn'); + if (!existingCrackBtn) { + const crackDiv = document.createElement('div'); + crackDiv.style.marginTop = '10px'; + crackDiv.innerHTML = ` + + `; + panel.querySelector('.section') ? panel.querySelector('.section').appendChild(crackDiv) : panel.appendChild(crackDiv); + } + + // Store the captured file for later use + activeCapture.captured = true; + activeCapture.capturedFile = data.file; } else if (data.file_exists) { const sizeKB = (data.file_size / 1024).toFixed(1); statusSpan.textContent = 'Capturing... (' + sizeKB + ' KB, ' + elapsedStr + ')'; @@ -5272,97 +5261,44 @@ activeCapture = null; } - // PMKID Capture - let activePmkid = null; + // Crack handshake with aircrack-ng + function crackHandshake(captureFile, bssid) { + const wordlist = prompt('Enter path to wordlist file:\n\nCommon locations:\n- /usr/share/wordlists/rockyou.txt\n- /usr/share/john/password.lst', '/usr/share/wordlists/rockyou.txt'); - async function capturePmkid(bssid, channel) { - if (!confirm('Start PMKID capture for ' + bssid + '?\n\nThis uses hcxdumptool to capture PMKID without needing clients.\n\n⚠ Only use on networks you own or have authorization to test!')) { + if (!wordlist) { + showInfo('Cracking cancelled'); return; } - const iface = monitorInterface || document.getElementById('wifiInterfaceSelect').value; - if (!iface) { - showError('No monitor interface available. Enable monitor mode first.'); - return; - } + showInfo('Starting aircrack-ng... This may take a while.'); - // Stop any existing scan first - if (isWifiRunning) { - showInfo('Stopping current scan...'); - try { - await fetch('/wifi/scan/stop', {method: 'POST'}); - if (wifiEventSource) { - wifiEventSource.close(); - wifiEventSource = null; - } - setWifiRunning(false); - await new Promise(resolve => setTimeout(resolve, 500)); - } catch (e) { - console.error('Error stopping scan:', e); - } - } - - try { - const response = await fetch('/wifi/pmkid/capture', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ interface: iface, bssid: bssid, channel: channel }) - }); - const data = await response.json(); - - if (data.status === 'started') { - activePmkid = { bssid: bssid, file: data.file, startTime: Date.now() }; - document.getElementById('pmkidPanel').style.display = 'block'; - document.getElementById('pmkidTargetBssid').textContent = bssid; - document.getElementById('pmkidStatus').textContent = 'Capturing...'; - document.getElementById('pmkidStatus').style.color = '#9933ff'; - showInfo('PMKID capture started for ' + bssid); - - // Poll for PMKID - activePmkid.pollInterval = setInterval(checkPmkidStatus, 3000); - } else { - showError('PMKID capture failed: ' + (data.message || 'Unknown error')); - } - } catch (err) { - showError('PMKID capture error: ' + err.message); - console.error('PMKID capture error:', err); - } - } - - function checkPmkidStatus() { - if (!activePmkid) return; - - fetch('/wifi/pmkid/status', { + fetch('/wifi/handshake/crack', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ file: activePmkid.file }) + body: JSON.stringify({ + capture_file: captureFile, + bssid: bssid, + wordlist: wordlist + }) }) .then(r => r.json()) .then(data => { - if (data.pmkid_found) { - document.getElementById('pmkidStatus').textContent = 'βœ“ PMKID CAPTURED!'; - document.getElementById('pmkidStatus').style.color = 'var(--accent-green)'; - showInfo('πŸŽ‰ PMKID captured! File: ' + data.file); - showNotification('πŸ” PMKID Captured!', `Target: ${activePmkid.bssid}`); - clearInterval(activePmkid.pollInterval); + if (data.status === 'success' && data.password) { + showInfo('πŸŽ‰ PASSWORD FOUND: ' + data.password); + showNotification('πŸ”“ Password Cracked!', data.password); + alert('Password found!\n\n' + data.password + '\n\nThis has been logged.'); + } else if (data.status === 'not_found') { + showInfo('Password not found in wordlist. Try a different wordlist.'); + alert('Password not found in wordlist.\n\nTry using a larger or different wordlist.'); + } else if (data.status === 'running') { + showInfo('Aircrack-ng is running in background. Check terminal for progress.'); } else { - const elapsed = Math.floor((Date.now() - activePmkid.startTime) / 1000); - document.getElementById('pmkidStatus').textContent = 'Scanning... (' + elapsed + 's)'; + showError('Crack failed: ' + (data.message || 'Unknown error')); } - }); - } - - function stopPmkidCapture() { - if (activePmkid && activePmkid.pollInterval) { - clearInterval(activePmkid.pollInterval); - } - - fetch('/wifi/pmkid/stop', { method: 'POST' }) - .then(() => { - document.getElementById('pmkidStatus').textContent = 'Stopped'; - document.getElementById('pmkidStatus').style.color = 'var(--text-dim)'; - showInfo('PMKID capture stopped'); - activePmkid = null; + }) + .catch(err => { + showError('Crack error: ' + err.message); + console.error('Crack error:', err); }); }