Files

57 lines
2.6 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
"""Harmless path checks against the local tunnel to the decoy; no payload execution."""
import argparse
import http.client
import ipaddress
import urllib.parse
PATHS = (
'/reports', '/reports/', '/admin', '/admin/', '/dashboard', '/evidence', '/evidence/',
'/logs', '/logs/', '/logs/events.jsonl', '/events.jsonl', '/artifacts/',
'/artifacts/' + '0' * 64 + '.bin', '/http/events.jsonl', '/network/events.jsonl',
'/var/lib/decoy/http/events.jsonl', '/var/lib/decoy/network/events.jsonl',
'/opt/decoy/honeypot.py', '/honeypot.py', '/evidence.py', '/vm_backend.py', '/guest.py',
'/network_capture.py', '/ui/sources.json', '/README.md',
'/../var/lib/decoy/http/events.jsonl', '/../../var/lib/decoy/http/events.jsonl',
'/%2e%2e/%2e%2e/var/lib/decoy/http/events.jsonl',
'/%2e%2e%2f%2e%2e%2fvar%2flib%2fdecoy%2fhttp%2fevents.jsonl',
'/%252e%252e%252fvar%252flib%252fdecoy%252fhttp%252fevents.jsonl',
'/..%5c..%5cvar%5clib%5cdecoy%5chttp%5cevents.jsonl',
'//var/lib/decoy/http/events.jsonl', '/logs/../events.jsonl', '/logs%00/events.jsonl',
)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--url', required=True, help='HTTP loopback tunnel URL')
args = parser.parse_args()
url = urllib.parse.urlsplit(args.url)
if url.scheme != 'http' or url.username or url.password or url.query or url.fragment:
parser.error('use an HTTP loopback tunnel URL without credentials or query')
try:
loopback = ipaddress.ip_address(url.hostname).is_loopback
except ValueError:
loopback = url.hostname == 'localhost'
if not loopback or url.path not in ('', '/'):
parser.error('test is restricted to the loopback tunnel root')
def request(path):
conn = http.client.HTTPConnection(url.hostname, url.port or 80, timeout=25)
try:
conn.request('GET', path)
response = conn.getresponse()
body = response.read(131073)
assert len(body) <= 131072, 'unexpected large response for ' + path
return response.status, body
finally:
conn.close()
status, body = request('/')
assert status == 200 and b'<html' in body.lower(), 'decoy UI unavailable'
for path in PATHS:
status, body = request(path)
assert status == 404, f'private path returned {status}: {path}'
assert body == b'Not Found', 'unexpected content at private path: ' + path
print(f'PASS: NetScaler UI available; {len(PATHS)} private/report/evidence/source/traversal paths return plain404')
if __name__ == '__main__':
main()