"""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)