Skip to content

feat: udp-amplifier-exposed smell — when your host is the weapon (v1.29.0) - #43

Merged
DannyRuizB merged 1 commit into
mainfrom
feat/udp-amplifier-exposed
Aug 6, 2026
Merged

feat: udp-amplifier-exposed smell — when your host is the weapon (v1.29.0)#43
DannyRuizB merged 1 commit into
mainfrom
feat/udp-amplifier-exposed

Conversation

@DannyRuizB

Copy link
Copy Markdown
Owner

Smell #38: udp-amplifier-exposed

The 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

  • Restricted source → spared: a resolver or NTP server for your own subnet is the normal, correct setup.
  • ESTABLISHED/RELATED gate → spared: those are replies to the host's own queries, not a service.
  • Any rate limit → spared, per-source or global. Unlike the TCP brute-force case, a total ceiling is exactly the right control for amplification (it caps what the host can emit) — which is what rate-limit-not-per-source already implies by scoping itself to TCP. The two smells stay coherent instead of contradicting each other.
  • OUTPUT side → never judged (BFS from INPUT/FORWARD, the unused-chain machinery): there the port is the host's own client socket.
  • TCP → never fires at all: a handshake cannot be forged, so nothing reflects.

ufw is covered, with a wrinkle worth knowing

ufw allow 53 with 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 /tcp and 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 limit that 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: limpio
  • Contadores cazados: el footer de la UI llevaba clavado en v1.26.0 (tres releases), y el titular del README + la lista de smells.

Linter: 37 → 38 smells.

🤖 Generated with Claude Code

…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>
@DannyRuizB
DannyRuizB merged commit 6782d58 into main Aug 6, 2026
1 check passed
@DannyRuizB
DannyRuizB deleted the feat/udp-amplifier-exposed branch August 6, 2026 12:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant