One Part Steganography, Four Redirectors, and a Splash of C2!

Curtis Brazzell
9 min readSep 20, 2020



What do you get when you combine Google Images, QR Codes, and Remote Command Execution? This silly project of mine I’d like to share with you all, of course! Building off of my security research from my last couple of blogs, I decided to use my research using dynamic web content to proxy traffic over third party image providers, and try to find a valid bi-directional method for sending data between a NAT’d client and a public server. Alternatively put, I wanted to see if I could build my own crude Command and Control (C2) framework from scratch which proxies traffic through third party image servers (Google, Imgur, Imgflip, etc) all via only encrypted GET requests in order to fool Blue Teams while doing convert Red Team operations. There are a million flavors of C2 frameworks these days so my objective here wasn’t to reinvent the wheel but to prove the channel technique is useful and unique. I did some preliminary Google searches and didn’t see anything being done like this before so, let’s get to work!

In my last blog I found how to get data out via a similar method, but I didn’t really have a plan for getting data back. The technique I highlighted would be more useful for proxying credentials and data exfil since it was one direction. Steganography came to mind since I was using an image proxy, but metadata with Exif techniques are limited. It hit me later that a QR code is an image that holds up to 4,296 alphanumeric characters. iQR can improve upon that and even support a staggering 40,637! No need to scan these in manually with a mobile device since I can write code to process the C2 server’s commands into a QR code which can be read by the agent (client) and processed. I also had a plan which I’ll detail below to change the content being presented in the event a SOC analyst decided to make the same web requests themselves.

I think Redirector techniques are awesome because you don’t have to burn your domains and IP addresses that host your C2. It also has the added benefit of working like an encrypted proxy so watching eyes and network monitoring tools are less likely to notice anomalies. My project has the added benefit of being part of a regular network flow because, everyone requests images from places like Google and Imgflip, right?! How else would you pass the time if not by checking the latest memes in your #random Slack channel? Yeah, me neither..

Getting Started

The first thing I wanted to do was write a simple python script to see what was involved with executing commands on the OS and encoding the output as a QR image. Then, I wrote another little script to read that image in and spit out the results on my terminal. There’s something neat about scanning an image on your phone and seeing the contents of your Windows directory spit out. Okay maybe that’s just me?

Go on, you know you want to scan it..

Well great! That works, so in theory this should at least work with a direct connection between an agent and a C2 service. I decided to stick with python to act as a web server using Flask to handle request parameters. I also just used python again for the agent (for now) because again, I’m not trying to make the world’s best C2 and I do want to support multiple operating systems on each end.

I then drew up a plan for how the data would be exchanged. Initially I wanted to use QR codes to both issue commands from the C2 and to send output back from the agent. However, I also wanted to incorporate third party image providers and from initial research it seemed they all used POST requests to upload images, which makes sense. I figure Blue Teams might be a little more suspicious if I’m submitting just as many images to Imgur as I am receiving them. How do they know I’m not just really good at making viral memes? Still, I’d rather find a way to stick to only GET requests and since they’re encrypted with HTTPS no one would see the URI or request body. I decided to use my exfil technique from my last blog, even though submitting data in the URI is limited to approximately half of what the QR code supports (~ 2000). Commands would still be read by the agent as QR though and encoded with Base64 for data integrity reasons. Besides, the data coming back has to be an image since these third party image services require a valid content type.

Google Image Search via URL (Using a Web Path of “img.png” and Parameters)
Response Containing a QR Image Returned from the Server
Burp Request for Searching by Image (With Full URL to the C2)

Fast Forward

Skipping ahead after a steep learning curve because I’m a terrible developer, I finished the code for the scrappy C2 listener and the server-side terminal script that interacts with the listener and underlying SQLite database. You can mock my code on Github here and use it as you wish, but please use it only for legal reasons. I wouldn’t use it in a live environment though because I’m sure there are injection issues. I’ll get the code buttoned up but as it stands it’s a rough PoC. It receives commands and works as you’d expect, by allowing a user to interact with newly checked-in agents and displaying results of any commands received to the terminal. It only executes shell commands and there aren’t any fancy modules or anything. When you issue a new command to an agent, your request is Base64 encoded and converted to a QR code which is hosted by the listener in a “img.png” virtual path. This is a technique I used in my last blog as well to bypass restrictions third party services like Google Images impose to make sure you’re using a valid image extension in addition to the content-type. Man I’m a rambler.. so sorry. I’ll hurry up and get to the goods!

