Research by: Tejaswini Sandapolla, Pratik Jeware and Karthickkumar K
Supply chain attacks have increased in recent years. A watershed incident occurred in December 2020 when SolarWinds customers were infiltrated through malicious code snuck into the software. The 3CX desktop apps for Windows and macOS—used for voice and video conferencing—are the targets of a recent similar attack that affected millions of users worldwide.
In this attack the threat actor employed a DLL side-loading technique to target the legitimate 3CXDesktopApp signed binary. The attack has been observed on Windows and macOS systems.
The attackers use the DLL side-loading method to execute malicious code and to evade detection mechanisms. This technique involves loading a malicious DLL file through a legitimate process, allowing the attacker to bypass security measures and execute their code. This technique is a popular tactic among advanced threat actors and poses a significant risk to organizations if not properly addressed.
This article will examine how the attacker infected both Windows and macOS operating systems using dynamic linked libraries (DLL/Dylib). For Uptycs customers, we list hunting queries and YARA rules to inventory affected systems and identify historical activity at the bottom of this post.
Windows Platform
Infection Flow
Figure 1 below illustrates the malicious operation on the Windows platform.
Figure 1: Infection flow on Windows
Technical Analysis
Once the user executes the 3CXDesktopApp MSI file installer it will begin to install all the associated files on the targeted machine in the directory C:\Users\<username>\AppData\Local\Programs\3CXDesktopApp, as shown in Figure 2.
Figure 2: Installed location of MSI execution
The folder named "app" contains the malicious DLLs as shown in Figure 3.
Figure 3: Folder with malicious DLLs
When the file named "3CXDesktopApp.exe" is executed, it first loads a malicious file called "ffmpeg.dll". This DLL file contains an RC4 key and decryption loop. Then, it loads a second malicious DLL file named "d3dcompiler_47.dll", which contains encrypted data.
The attacker then uses the RC4 algorithm to decrypt the data from the "d3dcompiler_47.dll" file. This results in the retrieval of a shellcode and a binary called "Payload1".
Figure 4: RC4 algorithm used to decrypt data from the DLL
Payload1
The DLL binary is a 64-bit file with a single export function name “DllGetClassObject”.
System information
The attacker obtains the MachineGuid by using the RegQueryValueExA function and examining the registry path SOFTWARE\Microsoft\Cryptography.
Figure 5: Gathering MachineGuid
Payload1 is designed to download an icon file from a Git project.
Figure 6: Download request
At the time of analysis, the GitHub files were no longer available on Git. However, we were able to obtain the icon file from open-source. Upon examining the icon file, it was discovered that the attacker had appended base64-encoded and AES-GCM encrypted data at the end of the downloaded icon image. This appended data after decryption might connect to a C2 domain which would further download payload2.
Figure 7: Icon image at the end
Payload2
We also investigated the subsequent payload (payload2). This payload is a 64-bit DLL binary file that contains only one export function named "DllGetClassObject".
It has been designed to retrieve the HostName, DomainName, and OS version information from the victim's machine using the NetWkstaGetInfo() and RtlGetVersion() Windows APIs.
Figure 8: Grabbed system information
The stealer has been observed targeting popular web browsers such as Chrome, Microsoft Edge, Brave, and Firefox with the goal of extracting sensitive data from the victim's browsing history and the “places.sqlite” database.
In order to achieve this, the stealer runs a specific query to retrieve the latest 500 browsing details from the victim's machine. All the collected information is shared with the attacker server.
SQL query:
SELECT url, title FROM urls ORDER BY id DESC LIMIT 500
SELECT url, title FROM moz_places ORDER BY id DESC LIMIT 500
Figure 9: Reading browser information
The attackers had also infected the macOS 3CX app. The analysis below details the macOS version of compromised 3CXDesktopApp.
macOS Platform
Infection Flow
The Figure 10 below illustrates the malicious operation on the macOS platform.
Figure 10: RC4 algorithm
After the installation of 3CXDesktopApp the application is installed on path “/Applications/3CX Desktop App.app/Contents/MacOS/3CX Desktop App”
The malicious library is located on the following path: “/Applications/3CX Desktop App.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libffmpeg.dylib”
Figure 11: Malicious library in the folder
The entrypoint of the libffmpeg.dylib module includes a reference to the "run_avcodec" export function, which is responsible for creating a new “pthread” with the thread entry point located at virtual address (loc_48430) as shown in Figure 12 below.
Figure 12: Pthread creation
All the strings are hardcoded and every time the file XORs it with “0x7a” to get the decrypted strings. The XOR decryption results in file path "~/Library/Application Support/3CX Desktop App/” which is then concatenated with the string “.session-lock '' to create a file location "~/Library/Application Support/3CX Desktop App/.session-lock". The program then attempts to open this file.
Figure 13: Decryption of path and ”.session-lock” file open code
All the strings are hardcoded and every time the file XORs it with “0x7a” to get the decrypted strings.
It then fetches the product version info by reading below plist file:
“/System/Library/CoreServices/SystemVersion.plist ”
It tries to create a file named “.main_storage and UpdateAgent”
It decrypts the User Agent which is also hardcoded via XOR.
Figure 14: Hardcoded User Agent bytes
The decryption loop (XOR) is shown in Figure 15 below.
Figure 15: Decryption of hardcoded bytes to get UserAgent
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.128 Safari/537.36
We also observed that it adds various HTTP headers and it then tries to connect with C2 domains which are also obtained by decoding hardcoded bytes with XOR “0x7a”.
In the macOS version we observed that all the C2 domains are hardcoded inside the malicious dylib and whereas in the Windows version there is an extra stage to get the C2 domains.
Once the payload has been downloaded from the C2 server, it makes the payload executable.
Below Figure 16 illustrates how the payload bytes are written into the file location by using “fopen()” function with “wb” mode. After that, the payload executable permission is changed via chmod.
Figure 16: Second Payload Execution
At the time of this analysis, the C2 domains were down, so we could not acquire the second stage payload. However, our team is actively monitoring the situation and will update the blog as we find more details.
Hunting queries and YARA rules for Uptycs customers are provided below, along with the IOCs for Windows and macOS.
IOCs
Details of the hash, agent, and URL for the analyzed Windows sample were obtained.
File name
|
MD5
|
MSI
|
0eeb1c0133eb4d571178b2d9d14ce3e9
|
3CXDesktopApp.exe
|
9833a4779b69b38e3e51f04e395674c6
|
ffmpeg.dll
|
74bc2d0b6680faa1a5a76b27e5479cbc
|
d3dcompiler_47.dll
|
82187ad3f0c6c225e2fba0c867280cc9
|
payload1
|
cea0696d7552b819f1481d8d82735239
|
Icon
|
71d5b9bfd6bf37ff5aa9752b2b6d5af1
|
Payload2
|
7faea2b01796b80d180399040bb69835
|
User Agent
|
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) 3CXDesktopApp/18.11.1197 Chrome/102.0.5005.167 Electron/19.1.9 Safari/537.36
|
URL
|
https://raw.githubusercontent.com/IconStorages/images/main/icon%d.ico
|
Details of the hashes and URLs for the analyzed macOS sample were obtained.
File Name
|
MD5
|
3CX Desktop App.dmg
|
d5101c3b86d973a848ab7ed79cd11e5a
|
3CXDesktopApp-18.11.1213.dmg
|
5729fb29e3a7a90d2528e3357bd15a4b
|
Related Hashes
Md5
|
Platform
|
9833a4779b69b38e3e51f04e395674c6
|
Windows
|
bb915073385dd16a846dfa318afa3c19
|
Windows
|
f3d4144860ca10ba60f7ef4d176cc736
|
Windows
|
d5101c3b86d973a848ab7ed79cd11e5a
|
macOS
|
ca8c0385ce2b8bdd19423c8b98a5924b
|
macOS
|
5729fb29e3a7a90d2528e3357bd15a4b
|
macOS
|
3703770e32820397c6e7e1e1221e6d0d
|
macOS
|
Domain/URLs
akamaicontainer[.]com
|
msstorageboxes[.]com
|
akamaitechcloudservices[.]com
|
officeaddons[.]com
|
azuredeploystore[.]com
|
officestoragebox[.]com
|
azureonlinecloud[.]com
|
pbxcloudeservices[.]com
|
azureonlinestorage[.]com
|
pbxphonenetwork[.]com
|
dunamistrd[.]com
|
pbxsources[.]com
|
glcloudservice[.]com
|
qwepoi123098[.]com
|
journalide[.]org
|
sbmsa[.]wiki
|
msedgepackageinfo[.]com
|
sourceslabs[.]com
|
msstorageazure[.]com
|
visualstudiofactory[.]com
|
zacharryblogs[.]com
|
|
Hunting Queries
The following queries will help Uptycs customers determine whether the 3CXDesktopApp is running in their environment.
Uptycs Process Events hunting query to discover 3CXDesktopApp in the environment. Further investigation is required to determine if the malicious 3CXDesktopApp is being used when this query returns results:
- select * from process_events where path like '%3CXDesktopApp%' order by time desc
Uptycs DNS Lookup Events hunting query to search for the IOCs in historical data:
SELECT * FROM
dns_lookup_events
WHERE
(question LIKE '%akamaicontainer.com%'
OR question LIKE '%akamaitechcloudservices.com%'
OR question LIKE '%azuredeploystore.com%'
OR question LIKE '%azureonlinecloud.com%'
OR question LIKE '%azureonlinestorage.com%'
OR question LIKE '%dunamistrd.com%'
OR question LIKE '%glcloudservice.com%'
OR question LIKE '%journalide.org%'
OR question LIKE '%msedgepackageinfo.com%'
OR question LIKE '%msstorageazure.com%'
OR question LIKE '%msstorageboxes.com%'
OR question LIKE '%officeaddons.com%'
OR question LIKE '%officestoragebox.com%'
OR question LIKE '%pbxcloudeservices.com%'
OR question LIKE '%pbxphonenetwork.com%'
OR question LIKE '%pbxsources.com%'
OR question LIKE '%qwepoi123098.com%'
OR question LIKE '%sbmsa.wiki%'
OR question LIKE '%sourceslabs.com%'
OR question LIKE '%visualstudiofactory.com%'
OR question LIKE '%zacharryblogs.com%'
)
YARA Hunting Queries:
Windows
Query to scan for malicious DLLs
select * from yara where count > 0 and path like 'C:\Users\%\AppData\Local\Programs\3CXDesktopApp\app\%%' and rule = 'rule Uptycs_3cxTrojanLoader_windows
{
meta:
malware_name = "3cxTrojanLoader"
description = "3cxTrojanLoader is a trojan that covertly attaches itself to legitimate executable files and activates when the file is launched on the victim computer."
author = "Uptycs Inc"
version = "1"
md5 = "74bc2d0b6680faa1a5a76b27e5479cbc"
strings:
$string_rc4alogrithm = {8A 94 04 50 03 00 00 00 D1 02 8C 04 50 04 00 00 44 0F B6 C1 46 8A 8C 04 50 03 00 00 42 88 94 04 50 03 00 00 44 88 8C 04 50 03 00 00 48 FF C0 48 3D 00 01 00 00}
$string_xor = {46 8A 94 0C 50 03 00 00 45 00 D0 45 0F B6 D8 42 8A AC 1C 50 03 00 00 46 88 94 1C 50 03 00 00 42 88 AC 0C 50 03 00 00 42 02 AC 1C 50 03 00 00 44 0F B6 CD 46 8A 8C 0C 50 03 00 00 45 30 0C 0E 48 FF C1 48 39 C8 75 95}
$string_rc4key = "3jB(2bsG#@c7" ascii wide
condition:
all of them
}'
macOS
Query to scan for malicious dylib file
select * from yara where count > 0 and path like '/Applications/3CX Desktop App.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/%%' and rule = 'rule Uptycs_3CXDropper
{
meta:
malware_name = "3CXDropper"
description = "3CXDropper is a malicious Dynamic Library which is loaded into 3CXDesktopApp and downloads payload from C2."
author = "Uptycs Inc"
version = "1"
md5 = "660ea9b8205fbd2da59fefd26ae5115c"
platform = "macOS"
date = "31-03-2023"
strings:
$3CXDropper_0 = {80 B4 04 30 13 00 00 7A 48 FF C0 48 83 F8 75 75 EF} //XOR with 7a
$3CXDropper_1 = {49 89 EE 48 89 44 24 10 0F 28 05 E4 17 1C 00 0F 29 84 24 90 13 00 00 0F 28 05 C5 17 1C 00 0F 29 84 24 80 13 00 00 0F 28 05 A6 17 1C 00 0F 29 84 24 70 13 00 00 0F 28 05 87 17 1C 00 0F 29 84 24 60 13 00 00 0F 28 05 68 17 1C 00 0F 29 84 24 50 13 00 00 0F 28 05 49 17 1C 00 0F 29 84 24 40 13 00 00 0F 28 05 2A 17 1C 00 0F 29 84 24 30 13 00 00 48 B8 55 4F 49 4D 54 49 4C 7A 48 89 84 24 9D 13 00 00 31 C0} //XOR
$3CXDroppertwo_2 = {37 15 00 13 16 16 1B 55 4F 54 4A 5A 52 2D 13 14 1E 15 0D 09 5A 34 2E 5A 4B 4A 54 4A 41 5A 2D 13 14 4C 4E 41 5A 02 4C 4E 53 5A 3B 0A 0A 16 1F 2D 1F 18 31 13 0E 55 4F 49 4D 54 49 4C 5A 52 31 32 2E 37 36 56 5A 16 13 11 1F 5A 3D 1F 19 11 15 53 5A 39 12 08 15 17 1F 55 4B 4A 42 54 4A 54 4F 49 4F 43 54 4B 48 42 5A 29 1B 1C 1B 08 13 55 4F 49 4D 54 49 4C 7A}
// Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.128 Safari/53
$3CXDroppertwo_3 = {55 29 03 09 0e 1f 17 55 36 13 18 08 1b 08 03 55 39 15 08 1f 29 1f 08 0c 13 19 1f 09 55 29 03 09 0e 1f 17 2c 1f 08 09 13 15 14 54 0a 16 13 09 0e}
// System/Library/CoreServices/SystemVersion.plist
$3CXDroppertwo_4 = {25 25 0e 0f 0e 17 1b 47}
// __tutma=
condition:
all of ($3CXDropper_*) and 1 of ($3CXDroppertwo_*)
}'