Investigating Command and Control Infrastructure (Emotet)

Although the majority of botnets still use a basic client-server model, with most relying on HTTP servers to receive commands, many prominent threats now use more advanced infrastructure to evade endpoint blacklisting and be resilient to take-down. In this article I will go through and explain my process of identifying Command and Control (C2) servers and understanding their topology, using Emotet as an example.


Emotet is the latest version of Feodo, its’ primary purpose is loading modules and gathering email addresses to target in malspam campaigns. At a glance it appears to use a very basic C2 setup with a bunch of IP addresses hard-coded into the binary, but on further inspection it’s a little bit more complex than it may seem.

If we use the free RiskIQ Community tool, we can have a look at each of the IP addresses and even see if any domain names have ever pointed to the ip, or still do. So let’s look up the first IP address.

RiskIQ tells us the IP address belongs to 1&1 (an ISP which provides servers for rent), and that it has had a bunch of domains currently pointed to it, one of which has pointed to this same IP address since 2009. 1&1 Internet is not an abuse friendly provider, so there’s no way a C2 server has survived 8 years, which is way longer than Emotet has even existed, so this is probably a legitimate web server.

Due to the fact the server has been taken down, I couldn’t poke it, so I decided to look at instead.

On port 80 it’s running a standard Plesk HTTPd, which suggests the server is again a web server, but this time it doesn’t appear to be running any websites. In the Emotet config the port specified is 8080, so let’s take a look at that.

It returns an Nginx 404 page, which is funny because the server is running Plex. Even more funny is the latency results we get in the Chrome Developer Tools if we send a request to port 80 and port 8080.

According to Chrome the server took 4 ms to send us a 6.5 Kilobyte web page on port 80, but took 325 ms to send us a 712 Byte 404 page on port 8080, which is not very logical at all.

The fact that most of the servers seem to have been hosting legitimate websites for a long time, run all sorts of different HTTP Services but always respond with an Nginx 404 error on the Emotet port, and there is a huge latency difference between the main port and Emotet port, leads me to one conclusion: all of these servers have been compromised and are running an Nginx reverse proxy on a secondary port, which relays requests to another server, hence the massive latency.

If we want to further confirm that we are facing a reverse proxy on the Emotet port and that this isn’t the bad guy’s origin server ,there’s another simple trick we can do with timing. Below is a simple python script I’ve written.

import socket
import datetime
import argparse

def time_request(host, request):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
    host = host.split(':')
    s.connect((host[0], int(host[1])))

    time_start =
    time_end =

    diff = time_end - time_start
    return diff.total_seconds() * 1000

parser = argparse.ArgumentParser()
parser.add_argument('--host', help="host (ip:port)", required=True)
args = parser.parse_args()

valid_request = 'GET /aaaaaaaa HTTP/1.1\r\nHost: {}\r\n\r\n'.format(
invalid_request = 'GET /aaaaaaaa HTTP/1.1\r\nMost: {}\r\n\r\n'.format(

valid_time = time_request(, valid_request)
invalid_time = time_request(, invalid_request)

print('valid request took {} ms\ninvalid request took {} ms'.format(valid_time, invalid_time))

The way this works is it sends two HTTP requests: one which should trigger a 404 error (page not found), followed by one which should trigger a 400 error (invalid request), recording the time taken for each. Both 404 and 400 pages should have similar response times; however, if we’re dealing with a reverse proxy the timing should differ noticeably. The first request will get forwarded to the origin, which will see the page doesn’t exist and return a 404. The second request is invalid so the proxy will not bother to forward it, instead a 400 error will be returned directly. Due to the fact the valid request is forwarded to the origin but the invalid one isn’t, the invalid request will get a response much faster than the valid one, if we’re dealing with a reverse proxy.

When we run the script against our suspected reverse proxy, we can see that the invalid request was more than 4 times faster than the valid one, so it’s pretty certain this is a proxy.

Now the question is, can we find the IP address of the server it’s forwarding requests to? It would be possible to contact the owner of the server and ask for some information, but the servers almost always get taken down before the owner responds (if they even do). Assuming the servers really are hacked, a possibility might be to re-hack one of them and poke around; however, not only is this illegal but also extremely unethical because these servers are people’s personal websites and not a bad guy controlled C2s (this is one of the reasons I don’t support the hacking back law, just because a server walks and talks like a C2 doesn’t mean it is one).

What I personally did was look through the list of IP addresses in the Emotet config and found a server which was hosted by an ISP I have a contact at, I then asked if they could look at inbound traffic to that IP address on the Emotet port and see if it correlates to any outbound traffic to another IP. I was told that for privacy reasons they could only inspect traffic if I could prove the server was acting maliciously and if the traffic inspected originated from my PC, so I ran Emotet in a VM and captured some traffic between my infected VM and the IP in question. When i provided the pcap to my contact at the ISP, they were quickly able to matched the traffic from my IP with their internal netflow data and observe that every time my PC sent a request to the hacked server, the hacked server would forward that request to another server (possibly the real C2).

Unfortunately, I wasn’t permitted to share the IP of the upstream server that my contact at the ISP shared with me; however a more recent one ( was found by TechHelpList which behaves identically to the one I found.

If we look it up in Shodan, we get the following.

We can see that port 80 returns the same universal 404 error as the hacked server did on the Emotet port, so most likely this is the port it forwards traffic to. Next question is, could this be the origin server or is it just another reverse proxy?

This time when I run my python script the invalid request actually takes longer, so it looks like we’ve found one of their backend servers.


Using hacked websites to proxy C2 servers has become much more common because it adds a layer of protection preventing researchers from easily finding and shutting down the actual C2 server; furthermore, it’s hard for security companies to flag the servers as malicious when they’re actually legitimate websites which have been running for years, not new servers set up with domains bought the day before.