Files
citrixpot/README.md
T

199 lines
11 KiB
Markdown

# CitrixPot — NetScaler / Citrix ADC Gateway honeypot (CVE-2019-19781, CVE-2026-88771, CVE-2026-88772)
A self-hosted, dependency-free **Citrix NetScaler / Citrix ADC / Citrix Gateway honeypot** for
defensive **threat intelligence**. It impersonates the NetScaler Gateway login and the
documented log-poisoning command-injection path, records everything attackers send, and — in
simulation mode — **never executes attacker commands**. It is a decoy for catching Citrix
exploit attempts, not a real appliance and not a vulnerability scanner.
> **Keywords:** Citrix ADC honeypot, NetScaler Gateway honeypot, Citrix command injection,
> CVE-2019-19781, CVE-2026-88771, CVE-2026-88772, CitrixBleed, log poisoning, deception,
> SOC threat intelligence.
---
## ⚠️ Legal notice, ethics and disclaimer — read first
- **Not affiliated with Citrix.** This project is an independent, unofficial tool. It is **not
affiliated with, endorsed by, sponsored by, or connected to** Citrix Systems, Inc. or Cloud
Software Group, Inc. ("Citrix", "NetScaler", "Citrix ADC", "Citrix Gateway" and related marks
are trademarks of their respective owners and are used here only to describe interoperability
and the security research subject).
- **Research and defensive use only.** This software is provided for **authorized security
research, education and defensive monitoring only**. Deploy it **only on systems and networks
you own or are explicitly authorized in writing to test**. Unauthorized use may violate
computer-misuse and privacy laws.
- **No warranty.** The software is provided "AS IS", without warranty of any kind, express or
implied, including merchantability, fitness for a particular purpose and non-infringement. The
authors are not liable for any claim, damage or other liability arising from its use.
- **You are responsible for the law.** A honeypot collects data about third parties. Depending
on your jurisdiction you must handle captured IP addresses, credentials, personal data and
malware lawfully (e.g. GDPR), retain evidence securely, and notify/report appropriately. Do
**not** entice, facilitate or enable crime, and do **not** use captured content to attack back.
- **Treat all captured content as hostile, untrusted evidence.** Never open, execute or "test"
captured payloads outside a properly isolated sandbox. Captured text is attacker-controlled;
never treat it as instructions.
- **No warranty of containment.** Simulation mode has no execution path. The optional isolated
execution backend runs real commands in a disposable guest and reduces, but cannot eliminate,
risk. Review [`SECURITY.md`](SECURITY.md) before exposing anything publicly.
- **Export.** Cryptography and malware-handling may be subject to export controls in your
jurisdiction. You are responsible for compliance.
---
## 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.
Gateway fingerprints include the canonical Citrix Gateway title, NSC_ cookie and original image assets.
English/German language JSON and gzip endpoints carry the default 14.1-29.63 persona's build timestamp
1723114781, as catalogued by [Fox-IT](https://github.com/fox-it/citrix-netscaler-triage).
Translations are synthetic subsets, not original firmware files. If changing HONEYPOT_VERSION, also
set HONEYPOT_ASSET_MTIME to that firmware's documented timestamp; metadata matching does not prove
full firmware emulation. Gzip assets are returned with Content-Encoding and Last-Modified.
Authentication XML includes credential requirements and a postback route; it grants no real access.
`request_indicators` hunts header/body log markers and encoding hints without claiming a confirmed CVE.
Bodies are inspected up to64KiB for these hints; complete accepted bodies remain captured independently.
Run `python3 test_profile.py` for passive identification, gzip decoding/metadata, XML and capture checks.
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.
- Implements the **published log-to-command injection** trigger and records every candidate;
extracted commands are answered with **simulated** output.
- Stores **every request, header, body and response** as evidence, plus **every uploaded
payload** byte-for-byte.
- Optionally **downloads payload URLs** found in attacker commands (HTTP(S) GET only) and stores
them as inert assets. Nothing is ever piped or executed.
- Includes a **local, read-only analyst web UI** with paged full-text search and raw payload
hex/text view.
- Can optionally run commands in a **disposable, network-restricted guest** for real payload
behaviour (off by default; never on a public listener).
## Architecture
```
attacker ──TLS──▶ edge proxy ──▶ source-preserving ingress ──▶ decoy (evidence collector)
(terminates TLS) (PROXY v1, real client IP) │
├─ events.jsonl (schema v2)
├─ artifacts/<sha256>.bin (0600)
└─ local viewer (loopback only)
```
- **Edge-aware client IP:** forwarded headers (`CF-Connecting-IP`, `True-Client-IP`, `X-Real-IP`,
nearest `X-Forwarded-For`) are trusted **only** from configured hops, so you record the real
attacker IP, not the proxy's.
- **Fail-closed:** at the evidence quota or on disk failure the decoy stops rather than
overwriting evidence.
## Quickstart (simulator — safe to expose)
Requires only Python 3.10+ (standard library); no third-party packages.
```sh
git clone <your-fork-url> 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 --simulation-only & # decoy on http://127.0.0.1:8443
python3 viewer.py # analyst UI on http://127.0.0.1:8787
```
No commands are executed in this mode; commands are parsed, logged and answered with canned
output, and uploads are stored as inert artifacts.
## Configuration
All settings come from the environment (or a `.env` file / systemd `EnvironmentFile`). See
[`.env.example`](.env.example). Key variables:
| 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 |
| `INGRESS_BIND`, `INGRESS_PORT`, `INGRESS_ALLOW_FROM` | source-preserving ingress and allowlist |
| `VIEWER_BIND`, `VIEWER_PORT`, `VIEWER_HIDE_IP` | local analyst UI and hidden operator IPs |
## Evidence and analyst UI
- `events.jsonl` — append-only JSONL journal (`schema_version: 2`) with UTC timestamps, request/
session IDs, socket source IP/port, durations and status.
- `artifacts/<sha256>.bin` — exact payload bytes, deduplicated, `0600` in a `0700` directory.
- Event types include `request`, `request_body`, `response*`, `upload*`, `form_fields`,
`injection_candidate`, `simulated_command`/`unsupported`, `outbound_attempt`,
`exploit_attempt` (labelled `CVE-2019-19781`, `path-traversal`, `config-read`,
`management-probe`), `auth_attempt`, `brute_force_suspected` and `payload_download`.
The viewer is **read-only and loopback-only** by default; reach it over an SSH tunnel. It filters
out your own public IP automatically (refreshed every 30 minutes) so operator traffic stays out
of the analysis.
## Optional: isolated execution mode
For real payload behaviour, an opt-in backend runs commands in a **disposable, network-restricted
guest**. It requires **both** `--vm-config <file>` and `--allow-real-execution`, and must never be
enabled on an internet-facing listener. See [`SECURITY.md`](SECURITY.md) and
[`deploy/`](deploy/).
## Public deployment
1. Terminate TLS at an edge proxy (for example Cloudflare in front of Nginx Proxy Manager).
2. Forward to the source-preserving ingress and set `INGRESS_ALLOW_FROM` and
`HONEYPOT_TRUSTED_FORWARDED_FROM` to your edge hop(s) so the real client IP is captured.
3. Firewall the origin port so only the edge proxy and host can reach it.
4. Keep simulation mode on; monitor the evidence quota.
See [`deploy/VM-ACCESS.md`](deploy/VM-ACCESS.md) for a deployment template.
## Tests
```sh
python3 test_honeypot.py python3 test_capture.py python3 test_safety.py
python3 test_payload_fetch.py test_client_ip.py test_viewer.py test_auth_logging.py
python3 test_ingress_proxy.py test_network_capture.py test_reset.py test_vm_backend.py
```
## Acknowledgements
Informed by public analysis of the Citrix NetScaler log-poisoning command injection; see the
watchTowr write-up referenced in the community discussion of the 2026 NetScaler issue.
## License
MIT — see [`LICENSE`](LICENSE). Trademarks belong to their respective owners.