I played around with the commands and the functionality in the database to make sure it was working as expected. It was! I then moved on to creating the agent. The agent was built first to use a direct connection but I quickly added support for Google Images and Imgflip. I haven’t implemented Imgur at the time of this writing but it’s simpler than Google because of less strict anti-scripting measures I had to get around, so I expect to add that very soon. The agent works by making a ping request for an image and supplies an “Agent ID” (GUID generated upon agent execution) along with another parameters to what looks like an image path. The Flask server receives the parameters and updates the database to show that the agent is new or has checked it. The server then looks to see if that agent has received a command to execute and, if it does, displays the QR image previously created in the previous paragraph. Once it’s accessed, the QR code is deleted so if the same request is repeated again all anyone would see is Agent Smith from The Matrix. It’s okay, I’m a Dad so I can make these jokes! I also got a kick out of how the server was saving all of the Agent Smith images for each agent and it resembled the clones in the third (not so good) movie taking over my machine.

Mr…… Anderson……

It worked beautifully! If a command executes and needs to be sent back to the C2 service, it makes essentially the same request as the check-in beacon but adds the output of the command as a Base64 encoded value in a GET parameter called “response”. The C2 receives it, decodes it, and displays it to the user’s terminal. I added a random time interval for the beacon between 1 and 10 seconds so it’s not as predictable and obvious. The most you’ll have to wait is 10 seconds between requests which doesn’t seem unreasonable to me. This value could be changed of course to speed things up.

Everything Working!

Going the Extra Mile

The C2 and Steganography transport method were working great, but I still wanted to add the Redirector component to my recipe. I knew Google Images was going to be tricky because of how they require cookies and other valid HTTP headers with requests. The response also redirects to another call and the HTML content of the image search would need to be parsed to find the image being returned by the C2 server. I also had to add a random value for cache busting purposes or Google would just return the previous call’s image. I’ll spare you all the details and tell you I figured it all out through trial and error and added that function to the agent. When you load up the agent you can choose a direct connection or Google Images, which will proxy your HTTPS calls through Google servers and back and forth between your C2. Traffic in Wireshark verified what I hoped to see.. nothing but encrypted TLS packets. YASS! It was 3 AM and I may or may not have done a touchdown dance at this point. You can’t prove anything.

C2 Sever / Agent Side by Side Execution
Four Image Redirector Methods for the C2 Channel
TLSv1.3 Traffic Between my Agent and Google Images Servers

In Conclusion (Finally.. sheesh)

Stegranography is nothing new, but I’ve always wanted to work on a project that used it in some way. It’s just really neat but it’s not the most practical thing and there are typically limitations for large transactions. Redirectors are neat, but the techniques change rapidly because methods like domain fronting get shut down. Third party image servers will likely make it harder to do this without authentication if this blog actually gets some attention. C2 frameworks use a variety of protocols and other methods to send and receive data discretely. However, I’ve convinced using all three of these together lends some attention for Red and Blue Teams and can overcome all of these limitations that they have by themselves. As you can see from my Windows dir output, you can hide a lot of data in a QR code (especially iQR!) that can be read quickly by software. I plan to update my code and add a “parts” parameter, so requests can be chunked up in case the output is huge. The C2 listener could simply wait for all of the pieces to arrive in the database before displaying it to the user. I also love that by using third party image servers I’m not only hiding in normal looking traffic but it has the benefit of being encrypted and does not require me to use a domain. Sure, an analyst could look at the agent and determine what’s going on under the hood but by then the game is up. I’d like to see this technique added to real C2 frameworks eventually so I don’t have to paste a Powershell launcher into my crappy (wait, did I say “scrappy” before?) python one. I feel it needs to be said again that mine is just to demonstrate the technique.

One last point I want to make is that this will basically work on any service that using unfurling, such as Twitter, LinkedIn, Slack, Microsoft Teams, etc. There are MANY. You could modify this code to post an “image” that sends a request to the C2 and when the image is unfurled, it will be the QR with the next command. You’d want to use a cache buster the same way I did for Google. The subsequent requests would work the same way this C2 does with the image servers. Taking things even further (as an observant Reddit user pointed out), you could use gifs instead of pngs and put a different QR in each frame to improve length restrictions and not have to make multiple requests!

I hope everyone reading this or using my code gets something out of it! This made me appreciate the frameworks out there I take for granted on an almost daily basis. Oh, and here are some relevant images from my two new Cybersecurity ABC children’s books, part of the “M is for Malware” series. Feel free to pre-order now on :)

“R is for Red Team” (Robots) Book on Left and “B is for Blue Team” (Dragons) Book on Right



Curtis Brazzell

Passionate geek for Information/Cyber Security! I’m always learning and am happy to contribute anything I can share with the community. Follow me @ Twitter!