wsrepl: The Interactive WebSocket REPL Built for Penetration Testing Workflows
Hook
The average penetration tester copies a curl command from browser DevTools at least a dozen times per assessment—but WebSocket connections don’t work that way. Until now.
Context
WebSocket testing during security assessments has always existed in an awkward middle ground. Browser DevTools let you inspect WebSocket frames, but you can’t easily replay or modify them. Burp Suite can intercept WebSockets, but switching between proxy mode and interactive testing breaks your flow. Command-line tools like websocat handle basic connectivity, but they’re designed for DevOps workflows, not penetration testing scenarios where you need to authenticate, maintain session state, and inject payloads between legitimate messages.
The problem compounds when you’re testing modern real-time applications—chat systems, collaborative editors, live trading platforms—where WebSocket is the primary transport. You find yourself constantly context-switching: copy the WebSocket URL from DevTools, craft a connection script, handle authentication, implement message history logging, write custom logic for testing authorization bypasses or injection attacks. Each step introduces friction. wsrepl emerged from Doyensec’s penetration testing practice as a purpose-built solution: a WebSocket REPL that understands offensive security workflows, speaks the same language as your existing tools, and gets out of your way when you need to automate.
Technical Insight
The architectural genius of wsrepl lies in its curl compatibility layer. When you right-click a WebSocket connection in Chrome DevTools and select ‘Copy as cURL’, you get an HTTP request—but wsrepl parses these curl commands and extracts the headers, cookies, and authentication tokens you need for the WebSocket upgrade. Replace curl with wsrepl and you’re connected:
wsrepl 'wss://target.com/api/ws' \
-H 'Cookie: session=abc123' \
-H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc...' \
--proxy http://127.0.0.1:8080
This single design decision eliminates the most tedious part of WebSocket testing: manually reconstructing authentication state. Under the hood, wsrepl uses Python’s asyncio and websockets libraries to maintain the connection while presenting an interactive shell. The architecture separates concerns cleanly: connection lifecycle management (connect/disconnect/reconnect with exponential backoff), message handling (bidirectional frame inspection with full history), and automation hooks through a plugin system.
The plugin system is where wsrepl transitions from simple REPL to automation framework. Plugins inherit from a base class that exposes lifecycle hooks—on_connect, on_message_received, on_message_sent, on_disconnect, and more. Here’s a practical example for testing authentication bypass in a chat application:
from wsrepl.plugin import WSPlugin
import json
class AuthBypassPlugin(WSPlugin):
def __init__(self):
self.message_count = 0
async def on_connect(self, websocket):
# Inject authentication payload immediately after connection
auth_msg = json.dumps({"type": "auth", "token": "malicious_token"})
await websocket.send(auth_msg)
self.log("Sent bypass attempt")
async def on_message_received(self, websocket, message):
self.message_count += 1
data = json.loads(message)
# If we receive admin-only message types, bypass succeeded
if data.get("type") == "admin_broadcast":
self.log(f"[!] Auth bypass successful at message {self.message_count}")
# Auto-respond to keep-alive without manual intervention
if data.get("type") == "ping":
pong = json.dumps({"type": "pong", "timestamp": data["timestamp"]})
await websocket.send(pong)
This plugin automatically handles authentication injection and keep-alive responses while flagging privilege escalation indicators—the kind of semi-automated workflow that’s painful to implement in traditional proxy tools.
wsrepl also implements dual ping/pong mechanisms that expose a subtle but important testing capability. The WebSocket protocol defines native ping (0x9 opcode) and pong (0xA opcode) frames for connection health checks. But many custom WebSocket implementations use text frames (0x1 opcode) with application-level “ping” messages instead. wsrepl supports both:
# Native WebSocket ping/pong
> /ping
< Pong received in 45ms
# Fake ping using text frame (tests application logic)
> /fakeping {"type": "ping"}
< {"type": "pong", "server_time": 1634567890}
This distinction matters when testing WebSocket implementations that might handle protocol-level and application-level keep-alives differently, potentially exposing race conditions or state management bugs.
The message history system persists every frame to disk with timestamps and direction markers, creating an audit trail that’s essential for penetration testing reports. When you discover an authorization bypass six hours into testing, you can scroll back through history to document the exact sequence of messages that triggered it—something ephemeral browser DevTools can’t provide.
Gotcha
The terminal UI, while lightweight and SSH-friendly, becomes a liability when dealing with binary WebSocket frames or deeply nested JSON structures. If you’re testing a WebSocket API that sends MessagePack or Protocol Buffer payloads, you’ll be staring at hex dumps in a 80-column terminal instead of a structured tree view. The lack of built-in payload encoding helpers means you’re manually base64-encoding or hex-escaping data that GUI tools would handle automatically.
The plugin system’s Python-only constraint creates friction for quick one-off transformations. Want to pipe incoming messages through jq for filtering or xmllint for formatting? You’ll need to write a Python plugin that shells out to those tools instead of composing simple Unix pipelines. For teams that prefer shell scripting over Python development, this architectural choice adds overhead. Additionally, wsrepl lacks a library of common penetration testing payloads (XSS vectors, SQL injection strings, JWT manipulation) that you’d find in mature offensive security tools—you’re building test cases from scratch or importing them manually in your plugins.
Verdict
Use wsrepl if you’re conducting web application penetration tests where WebSocket is a primary attack surface and you need the flexibility to quickly transition from manual message inspection to semi-automated testing through Python plugins. It excels when you’re working through SSH on remote pentest infrastructure, need audit trails for compliance reporting, or find yourself repeatedly copying authentication tokens from browser sessions. The curl compatibility eliminates setup friction that makes ad-hoc WebSocket testing painful. Skip it if your testing methodology relies on GUI tools for visualization, you need comprehensive fuzzing capabilities beyond custom plugin logic (stick with Burp Suite Professional), or you’re testing binary WebSocket protocols where hex dump readability matters. Also skip if your team prefers shell scripting over Python—websocat’s pipe-friendly design will fit your workflow better.