Feb 14, 2022
0 0

Watering hole deploys new macOS malware, DazzleSpy, in Asia

Written by

Hong Kong pro-democracy radio station website compromised to serve a Safari exploit that installed cyberespionage malware on site visitors’ Macs
On November 11th, Google TAG published a blogpost about watering-hole attacks leading to exploits for the Safari web browser running on macOS. ESET researchers had been investigating this campaign the week before that publication, uncovering additional details about the targets and malware used to compromise its victims. Here we provide a breakdown of the WebKit exploit used to compromise Mac users and an analysis of the payload, which is a new malware family targeting macOS. But first, let’s look at how victims came into contact with the malicious code in the first place.
It was reported by Felix Aimé from SEKOIA.IO that one of the websites used to propagate the exploits was a fake website targeting Hong Kong activists. We can read on its home page “Liberate Hong Kong, the revolution of our times”. The very recent registration date of the fightforhk[.]com domain, October 19th, 2021, and the fact that the website is no longer accessible, supports that idea. We could also confirm that the Internet Archive cached a copy of the web page on November 13th. This copy includes the malicious iframe, as seen in Figure 1.
Figure 1. fightforhk[.]com, as archived by the Wayback Machine on November 13th
ESET researchers found another website, this time legitimate but compromised, that also distributed the same exploit during the few months prior to the Google TAG publication: the online, Hong Kong, pro-democracy radio station D100. As seen in Figure 2, an iframe was injected into pages served by bc.d100[.]net – the section of the website used by subscribers – between September 30th and November 4th 2021.
Both distribution methods have something in common: they attract visitors from Hong Kong with pro-democracy sympathies. It seems that they were the primary target of this threat.
Figure 2. Excerpt of https://bc.d100[.]net/Product/Subscription on November 4th 2021
As seen in Figure 3, the page hosted on the malicious amnestyhk[.]org domain checks for the installed macOS version and redirects to the next stage if the browser is running on macOS 10.15.2 or newer.
Figure 3. Content of the defaultaa.html page on amnestyhk[.]org
The next stage, named 4ba29d5b72266b28.html (see Figure 4) simply loads the JavaScript containing the exploit code – mac.js.
Figure 4. Content of the 4ba29d5b72266b28.html page
Note that the script tag to load caps.js has been commented out. The previous version of the exploit loaded Capstone.js from that file, while in the new version, Capstone.js is prepended to the exploit code in mac.js.
The exploit used to gain code execution in the browser is quite complex and had more than 1,000 lines of code once formatted nicely. It’s interesting to note that some code, which suggests the vulnerability could also have been exploited on iOS and even on PAC-enabled (Pointer Authentication Code) devices such as the iPhone XS and newer, has been commented out, as seen in Figure 5.
Figure 5. Excerpt of the JavaScript exploit containing comments about how to target iOS and PAC-enabled devices
We have confirmed that the patch identified by Google TAG does fix the vulnerability. While it is possible this vulnerability was assigned CVE-2021-1789, we couldn’t confirm due to the lack of publicly available technical details. Below we outline our understanding of how the vulnerability affects Safari versions prior to 14.1.
The exploit implements two primitives to gain memory read and write access: one to leak the address of an object (addrof) and one to create a fake JavaScript object from a given memory address (fakeobj). Using these two functions, the exploit creates two arrays of different types that overlap in memory, and thus is able to set a value in one of them that is treated as a pointer when accessed using the other. The technique is well described by Samuel Groß in his multiple publications on the subject. Below we explain the vulnerability that made the leakage of object addresses possible.
The exploit relies on a side effect caused by modifying an object property to be accessible via a “getter” function while enumerating the object’s properties in JIT-compiled code. The JavaScript engine erroneously speculates that the value of the property is cached in an array and is not the result of calling the getter function. We have extracted the relevant part of the code that enables the addrof primitive, which you can see in Figure 6. Comments starting with (e)r are from ESET Research.
Figure 6. Commented excerpt of the exploit enabling the leak of object addresses
The first corruption happening here is the result of bar(vic). The function will return a pointer to a JSCell object (to be more precise, a GetterSetter), which should never be accessible from the JavaScript code. Here is the result of describe(bar(vic)) in a JavaScriptCore console:
Cell: 0x7fffb34dc080 (0x7ffff38cc4c8:[0x3af5, GetterSetter, {}, NonArray, Leaf]), StructureID: 15093
This JSCell is then converted to a JSObject by calling the JavaScript Object function. Internally, this results in calling the JSCell’s toObject method. There is no implementation for converting a GetterSetter to a JSObject and the code will eventually fall back and assume its type is a Symbol. The GetterSetter will erroneously be cast to a Symbol. You may have noticed the assertion that the cell type is a Symbol before performing the cast in the code; however, the ASSERT macro in WebKit is compiled out of release builds.
In memory, the location of getter[0] is the same as this corrupted symbol’s value. Thus, reassigning a value to getter[0] will change the value of the symbol. Its value is fetched from JavaScript using its toString method.
The updated JavaScriptCore code now checks whether the object contains properties with GetterSetter after the property enumeration, before considering whether the object’s attribute can be accessed “quickly”.
Detailing the fake object creation would require an article of its own. In short, it abuses the same bug, although this time the object is manipulated in a way that the JIT-compiled code accesses an item that is out-of-bounds and returns an address that was carefully sprayed on the heap before the fetch.
The rest of the code allows bypassing mitigations, such as the Gigacage, and loads the next stage.
As explained by Google TAG, the JavaScript loads a Mach-O executable file in memory. The rudimentary loader does not implement importing symbols from external libraries; instead, the addresses of dlopen and dlsym are patched into the loaded Mach-O. These can then be used from the executable to dynamically load and get the addresses of functions from external libraries.
Now that code execution has been gained, the next stage is a Mach-O that is loaded into memory and executed. This Mach-O exploits a local privilege escalation vulnerability to run the next stage as root. Our examination confirms Google’s analysis that the exploited vulnerability was described by Xinru Chi and Tielei Wang in a presentation at zer0con 2021, but it was also presented in more details at MOSEC 2021 by Tielei Wang. The vulnerability has been assigned CVE-2021-30869. Figure 7 shows a call to a function Tielei Wang called adjust_port_type in his last presentation. This function, responsible for changing the internal type of a Mach port, is implemented the same way in the Mach-O as was presented at MOSEC. Changing the type of a Mach port shouldn’t be possible unless a vulnerability exists.
Figure 7. Altering the port type from IKOT_NAMED_ENTRY to IKOT_HOST_PRIV to gain access to special (privileged) Mach ports
To summarize, the Mach-O does the following:
The decrypted payload is where our analysis differs the most from what was described by Google TAG: the payload delivered to vulnerable visitors to the D100 site was new macOS malware we named DazzleSpy.
DazzleSpy is a full-featured backdoor that provides attackers a large set of functionalities to control, and exfiltrate files from, a compromised computer. Our sample is a Mach-O binary file compiled for x86_64 CPU architecture.
In order to persist on the compromised device, the malware adds a Property List file (plist; see Figure 8) named com.apple.softwareupdate.plist to the LaunchAgents folder. The malware executable file is named softwareupdate and saved in the $HOME/.local/ folder.
Figure 8. Property List file in LaunchAgents folder
DazzleSpy connects to a hardcoded C&C server; the IP address and port found in the sample we decrypted was 88.218.192[.]128:5633. At first, the malware performs a TLS handshake, then uses a custom protocol to exchange JSON objects to deliver commands from the C&C server to compromised Macs. DazzleSpy’s binary contains an X.509 certificate used as a certificate authority (CA). It verifies that the server’s certificate is issued by that authority. In practice, the same self-signed certificate is used for both the CA and the C&C server. The technique protects the malware’s communications from potential eavesdropping by refusing to send data if end-to-end encryption is not possible.
Table 1 contains the list of commands supported by DazzleSpy. The first column is the name of the command that must be present in the JSON object received from the C&C server; many support optional or mandatory parameters.
Table 1. DazzleSpy C&C commands
While analyzing the DazzleSpy binary we found a number of interesting artifacts that might suggest an internal name for the malware and the authors’ origin.
In several places (for example, see Figure 9) the malware refers to osxrk and the string 1.1.0 seems likely to be an internal version number.
Figure 9. Possible internal name and version number of the DazzleSpy malware
Moreover, it seems DazzleSpy’s authors were not so concerned about operational security as they have left the username wangping in paths embedded in the binary. Figure 10 contains paths that reveal this username and internal module names.
Figure 10. Paths embedded in the DazzleSpy binary
Once the malware obtains the current date and time on a compromised computer, as you see in Figure 11, it converts the obtained date to the Asia/Shanghai time zone (aka China Standard Time), before sending it to the C&C server.
Figure 11. Decompiled code of the getSystemDate function
In addition, it should be noted that the DazzleSpy malware contains a number of internal messages in Chinese, for example as seen in Figure 12.
Figure 12. Internal error message in Chinese
Given the complexity of the exploits used in this campaign, we asses that the group behind this operation has strong technical capabilities. While there is information published online about the local privilege escalation (LPE) vulnerability used here, we couldn’t find anything about the specific WebKit vulnerability used to gain code execution in Safari. It’s also interesting that end-to-end encryption is enforced in DazzleSpy and it won’t communicate with its C&C server if anyone tries to eavesdrop on the unencrypted transmission by inserting a TLS-inspection proxy between the compromised system and the C&C server.
The watering-hole operations this group has pursued show that its targets are likely to be politically active, pro-democracy individuals in Hong Kong. This campaign has similarities with one from 2020 where LightSpy iOS malware (described by TrendMicro and Kaspersky) was distributed the same way, using iframe injection on websites for Hong Kong citizens leading to a WebKit exploit. We cannot confirm at this point whether both campaigns are from the same group, but ESET Research will continue to track and report on similar malicious activities.
SHA-256: 1F862B89CC5557F8309A6739DF30DC4AB0865668193FDFF70BA93F05D4F8C8B8

This table was built using version 10 of the MITRE ATT&CK framework.


Article Categories:

Comments are closed.