"""Run: python3 test_profile.py. Passive fingerprints and inert header/body intake.""" import gzip import base64 import hashlib import http.client import struct import threading import xml.etree.ElementTree as ET import urllib.parse import zlib from honeypot import ASSET_MTIME, Engine, Server from test_private_boundary import PATHS events = [] server = Server(('127.0.0.1', 0), Engine(lambda event, **data: events.append(dict(event=event, **data)))) server.serve_guest_files = False thread = threading.Thread(target=server.serve_forever, daemon=True) thread.start() def request(path, method='GET', body=None, headers=None): c = http.client.HTTPConnection('127.0.0.1', server.server_port, timeout=3) c.request(method, path, body, headers or {}) r = c.getresponse() result = r.status, dict(r.getheaders()), r.read() c.close() return result try: status, headers, html = request('/') assert status == 200 and b'Citrix Gateway' in html assert headers['Set-Cookie'].startswith('NSC_') # WhatWeb passive matcher for suffix in ('lang', 'lang-ext'): path = '/vpn/js/rdx/core/' + suffix + '/rdx_en.json' status, headers, compressed = request(path + '.gz') assert status == 200 and headers['Content-Encoding'] == 'gzip' assert compressed.startswith(b'\x1f\x8b\x08\x08') and b'rdx_en.json\x00' in compressed[:100] assert struct.unpack('inert-marker' form = urllib.parse.urlencode({'SAMLResponse': base64.b64encode(xml).decode()}).encode() assert request('/cgi/samlauth', 'POST', form, {'Content-Type': 'application/x-www-form-urlencoded'})[0] == 404 saml = next(e for e in reversed(events) if e['event'] == 'saml_payload') assert saml['source'] == 'form' and saml['evidence']['sha256'] == hashlib.sha256(xml).hexdigest() raw_body = next(e for e in events if e['event'] == 'request_body' and e['request_id'] == saml['request_id']) assert raw_body['evidence']['sha256'] == hashlib.sha256(form).hexdigest() def redirect(value): compressor = zlib.compressobj(wbits=-zlib.MAX_WBITS) compressed = compressor.compress(value) + compressor.flush() return '/cgi/samlauth?' + urllib.parse.urlencode({'SAMLRequest': base64.b64encode(compressed).decode()}) assert request(redirect(xml))[0] == 404 assert any(e['event'] == 'saml_payload' and e['encoding'] == 'base64+deflate' and e['evidence']['sha256'] == hashlib.sha256(xml).hexdigest() for e in events) before = len(events) assert request(redirect(b'A' * 65537))[0] == 404 assert any(e['event'] == 'limit' and e.get('resource') == 'saml_inflation' for e in events[before:]) assert not any(e['event'] == 'saml_payload' and e['encoding'] == 'base64+deflate' for e in events[before:]) assert request('/cgi/samlauth?SAMLResponse=not-base64')[0] == 404 assert any(e['event'] == 'saml_decode_error' for e in events) assert request('/logon/LogonPoint/Authentication/GetUserName')[0] == 404 assert any({'source': 'uri', 'kind': 'gateway-username-probe'} in e.get('indicators', []) for e in events) for path in PATHS + ('/.env', '/nsconfig/ns.conf', '/api/events'): status, _, body = request(path) assert status == 404 and body == b'Not Found', path print('PASS: Gateway fingerprint, gzip metadata, auth XML, SAML bytes/inflation bounds, indicators, private boundary') finally: server.shutdown() server.server_close() thread.join(3)