Improve Gateway fingerprints and header/body coverage
This commit is contained in:
1 parent
c65f049223
commit
d3b8967aa7
4 files changed
+117
-1
No files matched your search
+48
@@ -4,12 +4,14 @@ import argparse
|
||||
import base64
|
||||
import binascii
|
||||
import collections
|
||||
import gzip
|
||||
import email.parser
|
||||
import email.policy
|
||||
import hashlib
|
||||
import html
|
||||
import http.server
|
||||
import ipaddress
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
@@ -60,6 +62,24 @@ STATIC = {
|
||||
'/logon/LogonPoint/receiver/js/ctxs.core.min.js': (b"window.CTXS=window.CTXS||{};CTXS.core={version:'1.0'};\n", JS),
|
||||
}
|
||||
|
||||
# Synthetic translations; metadata matches the default 14.1-29.63 persona.
|
||||
# Source: Fox-IT citrix-netscaler-triage version timestamp catalog.
|
||||
ASSET_MTIME = env_int('HONEYPOT_ASSET_MTIME', 1723114781)
|
||||
for language in ('en', 'de'):
|
||||
translation = {'LogOn': 'Log On' if language == 'en' else 'Anmelden',
|
||||
'UserName': 'User name' if language == 'en' else 'Benutzername',
|
||||
'Password': 'Password' if language == 'en' else 'Kennwort',
|
||||
'CitrixCopyright': 'Copyright (c) Citrix Systems, Inc.'}
|
||||
for suffix in ('lang', 'lang-ext'):
|
||||
path = f'/vpn/js/rdx/core/{suffix}/rdx_{language}.json'
|
||||
encoded = json.dumps(translation, ensure_ascii=False).encode()
|
||||
STATIC[path] = (encoded, 'application/json; charset=utf-8')
|
||||
compressed = io.BytesIO()
|
||||
with gzip.GzipFile(filename=f'rdx_{language}.json', mode='wb', fileobj=compressed,
|
||||
mtime=ASSET_MTIME) as stream:
|
||||
stream.write(encoded)
|
||||
STATIC[path + '.gz'] = (compressed.getvalue(), 'application/json; charset=utf-8')
|
||||
|
||||
|
||||
class Session:
|
||||
def __init__(self):
|
||||
@@ -583,6 +603,20 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
return
|
||||
self.body_complete = True
|
||||
fields = self.capture_body(body)
|
||||
# Hunt across all intake surfaces; labels are indicators, never CVE confirmation.
|
||||
indicators = []
|
||||
for source, value in [('header:' + k, v) for k, v in self.headers.raw_items()] + [
|
||||
('body', body[:65536].decode('utf-8', 'replace'))]:
|
||||
decoded = urllib.parse.unquote_plus(value)
|
||||
if re.search(r'pitboss|NSPPE', decoded, re.I):
|
||||
indicators.append({'source': source, 'kind': 'netscaler-log-marker'})
|
||||
if re.search(r'base64|b64decode|atob\s*\(', decoded, re.I):
|
||||
indicators.append({'source': source, 'kind': 'encoded-content'})
|
||||
if source.startswith('header:') and len(value) > 8192:
|
||||
indicators.append({'source': source, 'kind': 'large-header'})
|
||||
if indicators:
|
||||
self.event('request_indicators', indicators=indicators,
|
||||
body_inspected_bytes=min(len(body), 65536), body_inspection_truncated=len(body) > 65536)
|
||||
try:
|
||||
url = urllib.parse.urlsplit(self.path)
|
||||
except ValueError:
|
||||
@@ -603,10 +637,24 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
code, data, mime = 200, LOGIN, 'text/html; charset=utf-8'
|
||||
if url.path in STATIC:
|
||||
data, mime = STATIC[url.path]
|
||||
if url.path.endswith('.gz'):
|
||||
extra_headers.append(('Content-Encoding', 'gzip'))
|
||||
extra_headers.append(('Last-Modified', self.date_time_string(ASSET_MTIME)))
|
||||
elif url.path.startswith('/nf/auth/'):
|
||||
data = ('<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">'
|
||||
'<Status>success</Status><Result>more-info</Result>'
|
||||
'<StateContext>' + html.escape(state.sid) + '</StateContext>'
|
||||
'<AuthenticationRequirements><PostBack>/nf/auth/doAuthentication.do</PostBack>'
|
||||
'<Requirements>'
|
||||
'<Requirement><Credential><ID>login</ID><Type>username</Type></Credential>'
|
||||
'<Label><Text>User name:</Text><Type>plain</Type></Label>'
|
||||
'<Input><Text><InitialValue/><Constraint>.+</Constraint></Text></Input></Requirement>'
|
||||
'<Requirement><Credential><ID>passwd</ID><Type>password</Type></Credential>'
|
||||
'<Label><Text>Password:</Text><Type>plain</Type></Label>'
|
||||
'<Input><Text><Secret>true</Secret><InitialValue/><Constraint>.+</Constraint></Text></Input></Requirement>'
|
||||
'<Requirement><Credential><ID>nsg-x1-logon-button</ID><Type>none</Type></Credential>'
|
||||
'<Label><Type>none</Type></Label><Input><Button>Log On</Button></Input></Requirement>'
|
||||
'</Requirements></AuthenticationRequirements>'
|
||||
'</AuthenticateResponse>').encode()
|
||||
mime = 'application/vnd.citrix.authenticateresponse-1+xml; charset=utf-8'
|
||||
elif url.path.startswith('/nitro/'):
|
||||
|
||||
Reference in new issue
Block a user