Back to Articles

wsrepl: The Interactive WebSocket REPL Built for Security Testing

[ View on GitHub ]

wsrepl: The Interactive WebSocket REPL Built for Security Testing

Hook

Copy a WebSocket connection from Burp Suite, replace 'curl' with 'wsrepl', and you're instantly in an interactive REPL with full message history and automation capabilities—this is the workflow wsrepl's developers had in mind.

Context

WebSocket penetration testing has always existed in an awkward middle ground. Browser DevTools excel at observation but fail at manipulation. Traditional HTTP tools like curl don't speak WebSocket's persistent connection protocol. Full-featured proxy tools like Burp Suite offer WebSocket support, but their GUI-driven workflows slow down rapid iteration and scripting.

Pentesters found themselves cobbling together custom Python scripts for each engagement, rewriting the same connection logic, message history tracking, and authentication replay mechanisms over and over. wsrepl emerged from Doyensec—a security research firm—to solve this exact problem: provide a curl-like command-line interface for WebSocket connections with a REPL for interactive exploration and a plugin system for automating repetitive security testing patterns. It's not trying to be a general-purpose WebSocket client; it's laser-focused on the penetration testing workflow where you need to establish a connection quickly, observe traffic, send crafted payloads, and automate multi-step authentication or fuzzing scenarios.

Technical Insight

wsrepl's architecture centers on three design decisions that make it particularly effective for security work: curl-compatible argument parsing, an asyncio-based interactive REPL with message history, and an inheritance-based plugin system with lifecycle hooks.

The curl compatibility is more than syntactic sugar. When you intercept a WebSocket connection in Burp Suite or copy it from Chrome DevTools, you get a curl command with all headers, cookies, and connection parameters. wsrepl accepts these same arguments—-H for headers, -x for proxy, -k for insecure SSL, -b for cookies—meaning you can literally paste the command and replace the binary name. This eliminates the context-switching friction that kills momentum during security assessments. The tool also supports initial message replay with -d, allowing you to establish a connection and immediately send authentication or initialization sequences copied from legitimate traffic.

The REPL itself is built on Python's asyncio, running the WebSocket connection in a background task while presenting an interactive prompt. Every message—sent or received—is logged with timestamps and assigned an ID, creating an audit trail that's essential when documenting vulnerabilities or understanding complex state machines. Here's what a basic session looks like:

# Connect to a WebSocket endpoint with custom headers
$ wsrepl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJ..." \
  wss://api.example.com/v1/stream

[*] Connected to wss://api.example.com/v1/stream

# Messages appear automatically as they arrive
< [1] {"type":"welcome","session":"abc123"}

# Send a message interactively
> {"action":"subscribe","channel":"orders"}

# View message history
>>> history
[1] << {"type":"welcome","session":"abc123"}
[2] >> {"action":"subscribe","channel":"orders"}
[3] << {"type":"subscribed","channel":"orders"}

# Send a crafted payload to test injection
> {"action":"search","query":"'; DROP TABLE users--"}

The plugin system is where wsrepl transitions from interactive tool to automation framework. Plugins inherit from a base class and implement hooks for WebSocket lifecycle events: on_connect, on_message_sent, on_message_received, on_disconnect, and on_close. This allows you to build reusable automation for common patterns without touching wsrepl's core code.

Here's a practical plugin that automatically handles JWT token refresh during long-running fuzzing sessions:

from wsrepl.plugins import BasePlugin
import json
import jwt
from datetime import datetime, timedelta

class JWTRefreshPlugin(BasePlugin):
    def __init__(self):
        self.current_token = None
        self.refresh_token = None
    
    def on_message_received(self, message):
        # Extract tokens from authentication response
        try:
            data = json.loads(message)
            if data.get('type') == 'auth_success':
                self.current_token = data['access_token']
                self.refresh_token = data['refresh_token']
                print(f"[Plugin] Stored tokens for auto-refresh")
        except json.JSONDecodeError:
            pass
    
    def on_message_sent(self, message):
        # Check if token is expiring soon and refresh preemptively
        if self.current_token:
            try:
                decoded = jwt.decode(self.current_token, 
                                   options={"verify_signature": False})
                exp_time = datetime.fromtimestamp(decoded['exp'])
                
                # Refresh if less than 5 minutes remain
                if exp_time - datetime.now() < timedelta(minutes=5):
                    refresh_msg = json.dumps({
                        "action": "refresh_token",
                        "refresh_token": self.refresh_token
                    })
                    self.send_message(refresh_msg)
                    print(f"[Plugin] Auto-refreshed JWT token")
            except Exception as e:
                pass

This plugin architecture means you can build a library of reusable security testing components—authentication handlers, fuzzing engines, response validators—that work across different engagements. The hooks receive and can modify messages in-flight, enabling sophisticated man-in-the-middle scenarios during testing.

wsrepl also implements features that matter specifically for security testing: custom ping/pong frame handling (both legitimate WebSocket pings and fake 0x1 opcode messages for testing server parsing), proxy support for routing through Burp Suite or other interception proxies, and SSL verification toggles for testing development environments with self-signed certificates. The auto-reconnection feature is particularly useful during fuzzing when you might crash the server—wsrepl will automatically re-establish the connection and continue from where it left off.

Gotcha

The curl compatibility is excellent until you need features that curl doesn't have. wsrepl inherits curl's argument parsing limitations, which means expressing complex scenarios sometimes requires awkward workarounds. For instance, sending multiple initial messages requires chaining -d flags, but there's no clean way to specify timing delays between them without writing a plugin.

Binary protocol handling is wsrepl's Achilles heel. While it technically supports binary WebSocket frames, the REPL interface is clearly designed for text-based JSON or XML protocols. If you're testing a binary protocol like protobuf over WebSocket, you'll spend more time fighting the interface than actually testing. The lack of built-in support for WebSocket extensions (per-message deflate compression, for example) means you might encounter compatibility issues with production systems that mandate compression.

The plugin documentation is sparse beyond the basic examples in the repository. Building anything beyond simple message logging requires reading wsrepl's source code to understand the plugin lifecycle and available methods. There's no plugin repository or community-contributed examples, so every shop ends up building similar authentication and fuzzing plugins from scratch. The tool's 236 stars suggest a small but dedicated user base—you're unlikely to find Stack Overflow answers when you hit edge cases.

Verdict

Use if: You're conducting security assessments that involve WebSocket endpoints and need rapid iteration between observation, manipulation, and automation. The curl compatibility makes it essential if your workflow involves Burp Suite or browser DevTools, and the plugin system shines when you need to automate multi-step authentication flows or run long-duration fuzzing sessions against stateful WebSocket APIs. It's particularly valuable for teams that want to build reusable security testing components across engagements. Skip if: You're doing general WebSocket development or debugging (browser DevTools are simpler), working primarily with binary protocols (websocat has better binary handling), or need a mature tool with extensive community plugins and documentation. Also skip it if you're already paying for Burp Suite Pro and don't need scriptability—Burp's WebSocket support integrated with its other tools might be more efficient for ad-hoc testing.