Kelios check in

Found this malware on ANY.RUN.

Here is the signature,

alert tcp $HOME_NET any -> $EXTERNAL_NET any (msg:"Kelios check in"; http.method; content:"POST"; http.uri; content:"/c2sock"; http.user_agent; content:"TeslaBrowser/5.5"; http.content_type; content:"multipart/form-data|3b| boundary="; reference:url,https://app.any.run/tasks/ca87bd74-393d-47cb-b134-adfeba1c4d3d/#; sid:2008007; rev:1;)

Hey there @NoahWolf!

I’ll be taking a look at this today/tomorrow. Sorry for the delay in getting back, just gets busy sometimes!

Based on one of the uploaded files system.txt, this appears to be LummaC2. Plenty of signature potential in this sample though, I’ll let you know what I find!

Happy Friday!

So, I finally got around to look at this, and figured I’d just tell you everything I found and did!

Awesome spot on the sample and a pretty decent signature! One thing we have to consider as rule writers is what parts of the rule could be static and which change between samples.

This is great example where the User-Agent changes between samples, and we even find some rules in the ruleset today, which contain hard-coded boundary values which make for a great candidate for tuning.

Lets explore!

Lumma Confirmation / Extracting Files

So, while looking at the sample you referenced (file (MD5: 34608FCB252B57E7AD01B6BF920DE05B) - Interactive analysis - ANY.RUN). I noticed there was a zip file being uploaded which contained a file called “system.txt”.

There are a couple different ways to extract this file from the pcap, but because it’s a multipart/form-data upload, I opted for a somewhat “silly”, though a quick, way of doing this.

I switched the Wireshark “Show Data As” option, that is avialable via the “Follow TCP Stream” window view over to “Hex Dump” mode, and selected the data that represented the zip file.

I then pasted this into CyberChef and added the “From Hex Dump” Recipe. I had to remove the starting “74 0d 0a 0d 0a” from the Input, as they were copied, but are not part of the zip file itself. Once that was I completed, CyberChef has a zip file in the output, so we can add the “unzip” Receipe and see what secrets are hidden within.

In this case, there are two files, but the System.txt contains the information we were looking for and confirmation this is Lumma Stealer.

This behavior, of including a System.txt file, is very common for Windows InfoStealers and can often be used to identify the malware.

Existing Rules

Based on the Lumma reference, I checked the ET ruleset and found two existing rules.

alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"ET MALWARE Win32/Lumma Stealer Data Exfiltration Attempt M2"; flow:established,to_server; http.method; content:"POST"; http.uri; content:"/c2sock"; bsize:7; nocase; http.user_agent; content:"HTTP/1.1"; bsize:8; http.content_type; content:"multipart/form-data|3b 20|boundary|3d d0 be|aj195iak20ka99441aj1"; fast_pattern; http.request_body; content:"|2d 2d d0 be|aj195iak20ka99441aj1"; startswith; content:"Content-Disposition|3a 20|form-data|3b 20|name|3d 22|file|22 3b 20|filename|3d 22|file|22|"; distance:0; content:"|0d 0a 0d 0a|PK"; distance:0; within:200; content:"name|3d 22|hwid|22|"; distance:0; content:"name|3d 22|pid|22|"; distance:0; within:200; content:"name|3d 22|lid|22|"; distance:0; within:200; reference:md5,9dcde1edfdd83f7cc28dcd31323be326; classtype:trojan-activity; sid:2043206; rev:1; metadata:affected_product Windows_XP_Vista_7_8_10_Server_32_64_Bit, attack_target Client_Endpoint, created_at 2023_01_04, deployment Perimeter, former_category MALWARE, malware_family lumma, performance_impact Low, confidence High, signature_severity Major, updated_at 2023_01_04;)
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"ET MALWARE Win32/Lumma Stealer Data Exfiltration Attempt M1"; flow:established,to_server; http.method; content:"POST"; http.uri; content:"sock"; nocase; http.content_type; content:"multipart/form-data|3b 20|boundary|3d|ob9a91jkpa01sjqrty192"; fast_pattern; http.request_body; content:"|2d 2d|ob9a91jkpa01sjqrty192"; startswith; content:"Content-Disposition|3a 20|form-data|3b 20|name|3d 22|file|22 3b 20|filename|3d 22|fname|22|"; distance:0; content:"|0d 0a 0d 0a|PK"; distance:0; within:200; reference:md5,9bfcdde1fc8a48cf05c5a63944c2475d; classtype:trojan-activity; sid:2039423; rev:2; metadata:attack_target Client_Endpoint, created_at 2022_10_17, deployment Perimeter, former_category MALWARE, malware_family lumma, performance_impact Low, confidence High, signature_severity Major, updated_at 2023_01_04;)

However, both of these rules depend on statically assigned content boundaries. This is something I picked up on your sample as well, when I re-ran it, it used the same boundary to separate out the parts in the multipart/form-data.

More Input

At this point, more data was required to support a signature that will hopefully not be evaded by changing some key elements such as the User-Agent, Boundary value, or URI.

Thankfully, a quick pivot on any.run on the IP address (77.73.134[.]68) shows PLENTY of samples. I checked out the following samples.

