Pupy WebSocket Transport

Pupy is an open source, cross-platform (Windows, Linux, OSX, Android) remote administration and post-exploitation tool mainly written in Python. It’s easily expandable, and includes stackable network transports for C2 communication. It’s for this reason, that I recently chose it as a base for a Red Team operation against a “security-tough” target.


I knew that the target had a corporate proxy, and most likely had SSL decryption capabilities. Therefore, all stages of the payload needed to be proxy aware. Pupy offers the auto_proxy option, but may require some tweaking to get a desired payload stages to be aware of default proxies. The engagement time per the scope was limited, so the RAT also needed to be easy to work with. I typically work with Python during my day to day, so this was the perfect fit.


WebSocket is a communications protocol that offers full-duplex communication streams over a single TCP connection. The WebSocket protocol is defined in RFC 6455. WebSockets are an interesting choice of transport, because it works well with existing proxy technologies, and proxies are notoriously limited in their ability to observe traffic other than standard HTTP requests.

Example: Cisco WSA / Ironport per public discussion cannot inspect WebSocket traffic. Forum Discussion

Pupy Transports

Pupy transports are located in pupy/pupy/network/lib/transports/ while configuration files are located in pupy/pupy/network/transports/<transport>/conf.py. Allowed transports are stored in pupy/pupy/network/lib/picocmd/picocmd.py.

Added Transports

I used the http transport as a base for development, and nabbed some code from Pithikos’s WebSocket Server.

Grab updated Transport

I have submitted a pull request to the main Pupy repository, but I’m not sure when it will get accepted. For now, you can get a copy of the updated code using the following.

git clone https://github.com/bitrot-sh/pupy.git pupy && cd pupy

Binary templates are available from the regular source.

wget https://github.com/n1nj4sec/pupy/releases/download/latest/payload_templates.txz
tar xvf payload_templates.txz && mv payload_templates/* pupy/payload_templates/ && rm payload_templates.txz && rm -r payload_templates

Nginx Redirector

Integrating with a more robust C2 is a must, as we don’t want to directly expose the C2 node to all the scanners and spammers on the Internet at large. Hiding behind redirectors also helps make our C2 more resilient, and we can simply add and remove redirectors as the get burned throughout the campaign.

Nginx offers the ability to run as a reverse proxy for Websocket connections.

location /wsapp {
    # Add any rules here to exclude non-agent connections
    proxy_http_version 1.1;
    proxy_set_header Host www.example.com;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    # Keep these open
    proxy_connect_timeout 7d;
    proxy_send_timeout 7d;
    proxy_read_timeout 7d;

Pupy Agent Generation

I’ll just generate a quick python stub for testing to ensure that the transport works. The transport will connect back to localhost port 8080 (nginx). Nginx will then redirect the traffic to port 9090. Typically you would have the C2 on a dedicated host, and just add additional redirector servers in front of it. For demonstration exercises, this will all just be localhost.

./pupygen.py -O linux -A x64 -f py --randomize-hash auto_proxy --transport websocket --host localhost:80

Pupy Handler

Per the configs, we’re running the C2 on port 9090. Nginx will handle routing the agent traffic on port 80.

./pupysh.py -t websocket --port 9090

Agent -> C2

Agent -> C2

Wireshark Traffic

Wireshark Traffic



  1. Red Team Infrastructure Wiki
  2. Nginx
  3. Pupy
  4. WebSocket RFC 6455
  5. Python
comments powered by Disqus