WebSockets are a Pain - A Journey in Learning and Leveraging

Before I dive in and show examples of implementations that worked, it is worth doing a quick explainer on what WebSockets are, how they work, what a typical request handshake looks like, and how to troubleshoot when things don't go the way you want them to.

WebSockets are a Pain - A Journey in Learning and Leveraging

This will be a quick guide on a few simple methods for leveraging WebSockets(they're nothing new, but they are an avenue I had not considered up until I was up against a wall looking for other communication protocols), a bit on C2 communications, and a piece on some short proofs of concept using both PowerShell and Linux native tooling (websocat). Initially, back in September 2022, when I started looking at WebSockets, I hated it very much:

However, after a solid 48hrs working with my two colleagues, Chris and Bman, we got some stuff working; this post will dive into some of the implementations you can use and quickly get data in and out of an environment.

What Are WebSockets?

WebSockets are a communication protocol allowing real-time, two-way communication between clients and servers over a single, long-lived connection. So what this means is essentially, they talk raw TCP in an open channel; there are two flavours, normal (WS) and web sockets secure, which use TLS to secure the connection(WSS). This is in contrast to traditional HTTP requests, which are short-lived and one-way, with the client requesting data from the server and then closing the link once the data has been received.

With WebSockets, the connection remains open; the client and server can send messages to each other anytime. This makes it possible to create real-time applications that can push updates to clients as soon as new data is available, without the client constantly polling the server for updates, many desktop applications like Microsoft Teams and Slack use websockets as they allow for asynchronous updates.

WebSockets use a standard WebSocket protocol, which is built on top of TCP/IP, and typically operate on ports 80 (HTTP) or 443 (HTTPS) which makes them great for pushing through web proxies and circumventing controls.

Why should you care?

As an attacker WebSockets provide a promising avenue for exploitation, as they're low latency, often are not blocked by web proxies and allow for easy infill and exfil of data.

  • Real-time communication: WebSockets allow for real-time, bi-directional communication between a client and server. This makes it possible to create applications that can push updates to clients as soon as new data is available without the client constantly polling the server for updates.
  • Reduced latency: Using a persistent connection, WebSockets can significantly reduce the latency in sending and receiving data between a client and server. This can lead to a more responsive user experience and faster data transfer times.
  • Lower bandwidth usage: Because WebSockets only send data when there is something new to transmit, they can be more efficient than traditional HTTP requests, which often involve sending redundant data.
  • Cross-platform compatibility: WebSockets are supported by most modern web browsers and can be used with various programming languages and platforms. This makes developing real-time applications that work across multiple devices and platforms easy.

Overall, WebSockets provide a powerful mechanism for building real-time, interactive web applications that can offer a more engaging and responsive user experience.

What a Basic Communication Handshake should look like:

WebSockets works slightly differently from traditional HTTP/HTTPS/DNS for communication in that it talks raw TCP, but the connection is initiated using an HTTP/HTTPS connection.

Both HTTP and WebSockets send messages over a TCP (Transmission Control Protocol) connection, a transport-layer standard that ensures streams of bits, sent over in packets, are delivered reliably and predictably from one system to another. Therefore, HTTP and WebSockets use the exact delivery mechanism at the packet/byte level, but the protocols for structuring the messages differ.

A typical connection request and established connection follow the steps listed:

  1. The browser or Application sends an HTTP GET request as a handshake request with an upgrade header specifying that the client wishes to establish a WebSocket connection.
  2. The request is sent to a ws: or wss: URI (translates to HTTP or HTTPS).
  3. If the server can establish a WebSocket connection and the connection is allowed (for example, if the request comes from an authenticated or whitelisted client), the server sends a successful handshake response, indicated by HTTP code 101 Switching Protocols.

To show this in actual requests, here's what the handshake request typically looks like:

GET /updates HTTP/1.1
Host: c2.zsec.uk
Connection: upgrade
Upgrade: websocket
Origin: http://blog.zsec.uk
Sec-WebSocket-Key: OEY/dyItVUt5ajctYWZHRg==
Sec-WebSocket-Protocol: html-chat, text-chat
Sec-WebSocket-Version: 13

To break this down, the first two lines are typical of what you'll see in any HTTP request regardless of the HTTP Verb; in this case, it's a GET request.

The Connection type tells the server that the client wants to upgrade the connection. The origin header tells the server where the original request came from with the last three headers disclosing the browser information about the WebSockets connection.

  • Sec-Websocket-Key Is a random, 16-byte, base64-encoded string; this can be anything (in the case of the example, I generated a random string, so if you go to decode, it'll look strange), which will be used later on to make sure that the server supports the WebSocket protocol.
  • Sec-Websocket-Protocol is a list of internal subprotocols that the client would like to use. What is a subprotocol?!? It is an internal convention regulating what data will be exchanged — Comparable to an API version number or type field. In the example above, the client effectively tells the server it would like to talk to you using the html-chat protocol. Still, if that’s not available, I can speak text-chat too, you can list as many in here as you want, and the server will fail over to the next available one.
  • Finally, Sec-WebSocket-Version tells the server which version of the WebSocket protocol should be used: version 13, released in December 2011, is the official and current standard; most modern applications and browsers will assume this anyways, but you can also specify older versions too in this header.

Then the server response is an HTTP 101 response:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

Web Redirectors

When it comes to running with command and control, web redirectors and redirectors of traffic, in general, are something you will come across for the best operational security. Don't expose your C2 to the internet!

There are ways of doing WebSockets in Apache and Nginx; having spent numerous times putting it together, I was suggested to check out Caddy, which works and flows nicely. Here are some configs that can be applied to get websockets working on a redirector of traffic through websockets.

Basic Web Redirector PoC

This simple proof of concept shows how to set up caddy as a web redirector and implement it as a web sockets redirector. To install on Ubuntu, run the following:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

Once the app is installed, you'll want to double-check it's running with:

sudo service caddy status

Followed by stopping it as we're going to modify its config.

sudo service caddy stop

Next, we want to modify the Caddyfile, where our config lives; this is stored by default in this location. /etc/caddy/CaddyfileYou're going to want to edit this file as root or with sudo:

# Caddyfile for Reverse Proxy

:80 {
        # Set this path to your site's directory.
        root * /usr/share/caddy

        # Another common task is to set up a reverse proxy:
        reverse_proxy C2Address:C2Port

# Refer to the Caddy docs for more information:
# https://caddyserver.com/docs/caddyfile

Once the file is edited, restart the Caddy service with:

sudo service caddy start

Troubleshooting and Basic PoCs

When it comes to playing with WebSockets, I found a few good tools for troubleshooting and testing out coms between client and server; there is an socat implementation for Linux that uses websockets called websocat(https://github.com/vi/websocat). This is pretty good for troubleshooting connections and understanding what a client/server connection looks like.

I also took some code that was written previously in PowerShell to talk websockets for pulling data from a machine and executing straightforward commands, which can be found here:

GitHub - ZephrFish/WebSocketsAreFun: Websockets Basic C2
Websockets Basic C2 . Contribute to ZephrFish/WebSocketsAreFun development by creating an account on GitHub.

Example Tooling

It should be noted that if you are using websockets for C2, they are typically a lot noisier in terms of traffic flow than regular HTTP/S due to the raw connection and constant flow nature. However, they offer a far quicker method for data transfer and near real-time communications.

They are also often not blocked by web proxies, so they offer an additional method for command and control communication, exfil and infill of tooling and data from an environment.

There are many websockets tools out there for legitimate dev purposes; I found PadSockets to be great for getting tooling in via Base64 blobs, in an opsec safe world. This offers zero protections but simply acts as a quick and dirty solution.

GitHub - ZephrFish/PadSockets: An online, collaborative, real-time notepad built with WebSockets and NodeJS
:notebook: An online, collaborative, real-time notepad built with WebSockets and NodeJS - GitHub - ZephrFish/PadSockets: An online, collaborative, real-time notepad built with WebSockets and NodeJS

Some C2 frameworks support websockets too out of the box and add-ons for Cobalt like external C2 channels:

Mythic has several agents and a profile that support websockets:

Go Fourth and Hack

I've linked a few tools I've been working on in this post and will continue to do some more work on tooling, as websockets offer an additional way to get data to and from environments and can be highly useful and efficient for getting information into an environment for red teams and insider threat type scenarios. I want to replicate what Updog(https://github.com/sc0tfree/updog) does well and add a layer for WebSockets to the future to-do list.