March 22 2021
TL;DR ChunkyTuna is a web shell that allows near direct access to either the STDIO streams of an arbitrary process or the IO streams of an arbitrary TCP port using the “transfer-encoding: chunked” HTTP mechanism.
What is it?
In a world where security best practices are increasingly being followed, egress filtering has become commonplace. During a red team engagement we breached a web server that only allowed HTTP inbound and allowed no outbound connections. While able to upload web shells, reverse shells were unable to establish a connection back to us. Also bind shells were not an option. The only existing tool we were aware of was TUNNA (by SECFORCE). While it worked, it proved too slow for practical exploitation.
ChunkyTuna started as a web shell coded during the engagement which allowed us to pivot through the compromised server and reach further into the target network. It has since evolved to support:
- Interactive webshell
- Reverse connections from a target (to simplify exfiltration)
- Additional languages including: JSP, PHP and .NET webshells
Its key mechanism is the use of the “transfer-encoding: chunked” HTTP mechanism rather than a constant poll loop with request/response pairs. This allows data from the server to be transferred immediately to the client without waiting for a request/response pair.
The HTTP protocol is normally used in a polling fashion: a client requests data from a server, data is returned and the connection is closed. In other words, the client initiates connection; the server doesn’t.
If you want to tunnel a TCP stream (for example, output from /bin/sh) through a HTTP connection in a scenario where the server does not or cannot initiate connections, then you need a polling loop: the client must continuously ask the server if there’s new data.
This beautiful diagram explains in a nutshell how a tool like TUNNA encapsulates a TCP stream:
In the example shown above, the TUNNA client opens port 4444 on the local host, and the webshell connects to a remote target on port 3389. The attacker can then connect to localhost:4444 with a RDP client; RDP traffic is tunnelled and a connection is established with the target.
And yes, the diagram is done in Microsoft Paint 🙂
This request/response tight loop has a problem: it generates lots of traffic which is not only suspicious, but easy to throttle. Also, any latency will likely compound and cause retransmission errors.
Using the same HTTP request/response
What we’re trying to solve here is avoiding a continuous request/response loop. It turns out there’s a mechanism that’s part of the HTTP spec: the “transfer-encoding: chunked” HTTP header.
To put it into context, a typical HTTP response contains a “Content-Length” header:
However, the length of the content might not be available when requesting the resource. For example, when downloading large files or when watching a live media stream. In this case, chunked transfer encoding can be used to download the content in “chunks” as part of the same HTTP response like this:
4 Wiki 5 pedia E in chunks. 0
The numbers indicate the length of the chunk (in hex). So the first chunk has length 4 and contains the data “Wiki”. The second chunk is “pedia”, and so on. In this manner a HTTP client knows when to “stop” reading a chunk, that is when its length is exhausted. The last chunk has length 0 and indicates the end of the data. (Source: wikipedia)
If server-side data can be sent as part of the same HTTP response, we don’t have to continuously poll; this means everything can go through one response and there’s no delay in getting new data.
The wireshark screenshot below shows an interactive webshell with ChunkyTuna:
As before, the red text is the client request; the blue text is the server response.
As the picture demonstrates, the client sends the command “ls -la” to the remote shell, and the response is returned immediately – there is no need for another HTTP request and response to read the output.
In other words, ChunkyTuna allows realtime, direct access to the I/O of a remote process — without having to initiate connections from the server to the client.
What about websockets?
WebSockets are a recent development that allows servers to send data to clients without the need for a request/response pair. They would be a good mechanism for this, except they aren’t supported natively on IIS, PHP and JAVA EE6. One of the requirements for ChunkyTuna was to be as generic as possible and to run on legacy systems.
Connecting to targets behind the firewall
Just like TUNNA, ChunkyTuna allows connecting to targets “behind” a firewall as long as the web server can establish a connection to them. Time to show off my amazing Paint skills again:
The blue arrows here represent the client data; the green arrows represent the server data. All data is sent as part of the same* HTTP request and response. In this scenario an attacker is connecting to a web server that’s listening on port 80:
Notice in the picture how we’re connecting to localhost:4444 instead of the target server (which is unreachable from the attacker).
Here’s a quick demo:
ChunkyTuna has another mode of operation: it can listen for connections from targets behind the firewall. This can be used for data exfiltration as shown below:
In this example, an attacker already has a shell on the target machine. The purpose is to exfiltrate data via e.g. tar. The ChunkyTuna webshell listens on port 12345 and when data is received, it is sent to the attacker on port 4444 via the web client. Again, the server does not initiate any connection; all data is transferred as part of a chunked transfer encoding via the initial HTTP response.
Here’s the demo:
Lastly, ChunkyTuna comes with an “interactive webshell” functionality that allows realtime access to a process’ input and output.
A video is worth a thousand Paint pictures:
Again keep in mind that, unlike other webshells, this requires no outbound connections from the server.
The good, the bad and the ugly
Pushing “webshells” to its limit was not an easy task. I also wanted to create webshells that would work “out of the box” without requiring changes to server configuration or other tricks. This meant, for example, removing a “heartbeat” thread in the PHP webshell because Threads are not part of native PHP. If you want to hear me moaning about this in more detail, I’ll give a talk at B-Sides London 2018 about ChunkyTuna.
If you’re reading this after the talk, we’ll post the link to its recording as soon as it goes live.
In conclusion, ChunkyTuna is a webshell that piggy-backs HTTP chunked encoding connections to give realtime I/O to a process input and output or to a TCP stream.
- Tunna, for inspiration and being a great tool
- Sam T. (head of research, Secarma Ltd.) for writing the first prototype and coming up with the original idea
*sadly, .NET and PHP pages can’t read the content of a chunked-encoding request until it’s downloaded entirely unlike JSP pages. This means that when talking to a PHP or ASPX webshell, the client still has to send a request each time it sends data; however, server data travels through one HTTP response. Realtime I/O is still preserved, and there’s no loop for request/response because if the client doesn’t have data, no outbound request is sent.
To create a fully weaponized exploit, we need to find a gadget chain which allows arbitrary code to be executed. To avoid releasing a fully working exploit, this is left as an exercise for the reader. The vulnerable version of Woo Commerce has everything you need to make this work…