SOCKS5 Tunnel via WebRTC TURN Wildberries: Implementation in Go
A tool has been developed to hide SOCKS5 traffic within WebRTC TURN sessions on the WB Stream platform. Traffic is disguised as a video conference using LiveKit SFU. Full cycle: reverse HTTP API, protobuf parsing, KCP over TURN relay, yamux multiplexing. Works without a browser, with a single launch command.
Reverse Engineering the stream.wb.ru API
Traffic collection via puppeteer revealed a sequence of four HTTP requests and a WebSocket connection. The main page blocks requests without a browser (code 498), but API endpoints are accessible.
Sequence:
- POST /auth/api/v1/auth/user/guest-register — Guest JWT token with displayName.
- POST /api-room/api/v2/room — Create a room with
roomType: ROOM_TYPE_ALL_ON_SCREEN,roomPrivacy: ROOM_PRIVACY_FREE. - POST /api-room/api/v1/room/{id}/join — Join (GET returns 501).
- GET /api-room-manager/api/v1/room/{id}/token — LiveKit token (account for gzip in Accept-Encoding).
WebSocket: wss://wbstream01-el.wb.ru:7880/rtc?access_token=...&sdk=js&version=2.15.3&protocol=16. ICE servers: turn:wb-stream-turn-1.wb.ru:3478.
Implemented with net/http without cookies/CSRF.
rr, _ := wb(cl, "POST", "/auth/api/v1/auth/user/guest-register",
[]byte(`{"displayName":"lh_42"}`), "")
Protobuf Parser for LiveKit SignalResponse
LiveKit transmits ICE servers in SignalResponse > JoinResponse > ICEServer (fields 1>5/9). Full google.golang.org/protobuf is overkill — a minimal 40-line parser was written.
Varint decoding:
func pbVar(d []byte, o int) (uint64, int) {
var v uint64
for s := 0; o < len(d) && s < 64; s += 7 {
b := d[o]; o++
v |= uint64(b&0x7f) << s
if b < 0x80 { return v, o }
}
return 0, o
}
Extracting length-delimited fields:
func pbAll(d []byte, f uint64) (r [][]byte) {
for o := 0; o < len(d); {
t, n := pbVar(d, o)
if n == o { break }
o = n
switch t & 7 {
case 2:
l, n := pbVar(d, o); o = n
e := o + int(l)
if e > len(d) || e < o { return }
if t>>3 == f { r = append(r, d[o:e]) }
o = e
// ...
}
}
return
}
The parser searches for ICE servers in field 5 (WB fork) and 9 (standard), extracting urls, username, credential.
Tunnel Architecture: KCP + TURN + yamux
TCP from SOCKS5 → yamux stream → KCP (AES-256) → TURN relay → VPS → internet.
Key components:
- TURN allocation via
pion/turn:tc.Allocate()returnsnet.PacketConn. - KCP over relay:
kcp.NewConn(peerVPS, blk, 10, 3, relay). - Yamux for stream multiplexing.
- SOCKS5 server on VPS.
uc, _ := net.ListenUDP("udp", nil)
tc, _ := turn.NewClient(&turn.ClientConfig{...})
relay, _ := tc.Allocate()
kc, _ := kcp.NewConn(peerVPS, blk, 10, 3, relay)
Server: KCP listener + yamux server + SOCKS5.
Automation and Fault Tolerance
- Smart key: base64(
IP|SHA256(password)). - Reconnection: yamux ping/15s, backoff 2-60s, TURN credentials cache 5min.
- Self-cleanup:
pgrep+ kill previous instances, systemd overwrite. - Graceful shutdown: Ctrl+C → 2s cleanup.
Launch and Limitations
go build -o lionheart .
# Server
./lionheart # copy key
# Client
./lionheart # paste key → 127.0.0.1:1080
Limitations: dependency on WB Stream API, hardcoded wbstream01-el.wb.ru. Plans: gomobile for Android/iOS (VpnService/NEPacketTunnelProvider).
Key Points
- Masquerading as WebRTC traffic for DPI/firewalls at gigabit speeds.
- Minimal protobuf parser without dependencies.
- TURN relay as
net.PacketConnfor KCP. - Full automation without browser/API keys.
- Pure Go, gomobile-ready.
— Editorial Team
No comments yet.