How to Build a 404 page not found C2

66619265A C2, or command-and-control, is used by attackers to control compromised systems. Most of these C2s are in control of large botnets, yet some are simply used by an attacker to have access to a system so they can pivot to another device or to steal credentials and gain “legitimate” access to the system.

I have seen and heard of many types of C2s out there, such as IRC, p2p, DNS, Twitter, Gmail, ICMP, and etc.  This list keeps getting bigger, and the ways C2 are implemented get more inventive everyday. I am rarely ever surprised by any new C2 I hear about.  Though one day having a conversation with John Strand, he mentioned a C2 that uses an HTTP 404 – File Not Found.  This…got my attention.  As a security professional, most of these techniques (but not all) are fairly easily detected and stopped.

Though the HTTP 404 – File Not Found would be a little more difficult to detect, filtering/blocking HTTP 404 – File Not Found altogether would be easy to stop. But how many environments block HTTP 404?  If fact how many environments out there monitor and view website HTTP 404?  If you are like me, when looking through network traffic, HTTP 404 is generally overlooked or skipped.  When combing through traffic, I am mostly looking for any anomalies in the traffic, traffic going to odd places on the Internet, or other things of that nature.

With my interest piqued, I started to do a little research to see if anyone has used this attack and if anyone has detected this type of C2 in the wild. I found a couple of writings, one a Black Hat white paper titled, “Hiding in Plain Sight” by Pierre-Marc Bureau and Christian Dietrich (, and another one, “Hiding Malicious Traffic Under the HTTP 404 Error” by He Xu ( The Black Hat white paper references the Fortinet paper by He Xu, and I used the Fortinet paper to model my proof of concept.  

In the article by He Xu, they actually detected and witnessed the HTTP 404 – File Not Found C2, and the article covers what they found and what was happening. Basically an infected device would reach out to this web server, but would get back a HTTP 404 – File Not Found.  The HTTP 404 seemed benign, however a comment on the source page had base64-encoded commands. These commands were instructions for the bot to replicate itself to USB drives, download and execute an executable, and finally to change some registry keys. Based on this article and the way the bot acted, I decided to create my own HTTP 404 – File Not Found C2.  Though instead of just having the compromised system get a command and run those instructions, I wanted it to be able to control and get a response from a system via the HTTP 404 – File Not Found.

The first part was setting up the web server by adding and configuring the .htaccess file to direct any error pages to a .html file of my choosing (below I redirect it to evil.html) .

Fuji2:Users:Mateo:Desktop:Screen Shot 2016-07-01 at 10.05.13 AM.png

As an attacker this web server could be one they setup and control directly or a server they have “access” to. Once the site was setup and the HTTP 404 – File Not Found was setup and working, I moved on to part 2, the C2 server.

This was the easiest part of code.  The code would wait for me to input a command that I wanted to give to a controlled device: base64 encode it, put it in an html comment with a predefined header, and wrap it into an html file.  It would then overwrite the current HTTP 404 – File Not Found html file.  This would overwrite the HTTP 404 – File Not Found file only when I entered a new command. For my testing, I setup and controlled the web server, though if it was a compromised server, I could have easily used FTP to upload new html files.

Fuji2:Users:Mateo:Desktop:Screen Shot 2016-07-01 at 12.06.29 PM.png

The next step was to create the C2 client.  A few things I wanted from the client; the ability to control the client with commands that would work for both windows and Linux, and a response from the commands back to a listening server.

The client had to reach out to a domain.  I used a static URL, but this could easily be changed to ask for a random page. Once it requested a page from the site, it would first determine if it was a 404 page.  If it was not a 404 page, then ignore and wait until the next request goes out.  If it is a 404 page not found, then check to see if it has comments in the source code.  If it finds comments, then check to see if the header in the comments matches a predefined header.  If the header matches, then decode the base64 string and execute the command(s).

Fuji2:Users:Mateo:Desktop:Screen Shot 2016-07-01 at 12.13.40 PM.png

This part of the code was a little more difficult as I wanted to not only execute commands on the client system, I also wanted to be able to give basic commands to Linux and windows alike. Then finally I needed to send the results of the executed command(s) back to a server.

What I did to fix this on the C2 client was to determine if the compromised system was Linux or Windows.  If the system was Linux then execute the commands as if the OS was Windows, then execute it as a PowerShell script re-encoded in base64.  Using PowerShell for the Windows OS would give some of the same basic commands as it would in Linux.

The final part of the C2 client then would send the results of the commands back to a listening server.  I chose for testing, a python server for a listener.  The way you send the commands back could be more elaborate to bypass egress filtering and packet filtering. But, for simplicity and ease, I just used a python server listener.

Finally, I created the C2 receiver for the results for the commands sent by the C2 client.  This was just a basic python server that is waiting for a connection.

For my demo I am using a Windows 10 machine as the “victim” and an Ubuntu desktop as the web server, the C2 controller, and the C2 receiver.

You can see the website is an Office 365 login page.

Fuji2:Users:Mateo:Desktop:Screen Shot 2016-07-01 at 12.32.35 PM.png

Though once I go to a page that does not exist, I should get a 404 error.

Fuji2:Users:Mateo:Desktop:Screen Shot 2016-07-01 at 12.34.33 PM.png

And you will notice that the source page does not have any comments, just a basic web page.

Fuji2:Users:Mateo:Desktop:Screen Shot 2016-07-01 at 12.51.16 PM.png

The C2 Server starts and waits for a command

Fuji2:Users:Mateo:Desktop:Screen Shot 2016-07-01 at 12.48.06 PM.png

The C2 command receiver opens a port and waits for an incoming connection.

Fuji2:Users:Mateo:Desktop:Screen Shot 2016-07-01 at 12.54.58 PM.png

The C2 client is then run on the victim machine(not much to see, the file starts then runs in the background).Fuji2:Users:Mateo:Desktop:Screen Shot 2016-07-01 at 12.56.32 PM.png

Now we can do a simple command, such as ls

Fuji2:Users:Mateo:Desktop:Screen Shot 2016-07-01 at 12.57.36 PM.png

As you can see, the client sends the contents of the Desktop (where the program was running from), back to the receiver. I will create a file, list the contents, and then show 404 page and source code of the page.

From the C2 server I am able to run commands, I used a PowerShell command and created an empty file in the current directory(desktop).Fuji2:Users:Mateo:Desktop:Screen Shot 2016-07-01 at 1.02.56 PM.png

The C2 receiver shows that the file was created.

Fuji2:Users:Mateo:Desktop:Screen Shot 2016-07-01 at 1.02.30 PM.png

You can now see the file I created from the C2 server is pictured on the desktop

Fuji2:Users:Mateo:Desktop:Screen Shot 2016-07-01 at 1.03.26 PM.png

The 404 page still looks the same.

Fuji2:Users:Mateo:Desktop:Screen Shot 2016-07-01 at 1.08.37 PM.png

But if you look at the source code you can see that there is a new comment at the bottom of the page.

Fuji2:Users:Mateo:Desktop:Screen Shot 2016-07-01 at 1.09.51 PM.png

And if you decode TmV3LUl0ZW0gLU5hbWUgRW1wdHlGaWxlLnR4dCAtSXRlbVR5cGUgRmlsZQ==

 You get:

New-Item -Name EmptyFile.txt -ItemType File

In conclusion, this could be a very powerful tool and easily overlooked.  It was a fun little project and you can see the source code at:

OK, the real site is: