fix: client kick/deauth use full bssid/target/channel deauth form
hak5cmd on this firmware has no CLIENT_KICK command and PINEAPPLE_DEAUTH_CLIENT requires (bssid, target, channel) — the UI kick/deauth buttons and the MCP pineap.kick_client tool previously passed only the client MAC, which printed usage and silently did nothing. - _deauth_target/_deauth_client_via_iface resolve the client's association interface via iwinfo (Access Point + Channel), pick the band-aware inject interface, and issue DEAUTH_CLIENT <bssid> <mac> <channel> - h_client_kick: deny-filter (persistent) + immediate deauth with rc checks - h_deauth_client: deauth with the full form; 502 with detail when the client is not associated - MCP pineap.kick_client: resolves the client first (no side effects on failure), then deny-filter + deauth; verified on device (clean error for unassociated clients, filter list restored) - tests updated for the new command chain
This commit is contained in:
@@ -9,8 +9,10 @@ import server
|
||||
def setUpModule():
|
||||
__import__('importlib').reload(server)
|
||||
|
||||
_orig_hak5 = server.hak5
|
||||
_orig_device_run = server.device_run
|
||||
_orig_assoc_clients = server.assoc_clients
|
||||
_orig_iface_ap_info = server._iface_ap_info
|
||||
_orig_pineap = server._pineap
|
||||
|
||||
|
||||
class NormalizeTest(unittest.TestCase):
|
||||
@@ -24,8 +26,10 @@ class NormalizeTest(unittest.TestCase):
|
||||
|
||||
class ClientsTest(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
server.hak5 = _orig_hak5
|
||||
server.device_run = _orig_device_run
|
||||
server.assoc_clients = _orig_assoc_clients
|
||||
server._iface_ap_info = _orig_iface_ap_info
|
||||
server._pineap = _orig_pineap
|
||||
|
||||
def test_clients_handler(self):
|
||||
server.assoc_clients = lambda: [{'mac': 'AA:BB:CC:DD:EE:FF', 'iface': 'wlan0open', 'rssi': -55}]
|
||||
@@ -35,15 +39,37 @@ class ClientsTest(unittest.TestCase):
|
||||
self.assertEqual(status, 200)
|
||||
self.assertEqual(payload['count'], 1)
|
||||
|
||||
def test_kick_validates_and_deny_adds(self):
|
||||
def _kick_env(self):
|
||||
calls = []
|
||||
def fake(*args):
|
||||
calls.append(args)
|
||||
return 'ok'
|
||||
server.hak5 = fake
|
||||
def fake_run(argv, timeout=30):
|
||||
calls.append(argv)
|
||||
return 0, '', ''
|
||||
server.device_run = fake_run
|
||||
server.assoc_clients = lambda: [{'mac': '00:11:22:33:44:55', 'iface': 'wlan0open', 'rssi': -55}]
|
||||
server._iface_ap_info = lambda iface: ('AA:BB:CC:DD:EE:FF', 6)
|
||||
server._pineap = lambda *a, **k: (0, '', '')
|
||||
return calls
|
||||
|
||||
def test_kick_validates_and_deny_adds(self):
|
||||
calls = self._kick_env()
|
||||
server.h_client_kick(type('C', (), {'args': (), 'body': {'mac': '00:11:22:33:44:55'}})())
|
||||
self.assertIn(('PINEAPPLE_DEVICE_FILTER_ADD', 'deny', '00:11:22:33:44:55'), calls)
|
||||
self.assertTrue(any(c[0] == 'PINEAPPLE_DEAUTH_CLIENT' for c in calls))
|
||||
self.assertIn([server.HAK5CMD, 'PINEAPPLE_DEVICE_FILTER_ADD', 'deny', '00:11:22:33:44:55'], calls)
|
||||
# The immediate deauth must use the full bssid/target/channel form.
|
||||
self.assertTrue(any(c[:4] == [server.HAK5CMD, 'DEAUTH_CLIENT', 'AA:BB:CC:DD:EE:FF',
|
||||
'00:11:22:33:44:55'] and c[4] == '6' for c in calls))
|
||||
|
||||
def test_kick_not_associated_still_filters(self):
|
||||
calls = []
|
||||
def fake_run(argv, timeout=30):
|
||||
calls.append(argv)
|
||||
return 0, '', ''
|
||||
server.device_run = fake_run
|
||||
server.assoc_clients = lambda: []
|
||||
status, payload = server.h_client_kick(type('C', (), {'args': (), 'body': {'mac': '00:11:22:33:44:55'}})())
|
||||
self.assertEqual(status, 200)
|
||||
self.assertIs(payload['deauth'], False)
|
||||
self.assertTrue(any(c == [server.HAK5CMD, 'PINEAPPLE_DEVICE_FILTER_ADD', 'deny',
|
||||
'00:11:22:33:44:55'] for c in calls))
|
||||
|
||||
def test_kick_bad_mac_400(self):
|
||||
status, payload = server.h_client_kick(type('C', (), {'args': (), 'body': {'mac': 'x'}})())
|
||||
@@ -51,12 +77,22 @@ class ClientsTest(unittest.TestCase):
|
||||
|
||||
def test_deauth_client(self):
|
||||
calls = []
|
||||
def fake(*args):
|
||||
calls.append(args)
|
||||
return 'ok'
|
||||
server.hak5 = fake
|
||||
server.h_deauth_client(type('C', (), {'args': (), 'body': {'mac': '00:11:22:33:44:55'}})())
|
||||
self.assertEqual(calls[0][0], 'PINEAPPLE_DEAUTH_CLIENT')
|
||||
def fake_run(argv, timeout=30):
|
||||
calls.append(argv)
|
||||
return 0, '', ''
|
||||
server.device_run = fake_run
|
||||
server.assoc_clients = lambda: [{'mac': '00:11:22:33:44:55', 'iface': 'wlan1wpa', 'rssi': -60}]
|
||||
server._iface_ap_info = lambda iface: ('AA:BB:CC:DD:EE:FF', 149)
|
||||
status, payload = server.h_deauth_client(type('C', (), {'args': (), 'body': {'mac': '00:11:22:33:44:55'}})())
|
||||
self.assertEqual(status, 200)
|
||||
# 5 GHz client -> wlan1mon inject, no _pineap pin needed.
|
||||
self.assertTrue(any(c[:4] == [server.HAK5CMD, 'DEAUTH_CLIENT', 'AA:BB:CC:DD:EE:FF',
|
||||
'00:11:22:33:44:55'] and c[4] == '149' for c in calls))
|
||||
|
||||
def test_deauth_client_not_associated_502(self):
|
||||
server.assoc_clients = lambda: []
|
||||
status, payload = server.h_deauth_client(type('C', (), {'args': (), 'body': {'mac': '00:11:22:33:44:55'}})())
|
||||
self.assertEqual(status, 502)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user