# Hysteria has an authenticated UDP ACL bypass that enables localhost and private-network UDP SSRF

Hysteria UDP relay caches ACL/outbound authorization at session creation, so an authenticated client can reuse an allowed session to reach blocked localhost and RFC1918 UDP services.

- Payout: Research
- Severity: high
- Program: Hysteria
- Found by: 0xlally
- Disclosed: 2026-06-26
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-vgrc-hq28-p3xp
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-vgrc-hq28-p3xp
- Tags: area:devices domain:network impact:internal-access kind:research platform:ghsa sev:high tech:go technique:source-review vuln:access-control vuln:ssrf

**Target:** UDP relay / outbound ACL in Hysteria server
**Root cause:** Destination is packet-scoped (each UDPMessage carries its own Addr) but ACL and outbound policy are evaluated only once when the UDP session is created; later packets call conn.WriteTo directly without re-checking policy.
**Steps:**
1. Configure ACL: `direct(127.0.0.1, udp/<allowedPort>)` and `reject(127.0.0.1/32)` (or `reject(10.0.0.0/8)`).
2. Start UDP echo on 127.0.0.1:<allowedPort> and another on 127.0.0.1:<blockedPort> (or an RFC1918 address).
3. Connect an authenticated Hysteria client and create one UDP session.
4. Send a packet to the allowed loopback destination to establish the session.
5. Reuse the same Session ID and send a packet to the blocked loopback/private destination.
6. Observe the blocked service replies — ACL was never re-evaluated.
**How found:** Noticed the protocol defines per-packet Addr and the client exposes Send(data, addr) for arbitrary destinations, then traced server code: session creation runs Hook/UDP(addr) once, but later packets in core/server/udp.go:92-111 call conn.WriteTo without re-running ACL — authorization is session-scoped while destination is packet-scoped.
**Exploitation:** Authenticated client opens a UDP session to an ACL-allowed destination, then reuses the same session ID to send packets to ACL-denied destinations (localhost or RFC1918), reaching internal UDP services from the server's network perspective.
**Insight:** When a protocol carries a per-packet destination but the server caches a connection/authorization at session scope, test whether policy is re-evaluated on subsequent packets — reuse an allowed session to pivot to denied destinations.

## Proof of concept (copied exactly from the original report)

**Integration test command for ACL bypass tests**

```
go test ./core/internal/integration_tests -run 'TestClientServerUDPACLBYPASS(Loopback|PrivateIPv4)' -count=1
```

**ACL config for localhost bypass case**

```
direct(127.0.0.1, udp/<allowedPort>)
reject(127.0.0.1/32)
```

**ACL config for private-network bypass case**

```
direct(127.0.0.1, udp/<allowedPort>)
reject(10.0.0.0/8)
```

