fix: harden UI actions and daemon calls for reliable control (v1.3.2)
Retry and serialize pineapd/hak5 calls, queue virtual-pager keys, and grey out buttons until the pager finishes. Deploy now installs python3-light after factory firmware. Bump version to 1.3.2. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+136
-3
@@ -80,11 +80,20 @@ class AttacksDeployTest(unittest.TestCase):
|
||||
self.old_state = server.PINEAP_STATE_FILE
|
||||
server.PINEAP_STATE_FILE = os.path.join(self.tmp, 'state.json')
|
||||
self.old_ent = {k: getattr(server, k) for k in
|
||||
('ENT_CONF', 'ENT_PIDFILE', 'ENT_EAP_USERS', 'ENT_STATE')}
|
||||
('ENT_CONF', 'ENT_PIDFILE', 'ENT_EAP_USERS', 'ENT_STATE',
|
||||
'ENT_DIR', 'ENT_CA_CERT', 'ENT_SERVER_CERT', 'ENT_SERVER_KEY',
|
||||
'ENT_LOG', 'ENT_CAPTURES', 'ENT_DH_FILE')}
|
||||
server.ENT_CONF = os.path.join(self.tmp, 'enterprise.conf')
|
||||
server.ENT_PIDFILE = os.path.join(self.tmp, 'mk8.pid')
|
||||
server.ENT_EAP_USERS = os.path.join(self.tmp, 'eap_users')
|
||||
server.ENT_STATE = os.path.join(self.tmp, 'state.json')
|
||||
server.ENT_DIR = os.path.join(self.tmp, 'ent')
|
||||
server.ENT_CA_CERT = os.path.join(server.ENT_DIR, 'ca.pem')
|
||||
server.ENT_SERVER_CERT = os.path.join(server.ENT_DIR, 'server.pem')
|
||||
server.ENT_SERVER_KEY = os.path.join(server.ENT_DIR, 'server.key')
|
||||
server.ENT_LOG = os.path.join(server.ENT_DIR, 'hostapd.log')
|
||||
server.ENT_CAPTURES = os.path.join(server.ENT_DIR, 'captures.json')
|
||||
server.ENT_DH_FILE = os.path.join(server.ENT_DIR, 'dh.pem')
|
||||
self.old_ent_running = server._ent_running
|
||||
self.old_ent_state = server._ent_state_loaded
|
||||
server._ent_running = lambda: True
|
||||
@@ -203,10 +212,25 @@ class AttacksDeployTest(unittest.TestCase):
|
||||
self.assertIn(['iw', 'phy', 'phy1', 'interface', 'add', 'wlan1ent',
|
||||
'type', 'managed'], cmds)
|
||||
self.assertIn(['iw', 'dev', 'wlan1ent', 'set', 'type', 'ap'], cmds)
|
||||
self.assertIn(['/usr/sbin/hostapd', '-B', '-P', server.ENT_PIDFILE,
|
||||
server.ENT_CONF], cmds)
|
||||
self.assertTrue(any(c[:4] == ['/usr/sbin/hostapd', '-B', '-P', server.ENT_PIDFILE]
|
||||
for c in cmds))
|
||||
self.assertEqual(self.f.state['pineapd.@hostapd[0].mgmtiface'], 'wlan1ent')
|
||||
self.assertEqual(self.f.state['pineapd.wlan1mon.hop'], '0')
|
||||
with open(server.ENT_CONF) as f:
|
||||
conf = f.read()
|
||||
self.assertIn('ca_cert=', conf)
|
||||
self.assertIn('server_cert=', conf)
|
||||
self.assertIn('private_key=', conf)
|
||||
self.assertIn('ieee8021x=1', conf)
|
||||
self.assertIn('eap_server=1', conf)
|
||||
self.assertNotIn('eap_server_identity', conf)
|
||||
self.assertNotIn('eap_server_erp', conf)
|
||||
self.assertNotIn('dh_file=', conf)
|
||||
self.assertIn('ieee80211w=0', conf)
|
||||
with open(server.ENT_EAP_USERS) as f:
|
||||
users = f.read()
|
||||
self.assertIn('PEAP,TTLS', users)
|
||||
self.assertIn('[2]', users)
|
||||
|
||||
def test_deploy_enterprise_rejects_non_5g_channel(self):
|
||||
status, _ = server.h_attacks_deploy(ctx({
|
||||
@@ -223,6 +247,77 @@ class AttacksDeployTest(unittest.TestCase):
|
||||
cmds = [r[0] for r in self.f.runs]
|
||||
self.assertIn(['iw', 'dev', 'wlan1ent', 'del'], cmds)
|
||||
|
||||
def test_deploy_enterprise_writes_passphrase_and_hidden(self):
|
||||
status, payload = server.h_attacks_deploy(ctx({
|
||||
'kind': 'enterprise', 'ssid': 'CorpAP', 'passphrase': 'labsecret',
|
||||
'enctype': 'wpa2', 'hidden': True, 'channel': 36}))
|
||||
self.assertEqual(status, 200)
|
||||
with open(server.ENT_EAP_USERS) as f:
|
||||
users = f.read()
|
||||
self.assertIn('labsecret', users)
|
||||
self.assertIn('PEAP,TTLS', users)
|
||||
self.assertIn('[2]', users)
|
||||
with open(server.ENT_CONF) as f:
|
||||
conf = f.read()
|
||||
self.assertIn('ignore_broadcast_ssid=1', conf)
|
||||
self.assertIn('ca_cert=', conf)
|
||||
|
||||
def test_eap_secret_sanitizes_quotes(self):
|
||||
self.assertEqual(server._eap_secret(''), 'dummy')
|
||||
self.assertEqual(server._eap_secret('ab"c\ndef'), 'abcdef')
|
||||
|
||||
def test_hostapd_unknown_items_parse_pager_error(self):
|
||||
err = ("Line 14: unknown configuration item 'eap_server_identity'\n"
|
||||
"1 errors found in configuration file '/root/loot/enterprise.conf'\n"
|
||||
"Failed to set up interface with /root/loot/enterprise.conf\n"
|
||||
"Failed to initialize interface\n")
|
||||
self.assertEqual(server._hostapd_unknown_items(err), ['eap_server_identity'])
|
||||
|
||||
def test_drop_hostapd_keys_removes_only_named_lines(self):
|
||||
conf = ('interface=wlan1ent\n'
|
||||
'eap_server=1\n'
|
||||
'eap_server_identity=hostapd\n'
|
||||
'dh_file=/tmp/dh.pem\n')
|
||||
new, changed = server._drop_hostapd_keys(
|
||||
conf, ['eap_server_identity', 'dh_file'])
|
||||
self.assertTrue(changed)
|
||||
self.assertIn('eap_server=1', new)
|
||||
self.assertIn('interface=wlan1ent', new)
|
||||
self.assertNotIn('eap_server_identity', new)
|
||||
self.assertNotIn('dh_file=', new)
|
||||
|
||||
def test_start_ent_hostapd_strips_unknown_keys_and_retries(self):
|
||||
with open(server.ENT_CONF, 'w') as f:
|
||||
f.write('interface=wlan1ent\neap_server=1\neap_server_identity=hostapd\n')
|
||||
orig = server.device_run
|
||||
seen = []
|
||||
|
||||
def wrapped(args, timeout=20, input_data=None):
|
||||
a = list(args)
|
||||
if a and a[0] == '/usr/sbin/hostapd':
|
||||
with open(server.ENT_CONF) as fh:
|
||||
text = fh.read()
|
||||
seen.append(text)
|
||||
if 'eap_server_identity' in text:
|
||||
return (1, '',
|
||||
"Line 3: unknown configuration item 'eap_server_identity'\n"
|
||||
"1 errors found in configuration file '%s'\n"
|
||||
"Failed to initialize interface\n" % server.ENT_CONF)
|
||||
return (0, '', '')
|
||||
return orig(args, timeout=timeout, input_data=input_data)
|
||||
|
||||
server.device_run = wrapped
|
||||
try:
|
||||
rc, _out, _err = server._start_ent_hostapd()
|
||||
finally:
|
||||
server.device_run = orig
|
||||
self.assertEqual(rc, 0)
|
||||
self.assertGreaterEqual(len(seen), 2)
|
||||
with open(server.ENT_CONF) as f:
|
||||
conf = f.read()
|
||||
self.assertNotIn('eap_server_identity', conf)
|
||||
self.assertIn('eap_server=1', conf)
|
||||
|
||||
def test_deploy_validation(self):
|
||||
status, _ = server.h_attacks_deploy(ctx({'kind': 'wpa', 'ssid': ''}))
|
||||
self.assertEqual(status, 400)
|
||||
@@ -322,5 +417,43 @@ class AttacksExportTest(unittest.TestCase):
|
||||
self.assertIn('/root/loot/pcap/b.cap', hc)
|
||||
|
||||
|
||||
class AttacksStatusTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.saved = {
|
||||
'_count_table': server._count_table,
|
||||
'_ent_summary': server._ent_summary,
|
||||
'daemon_sock_call': server.daemon_sock_call,
|
||||
'_uci_ap_summary': server._uci_ap_summary,
|
||||
'_read_hop': server._read_hop,
|
||||
}
|
||||
|
||||
def tearDown(self):
|
||||
for name, fn in self.saved.items():
|
||||
setattr(server, name, fn)
|
||||
|
||||
def test_status_exposes_enterprise_ap_for_ui(self):
|
||||
server._count_table = lambda t: {
|
||||
'hostap_handshake': 2, 'hostap_basic': 3, 'hostap_chalresp': 1
|
||||
}.get(t, 0)
|
||||
server._ent_summary = lambda detail=True: {
|
||||
'enabled': True, 'live': True, 'ssid': 'CorpLab',
|
||||
'iface': 'wlan1ent', 'stations': ['AA:BB:CC:DD:EE:FF'],
|
||||
'auth_method': 'mschapv2', 'certs': True, 'captures': 4,
|
||||
'ctrl_linked': True,
|
||||
}
|
||||
server.daemon_sock_call = lambda *a, **k: (200, {'pineape_disabled': False})
|
||||
server._uci_ap_summary = lambda *a, **k: None
|
||||
server._read_hop = lambda: '1'
|
||||
status, payload = server.h_attacks_status(ctx())
|
||||
self.assertEqual(status, 200)
|
||||
self.assertTrue(payload['enterprise']['ap']['live'])
|
||||
self.assertEqual(payload['enterprise']['ap']['ssid'], 'CorpLab')
|
||||
self.assertEqual(payload['enterprise']['identities'], 3)
|
||||
self.assertEqual(payload['enterprise']['mschapv2'], 1)
|
||||
self.assertEqual(payload['enterprise']['creds'], 4)
|
||||
self.assertEqual(payload['handshakes'], 2)
|
||||
self.assertTrue(payload['enterprise']['pineape']['enabled'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user