Detecting the Manjusaka C2 framework

Security practitioners may know about common command-and-control (C2) frameworks, such as Cobalt Strike and Sliver, but fewer have likely heard of the so-called Chinese sibling framework “Manjusaka” (described by Talos in an excellent writeup). Like other C2 frameworks, we studied the Manjusaka implant/server network communications in our lab environment, and here we document some of the detection methods available. We have also open-sourced the content we describe.

Deploying several detection strategies means that if and when an adversary changes one or more attributes of the C2 communication protocol, defenders have multiple opportunities to still detect its use. This is where the art of detection engineering comes into play. As network defenders we broadly look for:

  • Aspects of communication that will be hard for the attacker to change
  • Detection strategies that use resources efficiently
  • Detection strategies that produce alerts with as few false positives and false negatives as possible

These three key aspects often lie in tension, and depend not only on alert logic, but also the nature and volume of the traffic being inspected. In this context, the adage “there is no silver bullet” is apt, and so the strategies we employ to detect Manjusaka span several approaches, some of which are described in this blog:   

The Heartbeat

The Manjusaka C2 framework sends a heartbeat every second to the C2 server. This can be thought of as a “ping” - a request from the implant to the C2 server for instructions to carry out. Let’s look at the Wireshark display of one such heartbeat stream. Below we show implant communication in red, and the response from the C2 server in blue. As there are several interesting and unique elements we can focus on, see if you can identify a few before reading on, and then we’ll continue by outlining a few detection strategies. 

Given that this GET occurs every second (with no jitter), we could simply look for a GET to /global/favicon.png occur every second. Here is what this looks like in a SIEM on a timeline:

Timing elements (“beacons”) provide a valid detection strategy, however they are an aspect that is easy for an attacker to vary, so we look for alternative detection strategies.As illustrated in the following Wireshark screenshot, the heartbeat consists of a GET request with binary content being sent to the server.

It is fairly rare that content is sent to the server as part of a GET request*. However,  such activity is certainly not necessarily nefarious (e.g., ElasticSearch does this by design), so we need additional attributes to craft our detection logic.

Note that the Content-Length from the client is always “2” for the heartbeat and from the server is always “5”. Zeek provides both of these by default in http.log fields request_body_len and response_body_len, respectively. So we can simply look for a combination of a request_body_len of 2 and a response_body_len of 5 together with the GET method, and also a user agent matching a browser, which further reduces the chances of false positives.

Humio search

#path="*http*" method=GET user_agent="Mozilla/*" request_body_len=2 status_code=200 response_body_len=5

Splunk search

sourcetype="*http*" method=GET user_agent="Mozilla/*" request_body_len=2 status_code=200 response_body_len=5

As a variation, we can also look for when a GET request sends any content to a png, as this is very rare in practice.

Humio search

#path="*http*" method=GET request_body_len>0 uri="/global/favicon.png"

Splunk search

sourcetype="*http*" method=GET request_body_len>0 uri="/global/favicon.png"

User Agents

Surprisingly, this malware employs some quite unique user agents, so we can simply look for them in Zeek’s standard http.log. User agents can be easy for attackers to change - however when detections are this easy, it can be worth taking advantage of them anyway, as they offer some low-hanging fruit and remain effective until that evolution occurs.

Humio search 

#path="*http*" method=GET 

( user_agent="Mozilla/5.0 (Windows NT 8.0; WOW64; rv:58.0) Gecko/20120102 Firefox/58.0" OR user_agent="Mozilla/5.0 (Windows NT 8.0; WOW64; rv:40.0) Gecko")

Splunk search

sourcetype="*http*" method=GET 

( user_agent="Mozilla/5.0 (Windows NT 8.0; WOW64; rv:58.0) Gecko/20120102 Firefox/58.0" OR user_agent="Mozilla/5.0 (Windows NT 8.0; WOW64; rv:40.0) Gecko")

“The impossible .png”

A Manjusaka heartbeat consists of a GET request for a .png file, followed by a response from the server with a Content-Type of image/png. However there are two aspects of the content served that make this “impossible”.

  • Consider the PNG file header:
  • As seen in the Wireshark example earlier, the hexadecimal notation of the content delivered in the heartbeat response is  1A 1A 6E 04 29 , which is quite different from the expected byte sequence of a PNG file ( 89 50 4E 47 0D 0A 1A 0A​​). If the correct file signature for a PNG did appear, Zeek would populate the resp_mime_types field in http.log with image/png to indicate it saw a PNG file. However if an unknown byte sequence appears instead - as in our case - Zeek won’t populate the resp_mime_types field. Here’s a SIEM search based on this logic:

    Humio search 

#path="*http*" request_body_len>0 response_body_len>0 uri=*.png NOT resp_mime_types


Splunk search

sourcetype="*http*" request_body_len>0 response_body_len>0 uri=*.png NOT resp_mime_type

Even if a PNG has zero actual content (zero-by-zero image), its overall size can’t be less than eight bytes, corresponding to the number of file signature bytes required for PNGs. Since the content size of the server response is only “5’, it cannot possibly be a PNG file! In this case, a SIEM search might look like this:

Humio search

#path="*http*" request_body_len>0 response_body_len>0 uri=*.png response_body_len<8 

Splunk search

sourcetype="*http*" request_body_len>0 response_body_len>0  uri=*.png response_body_len<8

Suricata Rules

Using Suricata we can employ the same tactics outlined above except we can now also inspect the actual content of the “2” and “5” byte responses. This additional visibility allows us to detect exfiltration, where the client sends more the heartbeat size of “2” to the C2 server (method 3 below). Our open-sourced Suricata rules work in spirit as follows.

Screen Shot 2022-09-28 at 10.13.20 AM


In this blog we demonstrate an example of how employing a variety of detection strategies can improve a defender’s visibility of access to current C2 infrastructures. We also show how using a mixture of highly particular detection attributes (e.g., unique user agents) alongside more generic detections (e.g., detecting “impossible” PNGs) can provide robustness to attackers changing a single aspect to thwart one detection strategy. We can maintain visibility by leveraging a breadth of detection strategies.

By Corelight Labs Team

 *RFC7231 section 4.3.1: "A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request."



    Recent Posts