16685b20847f33924fb8d849229c41f0
2f3e344cf75f1e7de675daa76cb26d41
34608fcb252b57e7ad01b6bf920de05b
2119454f1f9a29c77912e1516d3d3515
a95020d898ba090e0ec4b9a7474bc039
d900dc6b7595abe8fd95eaf339d959f8
9f12ba134af9e047bc4aeec1b72d0ca1
b3b025b8445dcbd9b7aca560ad752b74
18f04064fc3b00847e89c9b1382a79dc
acb010fc0600ee75a6a1a1f0461bccca
c16ba0f2004c45a448d524867b6dfac5

After looking at all these samples, I found some patterns in the data. In particular, the same 4 names of the files being uploaded seem to be consistent in all POSTs.

Actually 2043206 - ET MALWARE Win32/Lumma Stealer Data Exfiltration Attempt M2 is pretty close to matching on all of them.

However, 2043206 appears to have not triggered on 34608fcb252b57e7ad01b6bf920de05b for a couple of reasons as well.

  1. Requires a specific boundary
    http.content_type; content:"multipart/form-data|3b 20|boundary|3d d0 be|aj195iak20ka99441aj1";
  2. Requires a specific User-Agent
    http.user_agent; content:"HTTP/1.1"; bsize:8;
  3. Requires a specific order of the parts in the multipart/form-data
    content:"Content-Disposition|3a 20|form-data|3b 20|name|3d 22|file|22 3b 20|filename|3d 22|file|22|"; distance:0; 
    content:"|0d 0a 0d 0a|PK"; distance:0; within:200; 
    content:"name|3d 22|hwid|22|"; distance:0; 
    content:"name|3d 22|pid|22|"; distance:0; within:200; 
    content:"name|3d 22|lid|22|";  distance:0; within:200;
    

I think the “foundation” of a rule that will match on all these samples is there, so I’d rather tune this rule instead of creating a new one, for now anyway (more on that later)

Making the Tune

Tuning a rule is always a balance between increasing and decreasing the chance of False Positives and False Negatives. One strategy is to always accompany “loosing” up the rule in one area with “tightening” it up in others. In case, the rule is going to be to “loosened” pretty significant by removing the three patterns which have kept it from firing on all these samples. To balance this, I’ll tighten the rule up in the following areas.

  1. Ensure the “file” with the filename of “file” has the Content-Type: attachment/x-object
  2. Add a PCRE to the value of the “hwid” file to ensure it’s a UUID wrapped in curly braces.

The proposed rule looks something like this

alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"ET MALWARE Win32/Lumma Stealer Data Exfiltration Attempt M2"; flow:established,to_server; http.method; content:"POST"; http.uri; content:"/c2sock"; bsize:7; nocase; http.request_body;  content:"Content-Disposition|3a 20|form-data|3b 20|name|3d 22|file|22 3b 20|filename|3d 22|file|22 0d 0a|Content-Type|3a 20|attachment/x-object|0d 0a 0d 0a|PK|03 04|"; content:"Content-Disposition|3a 20|form-data|3b 20|name|3d 22|hwid|22 0d 0a 0d 0a 7b|"; pcre:"/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\x7d/R"; content:"Content-Disposition|3a 20|form-data|3b 20|name|3d 22|pid|22|"; content:"Content-Disposition|3a 20|form-data|3b 20|name|3d 22|lid|22|"; reference:md5,9dcde1edfdd83f7cc28dcd31323be326; reference:md5,b3b025b8445dcbd9b7aca560ad752b74 classtype:trojan-activity; sid:1; rev:1;)

I quick tested this, and it seemed to do fine, but actually introduced a False Negative, because, by habit I suppose, I introduced additional two bytes |03 04| to identify the zip file. However, in the samples that the new rule did not alert on, it actually has PK|05 06|. Well isn’t that fun! Based on the Magic Bytes - this is actually for an “empty” zip file! Ok, lets trim that up after re-testing, the below rule seems to fire wonderfully.

alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"ET MALWARE Win32/Lumma Stealer Data Exfiltration Attempt M2"; flow:established,to_server; http.method; content:"POST"; http.uri; content:"/c2sock"; bsize:7; nocase; http.request_body;  content:"Content-Disposition|3a 20|form-data|3b 20|name|3d 22|file|22 3b 20|filename|3d 22|file|22 0d 0a|Content-Type|3a 20|attachment/x-object|0d 0a 0d 0a|PK"; content:"Content-Disposition|3a 20|form-data|3b 20|name|3d 22|hwid|22 0d 0a 0d 0a 7b|"; pcre:"/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\x7d/R"; content:"Content-Disposition|3a 20|form-data|3b 20|name|3d 22|pid|22|"; content:"Content-Disposition|3a 20|form-data|3b 20|name|3d 22|lid|22|"; reference:md5,9dcde1edfdd83f7cc28dcd31323be326; reference:md5,b3b025b8445dcbd9b7aca560ad752b74 classtype:trojan-activity; sid:1; rev:1;)

More signatures

This signature is still prone to False Negatives should the URI change. It might actually be worth while to see if any of the specific filenames/value (via PCRE or otherwise) by themselves make for a good signature which might last across multiple versions/forks/etc of the malware family.

See if you can come up with some and we can test 'em out!

1 Like