Skip to content

Commit 8fd9c1b

Browse files
committed
features: add EvilTwin, WPA3-SAE dragonblood, adaptive deauth + tests
1 parent f14b804 commit 8fd9c1b

65 files changed

Lines changed: 12351 additions & 287 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

tests/demo_statistics.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
"""
5+
Demonstration of client monitoring and statistics tracking.
6+
"""
7+
8+
import time
9+
from wifite.util.client_monitor import ClientConnection, AttackStatistics
10+
11+
def demo_client_connection():
12+
"""Demonstrate ClientConnection data model."""
13+
print("\n=== ClientConnection Demo ===")
14+
15+
# Create a client
16+
client = ClientConnection(
17+
mac_address="AA:BB:CC:DD:EE:FF",
18+
ip_address="192.168.100.10",
19+
hostname="iPhone-12"
20+
)
21+
22+
print(f"Client: {client}")
23+
print(f" MAC: {client.mac_address}")
24+
print(f" IP: {client.ip_address}")
25+
print(f" Hostname: {client.hostname}")
26+
print(f" Connected: {client.is_connected()}")
27+
print(f" Credentials submitted: {client.credential_submitted}")
28+
29+
# Simulate credential submission
30+
time.sleep(1)
31+
client.credential_submitted = True
32+
client.credential_valid = True
33+
34+
print(f"\nAfter credential submission:")
35+
print(f" Credentials submitted: {client.credential_submitted}")
36+
print(f" Credentials valid: {client.credential_valid}")
37+
print(f" Connection duration: {client.connection_duration():.2f}s")
38+
39+
40+
def demo_attack_statistics():
41+
"""Demonstrate AttackStatistics tracking."""
42+
print("\n=== AttackStatistics Demo ===")
43+
44+
stats = AttackStatistics()
45+
46+
print("Initial state:")
47+
print(f" Total clients: {stats.total_clients_connected}")
48+
print(f" Unique clients: {stats.get_unique_client_count()}")
49+
print(f" Credential attempts: {stats.total_credential_attempts}")
50+
51+
# Simulate attack progression
52+
print("\nSimulating attack...")
53+
54+
# First client connects
55+
time.sleep(0.5)
56+
stats.record_client_connect("AA:BB:CC:DD:EE:FF")
57+
print(f" Client 1 connected (t={stats.get_time_to_first_client():.2f}s)")
58+
59+
# Second client connects
60+
time.sleep(0.3)
61+
stats.record_client_connect("11:22:33:44:55:66")
62+
print(f" Client 2 connected")
63+
64+
# First client submits wrong password
65+
time.sleep(0.5)
66+
stats.record_credential_attempt(success=False)
67+
print(f" Failed credential attempt (t={stats.get_time_to_first_credential():.2f}s)")
68+
69+
# First client submits correct password
70+
time.sleep(0.2)
71+
stats.record_credential_attempt(success=True)
72+
print(f" Successful credential attempt (t={stats.get_time_to_success():.2f}s)")
73+
74+
# Display final statistics
75+
print("\nFinal statistics:")
76+
print(f" Duration: {stats.get_duration():.2f}s")
77+
print(f" Total clients: {stats.total_clients_connected}")
78+
print(f" Unique clients: {stats.get_unique_client_count()}")
79+
print(f" Currently connected: {stats.currently_connected}")
80+
print(f" Credential attempts: {stats.total_credential_attempts}")
81+
print(f" Successful: {stats.successful_attempts}")
82+
print(f" Failed: {stats.failed_attempts}")
83+
print(f" Success rate: {stats.get_success_rate():.1f}%")
84+
85+
# Show dictionary representation
86+
print("\nStatistics as dictionary:")
87+
for key, value in stats.to_dict().items():
88+
if value is not None:
89+
if isinstance(value, float):
90+
print(f" {key}: {value:.2f}")
91+
else:
92+
print(f" {key}: {value}")
93+
94+
# Show string representation
95+
print("\nString representation:")
96+
print(stats)
97+
98+
99+
if __name__ == '__main__':
100+
demo_client_connection()
101+
demo_attack_statistics()
102+
print("\n=== Demo Complete ===\n")
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
"""
5+
Integration tests for adaptive deauth in Evil Twin attack.
6+
7+
Tests verify that the adaptive deauth manager is properly integrated
8+
into the Evil Twin attack flow.
9+
"""
10+
11+
import unittest
12+
import time
13+
from unittest.mock import Mock, MagicMock, patch, call
14+
15+
16+
class TestAdaptiveDeauthIntegration(unittest.TestCase):
17+
"""Test adaptive deauth integration with Evil Twin attack."""
18+
19+
def setUp(self):
20+
"""Set up test fixtures."""
21+
# Mock target
22+
self.mock_target = Mock()
23+
self.mock_target.bssid = '00:11:22:33:44:55'
24+
self.mock_target.essid = 'TestNetwork'
25+
self.mock_target.channel = 6
26+
self.mock_target.clients = []
27+
28+
@patch('wifite.config.Configuration')
29+
def test_adaptive_deauth_initialized(self, mock_config):
30+
"""Test that adaptive deauth manager is initialized on attack creation."""
31+
# Set up Configuration mock with all required attributes
32+
mock_config.interface = 'wlan0'
33+
mock_config.evil_twin_deauth_interval = 5.0
34+
mock_config.wpa_attack_timeout = 60
35+
36+
from wifite.attack.eviltwin import EvilTwin
37+
38+
attack = EvilTwin(self.mock_target)
39+
40+
# Verify adaptive deauth manager exists
41+
self.assertIsNotNone(attack.adaptive_deauth)
42+
self.assertEqual(attack.adaptive_deauth.base_interval, 5.0)
43+
self.assertEqual(attack.adaptive_deauth.min_interval, 2.0)
44+
self.assertEqual(attack.adaptive_deauth.max_interval, 15.0)
45+
46+
@patch('wifite.config.Configuration')
47+
def test_deauth_statistics_initialized(self, mock_config):
48+
"""Test that deauth statistics are initialized."""
49+
mock_config.interface = 'wlan0'
50+
mock_config.wpa_attack_timeout = 60
51+
52+
from wifite.attack.eviltwin import EvilTwin
53+
54+
attack = EvilTwin(self.mock_target)
55+
56+
# Verify statistics initialized
57+
self.assertEqual(attack.deauths_sent, 0)
58+
self.assertEqual(attack.last_deauth_time, 0)
59+
60+
@patch('wifite.config.Configuration')
61+
@patch('wifite.util.process.Process')
62+
def test_handle_deauth_sends_broadcast(self, mock_process, mock_config):
63+
"""Test that _handle_deauth sends broadcast deauth when appropriate."""
64+
mock_config.interface = 'wlan0'
65+
mock_config.wpa_attack_timeout = 60
66+
67+
from wifite.attack.eviltwin import EvilTwin
68+
69+
attack = EvilTwin(self.mock_target)
70+
attack.interface_deauth = 'wlan0mon'
71+
72+
# Force adaptive deauth to say it's time to send
73+
attack.adaptive_deauth.last_deauth_time = 0
74+
75+
# Call handle_deauth
76+
attack._handle_deauth()
77+
78+
# Verify deauth was sent
79+
self.assertGreater(attack.deauths_sent, 0)
80+
self.assertGreater(attack.last_deauth_time, 0)
81+
82+
@patch('wifite.config.Configuration')
83+
def test_client_connect_pauses_deauth(self, mock_config):
84+
"""Test that client connection pauses deauth."""
85+
mock_config.interface = 'wlan0'
86+
mock_config.wpa_attack_timeout = 60
87+
88+
from wifite.attack.eviltwin import EvilTwin
89+
from wifite.util.client_monitor import ClientConnection
90+
91+
attack = EvilTwin(self.mock_target)
92+
93+
# Create mock client
94+
client = ClientConnection(
95+
mac_address='AA:BB:CC:DD:EE:FF',
96+
ip_address='192.168.100.10',
97+
hostname='test-device'
98+
)
99+
100+
# Simulate client connection
101+
attack._on_client_connect(client)
102+
103+
# Verify deauth is paused
104+
self.assertTrue(attack.adaptive_deauth.is_paused)
105+
106+
# Verify adaptive manager recorded the connection
107+
self.assertEqual(attack.adaptive_deauth.clients_connected, 1)
108+
109+
@patch('wifite.config.Configuration')
110+
def test_client_disconnect_resumes_deauth(self, mock_config):
111+
"""Test that client disconnection resumes deauth when no clients remain."""
112+
mock_config.interface = 'wlan0'
113+
mock_config.wpa_attack_timeout = 60
114+
115+
from wifite.attack.eviltwin import EvilTwin
116+
from wifite.util.client_monitor import ClientConnection, ClientMonitor
117+
118+
attack = EvilTwin(self.mock_target)
119+
120+
# Mock client monitor
121+
attack.client_monitor = Mock(spec=ClientMonitor)
122+
attack.client_monitor.has_connected_clients = Mock(return_value=False)
123+
124+
# Pause deauth first
125+
attack.adaptive_deauth.pause()
126+
127+
# Create mock client
128+
client = ClientConnection(
129+
mac_address='AA:BB:CC:DD:EE:FF',
130+
ip_address='192.168.100.10',
131+
hostname='test-device'
132+
)
133+
134+
# Simulate client disconnection
135+
attack._on_client_disconnect(client)
136+
137+
# Verify deauth is resumed
138+
self.assertFalse(attack.adaptive_deauth.is_paused)
139+
140+
@patch('wifite.config.Configuration')
141+
def test_adaptive_interval_changes(self, mock_config):
142+
"""Test that adaptive interval changes based on activity."""
143+
mock_config.interface = 'wlan0'
144+
mock_config.wpa_attack_timeout = 60
145+
146+
from wifite.attack.eviltwin import EvilTwin
147+
148+
attack = EvilTwin(self.mock_target)
149+
150+
initial_interval = attack.adaptive_deauth.current_interval
151+
152+
# Record client connection (should reduce interval)
153+
attack.adaptive_deauth.record_client_connect()
154+
155+
# Verify interval was reduced
156+
self.assertLess(attack.adaptive_deauth.current_interval, initial_interval)
157+
self.assertGreaterEqual(attack.adaptive_deauth.current_interval,
158+
attack.adaptive_deauth.min_interval)
159+
160+
@patch('wifite.config.Configuration')
161+
def test_no_activity_increases_interval(self, mock_config):
162+
"""Test that no activity increases deauth interval."""
163+
mock_config.interface = 'wlan0'
164+
mock_config.wpa_attack_timeout = 60
165+
166+
from wifite.attack.eviltwin import EvilTwin
167+
168+
attack = EvilTwin(self.mock_target)
169+
170+
initial_interval = attack.adaptive_deauth.current_interval
171+
172+
# Record no activity multiple times
173+
for _ in range(3):
174+
attack.adaptive_deauth.record_no_activity()
175+
176+
# Verify interval was increased
177+
self.assertGreater(attack.adaptive_deauth.current_interval, initial_interval)
178+
self.assertLessEqual(attack.adaptive_deauth.current_interval,
179+
attack.adaptive_deauth.max_interval)
180+
181+
@patch('wifite.config.Configuration')
182+
def test_deauth_statistics_in_display(self, mock_config):
183+
"""Test that deauth statistics are included in display."""
184+
mock_config.interface = 'wlan0'
185+
mock_config.wpa_attack_timeout = 60
186+
187+
from wifite.attack.eviltwin import EvilTwin
188+
189+
attack = EvilTwin(self.mock_target)
190+
191+
# Mock client monitor
192+
attack.client_monitor = Mock()
193+
attack.client_monitor.get_detailed_stats = Mock(return_value={
194+
'duration': 60.0,
195+
'total_clients': 2,
196+
'unique_clients': 2,
197+
'currently_connected': 1,
198+
'credential_attempts': 3,
199+
'successful_attempts': 1,
200+
'failed_attempts': 2,
201+
'success_rate': 33.3,
202+
'time_to_first_client': 10.0,
203+
'time_to_first_credential': 20.0,
204+
'time_to_success': 30.0
205+
})
206+
207+
# Send some deauths
208+
attack.adaptive_deauth.record_deauth_sent()
209+
attack.adaptive_deauth.record_deauth_sent()
210+
attack.deauths_sent = 30
211+
212+
# Get statistics
213+
stats = attack.adaptive_deauth.get_statistics()
214+
215+
# Verify deauth statistics are present
216+
self.assertIn('total_deauths_sent', stats)
217+
self.assertIn('deauths_per_minute', stats)
218+
self.assertIn('current_interval', stats)
219+
self.assertEqual(stats['total_deauths_sent'], 2)
220+
221+
@patch('wifite.config.Configuration')
222+
@patch('wifite.util.process.Process')
223+
def test_targeted_deauth_with_known_clients(self, mock_process, mock_config):
224+
"""Test that targeted deauth is used when clients are known."""
225+
mock_config.interface = 'wlan0'
226+
mock_config.wpa_attack_timeout = 60
227+
228+
from wifite.attack.eviltwin import EvilTwin
229+
230+
attack = EvilTwin(self.mock_target)
231+
attack.interface_deauth = 'wlan0mon'
232+
233+
# Add known clients to target
234+
mock_client1 = Mock()
235+
mock_client1.station = 'AA:BB:CC:DD:EE:FF'
236+
mock_client2 = Mock()
237+
mock_client2.station = '11:22:33:44:55:66'
238+
self.mock_target.clients = [mock_client1, mock_client2]
239+
240+
# Force many deauths to trigger targeted mode
241+
for _ in range(15):
242+
attack.adaptive_deauth.record_deauth_sent()
243+
244+
# Force adaptive deauth to say it's time to send
245+
attack.adaptive_deauth.last_deauth_time = 0
246+
247+
# Call handle_deauth
248+
attack._handle_deauth()
249+
250+
# Verify targeted deauth was used
251+
# (Process should be called multiple times for different clients)
252+
self.assertGreater(attack.deauths_sent, 0)
253+
254+
@patch('wifite.config.Configuration')
255+
def test_deauth_count_adapts_to_clients(self, mock_config):
256+
"""Test that deauth count adapts based on connected clients."""
257+
mock_config.interface = 'wlan0'
258+
mock_config.wpa_attack_timeout = 60
259+
260+
from wifite.attack.eviltwin import EvilTwin
261+
262+
attack = EvilTwin(self.mock_target)
263+
264+
# No clients - should be aggressive (15 packets)
265+
count = attack.adaptive_deauth.get_recommended_deauth_count()
266+
self.assertEqual(count, 15)
267+
268+
# One client - should be moderate (10 packets)
269+
attack.adaptive_deauth.record_client_connect()
270+
count = attack.adaptive_deauth.get_recommended_deauth_count()
271+
self.assertEqual(count, 10)
272+
273+
# Multiple clients - should be conservative (5 packets)
274+
attack.adaptive_deauth.record_client_connect()
275+
attack.adaptive_deauth.record_client_connect()
276+
count = attack.adaptive_deauth.get_recommended_deauth_count()
277+
self.assertEqual(count, 5)
278+
279+
280+
if __name__ == '__main__':
281+
unittest.main()

0 commit comments

Comments
 (0)