Skip to content

Commit b3f893f

Browse files
committed
test: add hcxdump integration tests
1 parent ef8761b commit b3f893f

1 file changed

Lines changed: 365 additions & 0 deletions

File tree

tests/test_hcxdump_integration.py

Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
"""
5+
Integration tests for hcxdumptool-based WPA capture.
6+
7+
Tests complete WPA attack flows with --hcxdump flag including:
8+
- WPA2 and WPA3 target capture
9+
- Fallback scenarios
10+
- Error recovery
11+
- Performance validation
12+
"""
13+
14+
import unittest
15+
import sys
16+
from unittest.mock import Mock, patch, MagicMock
17+
18+
# Mock sys.argv to prevent argparse from reading test arguments
19+
original_argv = sys.argv
20+
sys.argv = ['wifite']
21+
22+
from wifite.config import Configuration
23+
from wifite.model.target import Target
24+
from wifite.model.interface_info import InterfaceAssignment
25+
26+
# Set required Configuration attributes
27+
Configuration.interface = 'wlan0'
28+
Configuration.wpa_attack_timeout = 10
29+
Configuration.wpa_deauth_timeout = 2
30+
Configuration.use_hcxdump = True
31+
Configuration.no_deauth = False
32+
Configuration.ignore_old_handshakes = True
33+
Configuration.verbose = 0
34+
35+
from wifite.attack.wpa import AttackWPA
36+
37+
# Restore original argv
38+
sys.argv = original_argv
39+
40+
41+
class TestCompleteWPAAttackFlow(unittest.TestCase):
42+
"""Test complete WPA attack flow with --hcxdump flag."""
43+
44+
def setUp(self):
45+
"""Set up test fixtures."""
46+
self.mock_target = Mock(spec=Target)
47+
self.mock_target.bssid = 'AA:BB:CC:DD:EE:FF'
48+
self.mock_target.essid = 'TestNetwork'
49+
self.mock_target.channel = 6
50+
self.mock_target.encryption = 'WPA2'
51+
self.mock_target.power = -50
52+
self.mock_target.wps = False
53+
self.mock_target.pmf_required = False
54+
self.mock_target.essid_known = True
55+
56+
# Create dual interface assignment
57+
self.assignment = InterfaceAssignment(
58+
attack_type='wpa',
59+
primary='wlan0mon',
60+
secondary='wlan1mon',
61+
primary_role='Handshake capture',
62+
secondary_role='Deauthentication'
63+
)
64+
65+
Configuration.use_hcxdump = True
66+
67+
@patch('wifite.tools.hcxdumptool.HcxDumpTool')
68+
@patch('wifite.tools.airmon.Airmon')
69+
@patch('wifite.util.color.Color')
70+
@patch('wifite.attack.wpa.AttackWPA._capture_handshake_dual_hcxdump')
71+
def test_wpa2_attack_with_hcxdump_enabled(self, mock_capture, mock_color,
72+
mock_airmon, mock_hcxdump):
73+
"""Test WPA2 attack uses hcxdump when enabled and available."""
74+
# Mock hcxdumptool available
75+
mock_hcxdump.exists.return_value = True
76+
mock_hcxdump.check_minimum_version.return_value = True
77+
78+
# Mock monitor mode
79+
mock_airmon.start.side_effect = ['wlan0mon', 'wlan1mon']
80+
mock_airmon.stop.return_value = None
81+
82+
# Mock successful capture
83+
mock_handshake = Mock()
84+
mock_capture.return_value = mock_handshake
85+
86+
# Create attack
87+
attack = AttackWPA(self.mock_target)
88+
attack.interface_assignment = self.assignment
89+
90+
# Run dual interface attack
91+
result = attack._run_dual_interface()
92+
93+
# Verify hcxdump was checked
94+
mock_hcxdump.exists.assert_called_once()
95+
mock_hcxdump.check_minimum_version.assert_called_once_with('6.2.0')
96+
97+
# Verify hcxdump capture was called
98+
mock_capture.assert_called_once()
99+
100+
# Verify result
101+
self.assertEqual(result, mock_handshake)
102+
103+
@patch('wifite.tools.hcxdumptool.HcxDumpTool')
104+
@patch('wifite.tools.airmon.Airmon')
105+
@patch('wifite.util.color.Color')
106+
@patch('wifite.attack.wpa.AttackWPA._capture_handshake_dual_hcxdump')
107+
def test_wpa3_attack_with_pmf(self, mock_capture, mock_color,
108+
mock_airmon, mock_hcxdump):
109+
"""Test WPA3 attack with PMF uses hcxdump correctly."""
110+
# Update target for WPA3
111+
self.mock_target.encryption = 'WPA3'
112+
self.mock_target.pmf_required = True
113+
114+
# Mock hcxdumptool available
115+
mock_hcxdump.exists.return_value = True
116+
mock_hcxdump.check_minimum_version.return_value = True
117+
118+
# Mock monitor mode
119+
mock_airmon.start.side_effect = ['wlan0mon', 'wlan1mon']
120+
mock_airmon.stop.return_value = None
121+
122+
# Mock successful capture
123+
mock_handshake = Mock()
124+
mock_capture.return_value = mock_handshake
125+
126+
# Create attack
127+
attack = AttackWPA(self.mock_target)
128+
attack.interface_assignment = self.assignment
129+
130+
# Run dual interface attack
131+
result = attack._run_dual_interface()
132+
133+
# Verify hcxdump capture was called
134+
mock_capture.assert_called_once()
135+
136+
# Verify result
137+
self.assertEqual(result, mock_handshake)
138+
139+
140+
class TestFallbackScenarios(unittest.TestCase):
141+
"""Test fallback scenarios when hcxdumptool unavailable."""
142+
143+
def setUp(self):
144+
"""Set up test fixtures."""
145+
self.mock_target = Mock(spec=Target)
146+
self.mock_target.bssid = 'CC:DD:EE:FF:00:11'
147+
self.mock_target.essid = 'TestNetwork'
148+
self.mock_target.channel = 6
149+
self.mock_target.encryption = 'WPA2'
150+
self.mock_target.power = -50
151+
self.mock_target.wps = False
152+
self.mock_target.pmf_required = False
153+
self.mock_target.essid_known = True
154+
155+
# Create dual interface assignment
156+
self.assignment = InterfaceAssignment(
157+
attack_type='wpa',
158+
primary='wlan0mon',
159+
secondary='wlan1mon',
160+
primary_role='Handshake capture',
161+
secondary_role='Deauthentication'
162+
)
163+
164+
Configuration.use_hcxdump = True
165+
166+
@patch('wifite.tools.hcxdumptool.HcxDumpTool')
167+
@patch('wifite.tools.airmon.Airmon')
168+
@patch('wifite.util.color.Color')
169+
@patch('wifite.attack.wpa.AttackWPA._capture_handshake_dual_airodump')
170+
def test_fallback_when_hcxdumptool_not_installed(self, mock_airodump, mock_color,
171+
mock_airmon, mock_hcxdump):
172+
"""Test graceful fallback when hcxdumptool not installed."""
173+
# Mock hcxdumptool not installed
174+
mock_hcxdump.exists.return_value = False
175+
mock_hcxdump.dependency_url = 'https://github.com/ZerBea/hcxdumptool'
176+
177+
# Mock monitor mode
178+
mock_airmon.start.side_effect = ['wlan0mon', 'wlan1mon']
179+
mock_airmon.stop.return_value = None
180+
181+
# Mock airodump fallback
182+
mock_handshake = Mock()
183+
mock_airodump.return_value = mock_handshake
184+
185+
# Create attack
186+
attack = AttackWPA(self.mock_target)
187+
attack.interface_assignment = self.assignment
188+
189+
# Run dual interface attack
190+
result = attack._run_dual_interface()
191+
192+
# Verify tool check was performed
193+
mock_hcxdump.exists.assert_called_once()
194+
195+
# Verify fallback to airodump was called
196+
mock_airodump.assert_called_once()
197+
198+
# Verify result from fallback
199+
self.assertEqual(result, mock_handshake)
200+
201+
@patch('wifite.tools.hcxdumptool.HcxDumpTool')
202+
@patch('wifite.tools.airmon.Airmon')
203+
@patch('wifite.util.color.Color')
204+
@patch('wifite.attack.wpa.AttackWPA._capture_handshake_dual_airodump')
205+
def test_fallback_when_version_insufficient(self, mock_airodump, mock_color,
206+
mock_airmon, mock_hcxdump):
207+
"""Test graceful fallback when hcxdumptool version insufficient."""
208+
# Mock hcxdumptool installed but old version
209+
mock_hcxdump.exists.return_value = True
210+
mock_hcxdump.check_minimum_version.return_value = False
211+
mock_hcxdump.check_version.return_value = '5.0.0'
212+
mock_hcxdump.dependency_url = 'https://github.com/ZerBea/hcxdumptool'
213+
214+
# Mock monitor mode
215+
mock_airmon.start.side_effect = ['wlan0mon', 'wlan1mon']
216+
mock_airmon.stop.return_value = None
217+
218+
# Mock airodump fallback
219+
mock_handshake = Mock()
220+
mock_airodump.return_value = mock_handshake
221+
222+
# Create attack
223+
attack = AttackWPA(self.mock_target)
224+
attack.interface_assignment = self.assignment
225+
226+
# Run dual interface attack
227+
result = attack._run_dual_interface()
228+
229+
# Verify version checks were performed
230+
mock_hcxdump.exists.assert_called_once()
231+
mock_hcxdump.check_minimum_version.assert_called_once_with('6.2.0')
232+
233+
# Verify fallback to airodump was called
234+
mock_airodump.assert_called_once()
235+
236+
# Verify result from fallback
237+
self.assertEqual(result, mock_handshake)
238+
239+
@patch('wifite.tools.hcxdumptool.HcxDumpTool')
240+
@patch('wifite.tools.airmon.Airmon')
241+
@patch('wifite.util.color.Color')
242+
@patch('wifite.attack.wpa.AttackWPA._capture_handshake_dual_airodump')
243+
def test_graceful_fallback_maintains_functionality(self, mock_airodump, mock_color,
244+
mock_airmon, mock_hcxdump):
245+
"""Verify graceful fallback maintains full functionality."""
246+
# Mock hcxdumptool not available
247+
mock_hcxdump.exists.return_value = False
248+
mock_hcxdump.dependency_url = 'https://github.com/ZerBea/hcxdumptool'
249+
250+
# Mock monitor mode
251+
mock_airmon.start.side_effect = ['wlan0mon', 'wlan1mon']
252+
mock_airmon.stop.return_value = None
253+
254+
# Mock successful airodump capture
255+
mock_handshake = Mock()
256+
mock_handshake.capfile = '/tmp/handshake.cap'
257+
mock_handshake.bssid = self.mock_target.bssid
258+
mock_handshake.essid = self.mock_target.essid
259+
mock_airodump.return_value = mock_handshake
260+
261+
# Create attack
262+
attack = AttackWPA(self.mock_target)
263+
attack.interface_assignment = self.assignment
264+
265+
# Run dual interface attack
266+
result = attack._run_dual_interface()
267+
268+
# Verify fallback was successful
269+
self.assertIsNotNone(result)
270+
self.assertEqual(result.capfile, '/tmp/handshake.cap')
271+
self.assertEqual(result.bssid, self.mock_target.bssid)
272+
273+
274+
class TestErrorRecovery(unittest.TestCase):
275+
"""Test error recovery scenarios."""
276+
277+
def setUp(self):
278+
"""Set up test fixtures."""
279+
self.mock_target = Mock(spec=Target)
280+
self.mock_target.bssid = 'DD:EE:FF:00:11:22'
281+
self.mock_target.essid = 'TestNetwork'
282+
self.mock_target.channel = 6
283+
self.mock_target.encryption = 'WPA2'
284+
self.mock_target.power = -50
285+
self.mock_target.wps = False
286+
self.mock_target.pmf_required = False
287+
self.mock_target.essid_known = True
288+
289+
# Create dual interface assignment
290+
self.assignment = InterfaceAssignment(
291+
attack_type='wpa',
292+
primary='wlan0mon',
293+
secondary='wlan1mon',
294+
primary_role='Handshake capture',
295+
secondary_role='Deauthentication'
296+
)
297+
298+
Configuration.use_hcxdump = True
299+
300+
def test_hcxdump_capture_method_exists(self):
301+
"""Verify hcxdump capture method exists."""
302+
attack = AttackWPA(self.mock_target)
303+
self.assertTrue(hasattr(attack, '_capture_handshake_dual_hcxdump'))
304+
self.assertTrue(callable(getattr(attack, '_capture_handshake_dual_hcxdump')))
305+
306+
def test_parallel_deauth_method_exists(self):
307+
"""Verify parallel deauth method exists."""
308+
attack = AttackWPA(self.mock_target)
309+
self.assertTrue(hasattr(attack, '_deauth_parallel'))
310+
self.assertTrue(callable(getattr(attack, '_deauth_parallel')))
311+
312+
313+
class TestPerformanceValidation(unittest.TestCase):
314+
"""Test performance validation for dual monitoring."""
315+
316+
def setUp(self):
317+
"""Set up test fixtures."""
318+
self.mock_target = Mock(spec=Target)
319+
self.mock_target.bssid = 'EE:FF:00:11:22:33'
320+
self.mock_target.essid = 'TestNetwork'
321+
self.mock_target.channel = 6
322+
self.mock_target.encryption = 'WPA2'
323+
self.mock_target.power = -50
324+
self.mock_target.wps = False
325+
self.mock_target.pmf_required = False
326+
self.mock_target.essid_known = True
327+
328+
Configuration.use_hcxdump = True
329+
330+
@patch('wifite.tools.hcxdumptool.HcxDumpTool')
331+
def test_hcxdump_tool_availability_check(self, mock_hcxdump):
332+
"""Test hcxdump tool availability check is efficient."""
333+
# Mock tool available
334+
mock_hcxdump.exists.return_value = True
335+
mock_hcxdump.check_minimum_version.return_value = True
336+
337+
# Check availability
338+
available = mock_hcxdump.exists()
339+
version_ok = mock_hcxdump.check_minimum_version('6.2.0')
340+
341+
# Verify checks are efficient (single call each)
342+
self.assertTrue(available)
343+
self.assertTrue(version_ok)
344+
mock_hcxdump.exists.assert_called_once()
345+
mock_hcxdump.check_minimum_version.assert_called_once()
346+
347+
def test_interface_assignment_structure(self):
348+
"""Test interface assignment structure is correct."""
349+
assignment = InterfaceAssignment(
350+
attack_type='wpa',
351+
primary='wlan0mon',
352+
secondary='wlan1mon',
353+
primary_role='Handshake capture',
354+
secondary_role='Deauthentication'
355+
)
356+
357+
# Verify structure
358+
self.assertTrue(assignment.is_dual_interface())
359+
self.assertEqual(assignment.primary, 'wlan0mon')
360+
self.assertEqual(assignment.secondary, 'wlan1mon')
361+
self.assertEqual(assignment.attack_type, 'wpa')
362+
363+
364+
if __name__ == '__main__':
365+
unittest.main()

0 commit comments

Comments
 (0)