feat: udp-amplifier-exposed smell — when your host is the weapon (v1.29.0) - #43
Merged
Conversation
…29.0) The first smell that asks the opposite question. Every other one asks "who gets INTO this host?"; this asks who the host ATTACKS. UDP takes no handshake, so a reflector answers a forged source address: a small query claiming the victim's IP comes back to the victim much bigger. That is how a small VPS becomes a DDoS weapon — 1.35 Tbps off open memcached (GitHub, 2018), 400 Gbps off open NTP and DNS. Ports carry their documented amplification factor (US-CERT TA14-017A and the memcached / WS-Discovery advisories) so the finding states the real multiplier: chargen 358x, dns 54x, rpcbind 28x, ntp 556x, cldap 70x, ssdp 30x, ws-discovery 500x, memcached 51,000x. Limited to ~25x and above on purpose: SNMP, NetBIOS and mDNS reflect too, but a smell that fires on every LAN service earns nothing. Exemptions: a restricted source (a resolver for your own subnet is the correct setup), an ESTABLISHED/RELATED gate (replies, not a service), ANY rate limit — per-source or global, because unlike the TCP brute-force case a TOTAL ceiling is exactly the right control here, which is what rate-limit-not-per-source already implies by scoping to TCP — and only chains reachable from INPUT/FORWARD (BFS over jumps; on the OUTPUT side the port is the host's own client socket). TCP never fires: a handshake cannot be forged. ufw IS covered, and a protocol-less `ufw allow 53` counts as UDP — it opens both transports, which is exactly how an accidental open resolver gets created. Composes with exposed-admin-port on memcached UDP: two different costs on one rule (someone reads your cache / your host attacks a stranger). New sample where nothing is hacked and every rule was added on purpose, including an NTP accept with -m limit that is correctly NOT flagged. Tests 165 -> 174. Counters caught: footer stuck at v1.26.0 (three releases), README headline count and smell list. 37 -> 38 smells. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Smell #38:
udp-amplifier-exposedThe first smell that asks the opposite question. Every other one here asks "who gets into this host?". This one asks who the host attacks.
UDP takes no handshake, so a reflector answers a forged source address: the attacker sends a small query claiming the victim's IP, and this host mails the victim a much bigger reply. That is how a small VPS becomes a DDoS weapon — the 1.35 Tbps GitHub flood (2018) came off open memcached, the 400 Gbps Spamhaus / OVH floods off open NTP and DNS. The cost is not "someone reads your cache" (
exposed-admin-port's axis, which composes here for memcached) — it is your uplink saturated and your provider's abuse desk calling about traffic you never sent.The port table states the real multiplier
From US-CERT TA14-017A and the memcached / WS-Discovery advisories: chargen 19 (358x), dns 53 (54x), rpcbind 111 (28x), ntp 123 (556x), cldap 389 (70x), ssdp 1900 (30x), ws-discovery 3702 (500x), memcached 11211 (51,000x).
Deliberately limited to factors around 25x and above — SNMP (6.3x), NetBIOS (3.8x) and mDNS (10x) reflect too, but a smell that fires on every LAN service earns nothing.
Exemptions that keep it honest
rate-limit-not-per-sourcealready implies by scoping itself to TCP. The two smells stay coherent instead of contradicting each other.unused-chainmachinery): there the port is the host's own client socket.ufw is covered, with a wrinkle worth knowing
ufw allow 53with no protocol opens TCP and UDP, so a protocol-less ufw allow counts as UDP here — that spelling is exactly how an accidental open resolver usually gets created (pinned by test alongside explicit/tcpand a LAN-scoped/udp).Sample
New iptables (UDP reflector — DDoS amplifier): a box where nothing is hacked and every rule was added on purpose — a resolver "widened while debugging" (the LAN-scoped original still sits right above it), memcached opened "so the other container could reach it", SSDP for a media server, and an NTP accept with
-m limitthat is correctly not flagged.Verificado en local
npm test: 174/174 (9 tests nuevos: fires+multiplier, las 4 exenciones, UDP-only, dirección inbound, nft con set, las 4 grafías de ufw, la composición con exposed-admin-port, y el sample)npm run lint: limpioLinter: 37 → 38 smells.
🤖 Generated with Claude Code