Operation Poisoned News: Hong Kong Users Targeted with Mobile Malware via Local News Links Technical Brief By Elliot Cao, Joseph C. Chen, William Gamazo Sanchez, Lilang Wu, and Ecular Xu Contents 1 Attack Chain 1.1 Watering hole attack tactic 1.2 Infection chain 2 Exploits Analyses 2.1 The JavaScriptCore exploit 2.1.1 Bug triggering 2.1.2 The addrof/fakeobj primitives 2.1.3 Arbitrary address read/write primitive 2.1.4 Shellcode execution 2.2 Kernel exploit 3 iOS Malware lightSpy 3.1 Startup loader 3.2 Light: The main malicious control agent 3.3 BasicInfo module (Command ID 11000) 3.4 ShellCommandaaa module (Command ID 20000) 3.5 KeyChain module (Command ID 31000) 3.6 Screenaaa module (Command ID 33000) 3.7 SoftInfoaaa module (Command ID 16000) 3.8 FileManage module (Command ID 15000) 3.9 WifiList module (Command ID 17000) 3.10 Browser module (Command ID 14000) 3.11 Locationaaa module (Command ID 13000) 3.12 The iOS WeChat module (Command ID 12000) 3.12.1 The framework for stealing information 3.12.2 WeChat collected Information 3.13 iOS QQ module (Command ID 25000) 3.14 iOS Telegram module (Command ID 26000) 4 Android Malware dmsSpy 4.1 Distribution 4.2 Behavior Analysis 5 Appendix Trend Micro discovered a watering hole attack against iOS users in Hong Kong that first became active in January 2020. The campaign designed several webpages disguised as local news pages then injected them with an iframe that loads an iOS exploit. The iOS exploit flow was designed to exploit vulnerable iOS versions 12.1 and 12.2 on several models ranging from the iPhone 6S to the iPhone X. Users with unpatched iPhones that access the concerned links will be infected with an iOS malware that can spy on and take full control of the devices. We found that the campaign tricked users into clicking on the malicious news links by posting them on popular forums in Hong Kong. The iOS malware, which we named "lightSpy" (detected by Trend Micro as IOS_LightSpy.A), is a modular backdoor that allowed the attacker to remotely execute a shell command and manipulate files on the infected device. It is also implemented with several functionalities through different modules for exfiltrating data from the infected device including: • Hardware information • Contacts • Keychain • SMS messages • Phone call history • GPS location • Connected Wi-Fi history • Browser history of Safari and Chrome The malware also reports the surrounding environment of the device by: • Scanning local network IP address • Scanning available Wi-Fi network The campaign also employs modules specifically designed to exfiltrate data from popular messenger applications such as QQ, WeChat, and Telegram. Our research revealed the campaign also targeted Android devices in 2019. We found URL links of a malicious APK file posted on public Hong Kong-based Telegram channels. The message that the threat actors sent was disguised as a promotion of a seemingly legitimate application luring Android users to install it on their devices. The malware can also exfiltrate device information, contacts, and SMS messages. We named the Android malware "dmsSpy" (detected as AndroidOS_dmsSpy.A). The design and functionality of the operation suggest that it is not a targeted attack but one that aims to compromise mobile devices as many as possible for backdoor and surveillance. We dubbed the campaign "Operation Poisoned News." 1 Attack Chain 1.1 Watering hole attack tactic On February 19, we started noticing a watering hole attack targeting iOS users. The malicious webpage crafted by the attacker contained three iframe links to three different sites, with only one that was visible on the browser. The visible link connected to a page from a legitimate news website to make users believe they are looking at the original news website. One invisible iframe connected back to the webserver for the visitor statistic. Another invisible iframe connected to another server, which hosted the main script of the iOS exploit. Figure 1. HTML code of the malicious website with three iframes The threat actors further tricked users on the source of these malicious news webpages by posting them on four different forums of Hong Kong-based users. All of these four forums are popular and provide their own mobile applications for their users. Operation Poisoned News usually posted the topic on the general discussion section of the forums. The forum post includes the title of the news, the pictures from the news, and the malicious link the threat actors prepared. The forum accounts we found were registered right before the malicious link was posted. We believe it was directly posted by the campaign, and not a case where people reshared the news links from another source. The news topics selected as lure were mostly related to sexually implied headlines or those related to the COVID-19 disease. We believe these topics weren’t used to target specific users. Figure 2. List of news topics posted by the campaign Figure 3. Forum post with the link to malicious site We also found a second type of watering hole website that did not use an iframe to load news websites. The page directly copied the original news page and injected the iframe linked to the campaign's exploit server. Our telemetry data shows this type of watering hole was distributed in Hong Kong starting January 2. However, we were not able to identify where the malicious link was distributed at that time. Figure 4. Copied news page with an iframe that loads the remote exploit On March 20, the watering hole attack from Operation Poisoned News continued, as the campaign posted on a forum regarding a supposed schedule for protests in Hong Kong. The link leads to the same infection chain as in the earlier cases. Figure 5. Link to malicious site claiming to be a protest schedule 1.2 Infection chain The attack takes advantage of iOS versions 12.1 and iOS 12.2, which targets iPhone models from the 6S up to the iPhone X. The following figure shows how the exploit checks for different supported iOS and device versions. Figure 6. Code checking for target iOS devices The full exploit chain involves exploiting a silently patched Safari bug on multiple recent iOS versions and a customized kernel exploit. Once the Safari browser renders the exploit, a silently patched bug is taken advantage of, which leads to the exploitation of a known kernel vulnerability to gain root privileges. The exploited kernel bug has been assigned with the CVE ID CVE-2019-8605. However, the silently patched bug exploited on Safari does not have an assigned CVE ID; some researchers also noted an associated history of failed patches. After compromising the devices, the attacker installs undocumented and sophisticated spyware for maintaining control over devices and exfiltrating information. The spyware has a modular design with multiple capabilities, such as: • Modules update • Remote command dispatch per module • Complete shell command module Many of the modules were designed for data exfiltration; for example, there are modules for stealing information from WeChat and Telegram. The following image shows the full attack chain and names the modules initially downloaded and configured. Figure 7. lightSpy infection chain Because the malware was previously undocumented, we named it "lightSpy." Light is the module manager of this iOS spyware architecture. While analyzing the payload.dylib payload, we noticed that the decoded configuration file used by launchctl shows a URL that points to /androidmm/light, which hints that there is probably also an Android version of lightSpy. Figure 8. Config file hints at Android counterpart The payload, payload.dylib, is signed using the Apple developer certificate chain, probably to evade detection. The campaign is relatively new, based on the signature date (Nov. 29, 2019). Figure 9. Signed time indicates late November 2019 The next sections describe each stage of the full attack chain for iOS, including an analysis of the lightSpy malware. The final section covers the Android APK and how it is related to the Operation Poisoned News campaign. 2 Exploits Analyses Even when the exploited bug and code execution techniques used in the captured exploit are known in the research community, this section will cover the exploit stages, providing some details focusing on what is unique to the analyzed samples. 2.1 The JavaScriptCore exploit To briefly describe the exploit used to deploy lightSpy, the following sections will be covered: 1. Bug triggering: The use of the “silently patched” vulnerability 2. The addrof/fakeobj primitives: The use of generic exploit primitives to build an arbitrary R/W primitive from faked objects. 3. Arbitrary address read/write primitive: Take advantage of the final WebAssembly arbitrary R/W primitives to overwrite the WebAssembly object 4. Shellcode execution: The exploit shellcode execution that precedes the kernel exploit to get root privileges on the devices 2.1.1 Bug triggering This bug was accidentally found by @qwertyoruiop in hxxp://rce[.]party/wtf.js. It has since been fixed and does not have a CVE number assigned. It is a JIT (just-in-time)-type confusion bug in Safari’s JavaScript engine JavaScriptCore. Figure 10. Key parts of the PoC 1. The for loop triggers the JIT bug on the function victim() 2. In the function victim(), the expression “let r = 5 in oj;” triggers the function has() callback 3. Because the flag “hack” has been set to 1 after the loop calling function victim() being JITed, the “if” branch is executed and confuse[1] is set to an object. So the array “confuse” is converted from “ArrayWithDouble” to “ArrayWithContigous” by this callback The problem is JIT does not know there could be a side effect in this callback and the second element of “confuse” is a pointer, which was a number, and still treats the array “confuse” as “ArrayWithDouble”, causing the type confusion. 2.1.2 The addrof/fakeobj primitives From a Phrack article, Saelo has introduced the addrof and fakeobj primitives. The addrof() function is used to leak the memory address of the given JavaScript object, and the fakeobj() function is used to accept some given address and return a faked JavaScript object at that location. Because of the JIT-type confusion bug, the primitives addrof and fakeobj can easily be implemented by confusing a double and a pointer in the array: Figure 11. The addrof and fakeobj primitives 2.1.3 Arbitrary address read/write primitive After getting addrof/fakeobj, it sprays 0x5000 Float64Array and a few WebAssembly objects. It is easy to build a faked and effective Structure ID of 0x5000, which matches the real Structure ID of the sprayed Float64Array. Next, it uses the Structure ID and fakeobj to get a faked object, and adds the Structure ID to get a faked WebAssembly.Memory object. It then creates a faked wasmInternalMemory, which has a large size, and sets it as the faked WebAssembly.Memory object’s memory property. Figure 12. Faked Structure ID, WebAssembly.Memory, and wasmInternalMemory (top), and Faked objects (bottom) Finally, it gets a stable memory read/write primitive by this faked WebAssembly.Memory object: Figure 13. Memory read/write primitives 2.1.4 Shellcode execution After getting the arbitrary address read/write primitive, the exploit achieves the shellcode execution in stage two. It creates a JITed function and gets the function address by the exported symbol “startOfFixedExecutableMemoryPool”. After that, it builds a return-oriented programming (ROP) chain to write the shellcode to the JIT page and creates a temporary stack to execute the ROP chain. Figure 14. Temp stack for executing the ROP chain Since the payload contains the jailbreak code after the successful execution of the payload, it will get root privilege. The next section describes how the payload gets root privilege. 2.2 Kernel exploit In this section, we mainly introduce the local privilege escalation exploit chain used in this attack. All the exploit codes can be found in the payload.dylib payload. In the jailbreak rootkit published on GitHub by @pwn20wnd & @sbingner, it integrates the following public exploits: Indicator Attribution Description empty_list CVE-2018-4243 iOS 11.0 - 11.3.1 multi_path exploit CVE-2018-4241 iOS 11.2 - 11.3.1 async_wake CVE-2017-13861 iOS 11.1.2 Voucher_swap CVE-2019-6225 iOS 11.2 - iOS 12.1.2 mach_swap CVE-2019-6225 iOS 11 - 12.1.2 (<=A9 devices only) mach_swap2 CVE-2019-6225 iOS 11 - 12.1.2 (on A7 - A11 devices) Table 1. Public exploits used by an iOS jailbreak rootkit To support the iOS 12.2.* versions, this attack campaign used another vulnerability (CVE-2019-8605), which was found by Google Project Zero member Ned Willamson. There are also different exploit versions published on GitHub. In our findings, the campaign used the exploit host in sock_port, which supports iOS 10.0-12.2 and extends the jailbreak ability. Figure 15. Where the privilege escalation attack starts Not only did the sock_port project use CVE-2019-8605 to get the receive rights of the kernel task port in the get_tfp0() function, it also nearly supports most devices with system versions between 10.0 and 12.2. Therefore, in the exploit chain of this campaign, it simply integrates these codes to help to achieve the tfp0, as shown in the following figure. Figure 16. The get_tfp0() function Figure 17. The get_tfp0 function in payload.dylib, which is the same as the sock_port project Figure 18. After getting the tfp0, it initializes the patch handler, which can help find the address of necessary function symbols Figure 19. Combining the kernel slides, it resets the real address for those symbols Figure 20. The kernel task’s cred value is then stolen for the current process so that it becomes root After that, it first gets the address of the IOSurfaceRootUserClient port then uses it to get the address of the actual client and vtable. It then creates a fake client with a fake vtable and overwrites the existing client with the fake one. Lastly, the IOUserClient::getExternalTrapForIndex function in vtable gets replaced with the ROP gadget (add x0, x0, #0x40; ret;) so it can use IOConnectTrap6 to call any function in the kernel as the kernel itself. Figure 21. Code overwriting with the fake client and fake vtable Figure 22. Code showing the completed jailbreak operation 3 The iOS Malware lightSpy After gaining full kernel privilege, it downloads many malicious libraries to target applications. Figure 23. Downloaded modules 3.1 Startup loader The tool launchctl loads or unloads daemons or agents. After downloading all the payloads, the exploit spawns a daemon using launchctl with “ircbin.plist” as the argument. Figure 24. The launchctl tool is used with ircbin.plist as the argument This daemon uses irc_loader as an executable. This loader is just a launcher and will be used to start up the main malicious agent deployed on the target side. It first parses the C&C “IP:PORT” address then the download address. Figure 25. The irc_loader as an executable The startup parameters are hidden in the irc_loader binary and are encrypted with the AES algorithm. After decryption, the parameters are shown in the following figure. Figure 26. The parameters after decryption After getting these parameters, it will use them to launch another module called “light”. Figure 27. Loading the "light” module 3.2 Light, the main malicious control agent After “light” starts up, it first initializes a database, which is used to store all the control information. Figure 28. Database is initialized for control information The SQL statement includes the following: CREATE TABLE IF NOT EXISTS t_transport_control (id integer PRIMARY KEY AUTOINCREMENT, cmd integer, wifi integer, mobile integer CREATE TABLE IF NOT EXISTS t_command_plan (id integer PRIMARY KEY AUTOINCREMENT,type integer,start integer,stop integer,interval integer , interval_pos integer,cmd integer,arg text NOT NULL CREATE TABLE IF NOT EXISTS t_command_record (id integer PRIMARY KEY AUTOINCREMENT,cmd integer, arg text, status integer,type integer, response text, starttime integer CREATE TABLE IF NOT EXISTS t_config (id integer PRIMARY KEY AUTOINCREMENT, key text, value text CREATE TABLE IF NOT EXISTS t_dormant_control (id integer PRIMARY KEY AUTOINCREMENT, key text, value integer CREATE TABLE IF NOT EXISTS t_plugin (id integer PRIMARY KEY AUTOINCREMENT,name text NOT NULL,version text,md5 text,url text,path text,classname text,initparam text,isupdate integer,isdelete integer,downstatus integer After that, it initializes a thread using the libwebsockets library to implement the messages' receiving function. Figure 29. Communication flow The libwebsockets framework supports registering a callback broker as a protocol when creating the web socket handler. After this thread starts, the callback broker is responsible for managing the status of the socket handler. Figure 30. Callback broker The broker method (for managing the lifecycle of web socket handler) used an interrupted reason to trap into a different handler method. Among those reasons, LWS_CALLBACK_CLIENT_RECEIVE reason, whose value is 8, is responsible for receiving the commands sent from the C&C server in this attack event. Figure 31. Broker method used for managing the lifecycle of the web socket handler After getting the message, it will call the DealFrameCommand() function to deal with each kind of message, such as config, command plan, and command execution messages. Figure 32. A sample showing how it deals with command plan messages An init() then thread initializes the plug-in loading. The initialized process is shown below. Figure 33. Plug-in loading gets initialized The plug-in loading method is notable: It first gets the plug-in name, path, and classname, then uses the path to load the plug-in file through the dlopen() function. After that, it uses the objc_getClass() function to get the exposed class object, with “classname” as the argument. This way, the Light module can get each plug-in’s main class object and use these class objects to start up their own thread. Figure 34. The objc_getClass() function with "classname" as argument Figure 35. With baseinfoaaa.dylib module as an example, it first calls the init() method Figure 36. It then starts up the run loop After all the plug-ins load successfully, attackers can send the control commands for this malicious agent. The agent will dispatch these commands to different modules. Figure 37. The agent calls the ExeCommand:arg: function, which is in the CommandThread class, to execute the commands Figure 38. The ExeCommand:arg: function uses a related plug-in object to call their own StartCommand:Argv: function for executing corresponding commands 3.3 BasicInfo module (Command ID 11000) This module is mainly for gathering and uploading information such as iPhone hardware information, contacts, SMS messages, and phone calls. Figure 39. The BasicInfo module gathers different iPhone information Figure 40. Code that gathers targets’ SMS information 3.4 ShellCommandaaa module (Command ID 20000) This module is mainly used for executing shell commands. Figure 41. The ShellCommandaaa module for executing shell commands Figure 42. The popen function is used to fork a child process and execute shell commands The module will upload the execution result if necessary. Here it uses the dictToJsonData() function to serialize the result and post the data to the hxxp://…/api/shell/result server. Figure 43. ShellCommandaaa uses the dictToJsonData() function 3.5 KeyChain module (Command ID 31000) This module is mainly for getting targets’ Keychain information. It uses the SecItemCopyMatching() function with the following dictionary to copy Keychain items. Figure 44. The SecItemCopyMatching() function Figure 45. Each item, including the password, certificate, and key, is parsed and added into the return data object Figure 46. Sensitive information is uploaded to the hxxp://…/api/keychain/ server 3.6 Screenaaa module (Command ID 33000) This module is mainly for scanning around the target device. The method it uses goes through these four steps: 1. Determine the target device IP address and the subnet mask. 2. Calculate the range of possible addresses in its subnet. The range is obtained by using logical AND operator, where operands are binary values of the IP address and subnet mask. Figure 47. MMLANScanner start function 3. Iterate through the range and ping each IP address. Figure 48. Ping operation via MMLANScanner 4. Upload the data to the hxxp://…./api/lan_devices/ server using the void __cdecl -[LanDevices mainPresenterIPSearchFinished:](LanDevices *self, SEL a2, id a3) function. Figure 49. Uploading the data to the server 3.7 SoftInfoaaa module (Command ID 16000) This module has two sub-command IDs: 16001 and 16002. Command 16001 is used to get the software list, while command 16002 is used to get the process list. The following figure shows how to get the installed software list (id __cdecl +[AppInfo getAppInfoList](id a1, SEL a2)). It mainly uses an undocumented application programming interface (API) called installedApplications to achieve that. Figure 50. Getting the installed software list The following figure shows how it first calls the “ps -Aef” command to get the process list, then calls the getRunningProcessesList function to parse for details. Figure 51. Getting the process list information Figure 52. Getting the process ID (PID), process path, app, to name a few Lastly, it uploads the software list or process list information to the corresponding server. Figure 53. Getting the software and running processes list 3.8 FileManage module (Command ID 15000) This module is mainly used for file or directory operation, including the following sub-commands: get directory and file list, upload file, download file, delete file, create directory, rename file, move file, copy file, and get the directories of applications. Figure 54. Various FileManage module commands 3.9 WifiList module (Command ID 17000) This module is mainly for getting Wi-Fi information, including Wi-FI history, where the command ID is 17001, and the Wi-Fi scan list has a command ID of 17002. Figure 55. Getting the Wi-Fi history and scan list Figure 56. Getting the Wi-Fi history by directly reading the data stored in the com.apple.wifi.plist file Figure 57. Parsing each item to get the basic service set identifier (BSSID), SSID_STR, lastAutoJoined, lastJoined, and even the password information To get the Wi-Fi scan list, it loads the private MobileWiFi framework first and imported necessary functions through the dlsym function. Figure 58. The dlysym function It also creates a Wi-Fi manager using the WiFiManagerClientCreate() function. It then uses WiFiManagerClientCopyDevices to copy the devices and set it to UtilNetworksManager object. Figure 59. The WiFiManagerClientCreate() function as Wi-Fi manager It then uses the getScanList function to parse the detail properties, including the service set identifier (SSID), MAC, encryption type, and signal strength information. Figure 60. The getScanList function Figure 61. Uploading sensitive information to the corresponding server 3.10 Browser module (Command ID 14000) It is mainly used to get the device’s browser history for Safari and Chrome. For Safari, it first loads the history database from the Safari application path. Figure 62. Getting the Safari browser history It uses the following Structured Query Language (SQL) statement to query each browser item, then parses each detail properties such as URL, title, and visit time information. “select a.id,url,domain_expansion,title,visit_count,visit_time from history_items as a left join history_visits as b on a.id=b.history_item where a.id>%d order by a.id asc” Figure 63. Retrieving properties such as URL, title, and visit time The browser history database of Chrome is located in the “/Library/Application Support/Google/Chrome/Default/History” directory. The rest of the steps are almost similar to the ones used to get the Safari history. Figure 64. Getting the Chrome browser history Figure 65. Uploading the history information to the hxxp://…/api/browser_history/ server 3.11 Locationaaa module (Command ID 13000) This module is mainly used to get the targets’ iPhone location information. It includes two sub-commands. When the command is 13002, it sets up the continuous configuration with the attacker’s parameters. Figure 66. Command 13002 Figure 67. Parameters are primarily the update interval and duration Figure 68. Command ID 13001, where a task is added to continue updating the location data using the given configuration Figure 69. Uploading the location details with the device info to the hxxp://…/api/location/ server 3.12 The iOS WeChat module (Command ID 12000) This module is mainly used to collect the targets’ WeChat associated information, such as account information, contacts, groups, messages, and files. Figure 70. The gathered WeChat information 3.12.1 The framework for stealing information The steps used to steal the information: 1. Get the users' WeChat accounts To get the WeChat accounts' information, it first locates WeChat’s Documents directory and parses the LoginInfo2.dat file. This file stores many of the accounts' information using a special format that includes id_persion, phone, and name. Figure 71. Retrieving LoginInfo2.dat, which contains account information It then uses the id_persion value to compute an MD5 hash. Id_persion is a value, like “wxid_xxxx.” WeChat supports multiple users, so it uses this hash to create each account‘s directory for storing information such as account ID and usage. Figure 72. Getting the id_persion value After finding each account directory, all the properties, including id_persion, directory, phone, and nickname info for each account, will be collected. Figure 73. Using the id_persion value to calculate a hash and using it to find each account directory 2. Use the collected accounts to get the corresponding information that command ID refers to The following figure shows that attackers will repeatedly go through all accounts and execute the upload function. Figure 74. Attackers will repeatedly go through all accounts and execute the upload function Figure 75. In the upload function, it uses the related handler execute getData() function to get the detailed content, which it sends to the related server. 3.12.2 WeChat collected Information WeChatAccount Figure 76. Collecting the head icons for each account WeChatGroup Figure 77. Gathering data in the WCDB_Contact.sqlite database It queries this database using the “select dbContactChatRoom,dbContactRemark,userName,ROWID from friend where ROWID>%d” SQL statement. After that, it parses each item that contains the “chatroom” string. Figure 78. Parsing for the "chatroom" string WeChatMessage This part is mainly used to collect targets’ WeChat message information. To collect the messages, it firstly collects all the friends from the WCDB_Contract.sqlite database and filters out unwanted ones like “newapp,” then saves the information into a global dictionary variable named “accountMD5” using the pattern. Figure 79. Retrieving the WeChat friends list information and saving it to accountMD5 Figure 80. Opening a handler to the MM.sqlite database, which is used to save all the messaging information In this database, all the messages sent to certain friends are saved in the Chat_UserNameHash table, so it can iteratively go through all the tables and then save the messages with UserName_Hash for all friends. Figure 81. Sending saved messages to Chat_UserNameHash It first used the “SELECT CreateTime,Des,MesLocalID,Message,type FROM %@ where MesLocalID>%d” SQL statement to get all the message items. Among these columns, the MesLocalID is the name used to save a message file. Type indicates the message type, including simple message, image, audio, video, and open data, which can get the file type from suffix. To get an audio message, it firstly sets up the message type, and then uses the “/accountHome/Audio/message_id.aud” path to read the content. This way, the attackers collect all the messages. Figure 82. Getting an audio message WeChatContacts The contacts information is saved in the “WCDB_Contact.sqlite” database. Figure 83. The WCDB_Contact.sqlite database path It uses the following SQL statement to get the contacts information: select dbContactHeadImage,dbContactProfile,dbContactRemark,userName,ROWID from friend where ROWID>%d order by ROWID Among these columns, the dbContactHeadImage column is mainly used to store the head image information; dbContactProfile stores each friend’s profile information, including country, province, and, city; and the dbContactRemark field stores each friend’s remark details, such as name and alias. Figure 84. Getting contacts’ information WeChatFile This module is mainly used to collect all the messages’ file path, which is similar to the WeChatMessage module. Figure 85. WeChatFile module We shared our analysis with Tencent, which responded with the following: “This report by Trend Micro is a great reminder of why it’s important to keep the operating system on computers and mobile devices up to date. The vulnerabilities documented in the report, which affected the Safari web browser in iOS 12.1 and 12.2, were fixed in subsequent updates to iOS. A very tiny percentage of our WeChat and QQ users were still running the older versions of iOS that contained the vulnerability. We have already issued a reminder to these users to update their devices to the latest version of iOS as soon as possible. Tencent takes data security extremely seriously and will continue to strive to ensure that our products and services are built on robust, secure platforms designed to keep user data safe.” 3.13 iOS QQ module (Command ID 25000) The whole architecture of this module is nearly similar to that of the WeChat module. Figure 86. The iOS QQ module The only difference here is the location of the information and its format. Figure 87. Getting the targets’ QQ information 3.14 iOS Telegram module (Command ID 26000) The whole architecture of this module is nearly similar to that of the WeChat module as well. Figure 88. The iOS Telegram module Like the QQ module, the difference here is the location of the information and its format. To get the targets’ account info, it first locates the “Documents” directory. It then goes through the “telegram-data” folder, then uses the regular expression “account-\\d+” to get the account list. Figure 89. Getting the target’s account information The other submodules are almost similar to the WeChat module. Apple has been notified of this research through Trend Micro’s Zero Day Initiative (ZDI). We also reached out to Telegram on our findings and have not received a response at the time of publication. 4 Android Malware dmsSpy 4.1 Distribution While we were tracking the activity of the Operation Poisoned News campaign, we identified two URLs linked to Android APK files with the domains they used. Both of the URLs were posted on public Telegram channels used by users in Hong Kong in 2019. The messages were already deleted when we checked the Telegram channels. However, we were able to find the text messages from the webpage of the Telegram channel cached by the Google search engine. One of the linked APKs was shared as an application for watching paid porn videos for free. The link was already down when we checked it. For this one, we were not able to find the original APK file downloaded from the link. Figure 90. Shared message on Telegram with APK linked to the infrastructure of Operation Poisoned News Another APK link was disguised as a calendar application for checking the schedule of upcoming political events in Hong Kong. Though the link was also down, we managed to find the original file downloaded from it. Figure 91. A message on Telegram shared malicious APK of Operation Poisoned News Figure 92. Malicious APK disguised as a calendar 4.2 Behavior Analysis The calendar application shown above requires many sensitive permissions such as READ_CONTACTS, RECEIVE_SMS, READ_SMS, CALL_PHONE, ACCESS_LOCATION, and WRITE/READ EXTERNAL_STORAGE. When launched, it first collects device information such as device ID, brand, model, OS version, physical location, and SDcard file list. It then sends the collected information back to the C&C server. Figure 93. Going through all files on SD card Figure 94. Getting the location information It also steals contact and SMS information stored in the device. Furthermore, it registers a receiver that monitors new incoming SMS messages and syncs messages with the C&C server in real-time. Figure 95. The SMS receiver USSD Code Operator Description *118*35# CUniq Check remaining credit and expiry date *#130# CMHK Check remaining credit and expiry date *109# hkcsl Check main balance checking ##107# 3HK Check credit balance, mobile number and expiry date *111# hkcsl Password inquiry Table 2. Trying to dial certain USSD codes to query the device’s SIM card information Figure 96. Dialing USSD code The app can perform an update by querying the C&C server to fetch the URL of the latest APK file, then download and install it. Figure 97. Getting the latest APK file URL Figure 98. Installing the APK file While checking the communication between the C&C server and the APK malware, we noticed that the server did not disable the debug mode of the web framework, which allowed us to see the list of APIs used for C&C communication. Some of the APIs have been used in the malicious calendar application. We suspect that the attacker is still improving the payload to improve its capabilities. One of the APIs, called “screen_shot,” implies that it may be able to get the screenshot of the device. Another API of install_apk hints that the attackers would also have the capability to install the additional APK file to infected devices. Figure 99. The debug message leaked the APIs of the C&C server Not only is the malicious APK downloaded from a server hosted with the domain used by Operation Poisoned News, but the C&C domain also overlaps with the domain they used to host the malicious news page for the watering hole attack. For that reason, we believe that the APK malware is operated by the same campaign. 5 Appendix MITRE ATT&CK Matrix™ iOS Android Indicators of Compromise (IoCs) Indicator Filename Attribution Trend Micro Detection d5239210a9bc0383f569e9ca095fe8 bdfb9a482bc0c77c8658fcecb23b8a2 6bc payload.dylib lightSpy hash IOS_LightSpy.A 4887389ffaf4257b37408eac9f1740e abe805f830009cf58185757372f9036 67 light lightSpy hash IOS_LightSpy.A 3163c8b8deb3cdda9636c87379b1c 384dec207ce9f15f503ffb4b1ef8cfab 945 ircbin.plist lightSpy hash IOS_LightSpy.A f3f14cdada70d49c3e381cc1b05860 18e6b983af8799d3e6c4bee3494c40 e1d6 baseinfoaaa.d ylib lightSpy hash IOS_LightSpy.A 23e8884c69176d5cf4da0260cdbb29 6301c0e0afccd473d57033ac1a06f2 27c3 browser lightSpy hash IOS_LightSpy.A ce5241de3a378a64266c56fe5094ec bb8baa7afd677a3112db8074db78a5 5df1 EnviromentalR ecording lightSpy hash IOS_LightSpy.A 07c30054c7c22b8b53638367c4c3ad 484a1a336b615e1a6944260d5ec79 7a66a FileManage lightSpy hash IOS_LightSpy.A 51d7ebd3af38432c68c913aef48fe26 a206fda4b52c9f09728df69cab13a4b 3b ios_qq lightSpy hash IOS_LightSpy.A 0dfec52076249d91ec623ea5217735 2fbc8fb258db316eac85462c7b459f1 a2d ios_telegram lightSpy hash IOS_LightSpy.A 3c1bfbdfae91f1f248180c2102ed65fb dec086a334193894db67b0461a048 5c5 ios_wechat lightSpy hash IOS_LightSpy.A 1eec0e1ebeefc6667b6ee08e8dede5 cd36ca10697180f10e2d43a2fdebbe efcb irc_loader lightSpy hash IOS_LightSpy.A 650a5958a06b16aa819e4e8685874 6750b8c72a75f31bfdfb6b47fd38d72 b602 KeyChain lightSpy hash IOS_LightSpy.A 641d22e38b4135c56b7fb6037a6d76 098ffae9e84664993a3f4c07859b772 41e locationaaa.dy lib lightSpy hash IOS_LightSpy.A 3135efd29cb8b0fab961ddd7ec99e1 48dc4c5cca6c3303d60192dc966484 9545 Screenaaa lightSpy hash IOS_LightSpy.A 54c27a8b48b96e63402698d3bba41 480a815d103c92084d467d3c664ee c0a7f8 ShellComman daaa lightSpy hash IOS_LightSpy.A 1c0316d0194e8008904679242d592 d1a2aeeb2bacef28c7854e4361692a 085e7 SoftInfoaaa lightSpy hash IOS_LightSpy.A 6caa6342caefe3fea23353e850cb2c 974e8607c017661b7410de7a10004 b05ec WifiList lightSpy hash IOS_LightSpy.A 575890d6f606064a5d31b33743e056 HKcalander.a dmsSpy AndroidOS_dmsSpy.A 54b9ed9200758a9802491286c6a31 3139a pk Hahs 45.134.1[.]180 lightSpy C&C IP address 45.83.137[.]83 Watering hole exploit server app[.]poorgoddaay[.]com dmsSpy C&C domain movie[.]poorgoddaay[.]com dmsSpy download server domain news[.]poorgoddaay[.]com Watering hole server domain appledaily[.]googlephoto[.]vip Watering hole server domain www[.]googlephoto[.]vip Watering hole server domain app[.]hkrevolution[.]club dmsSpy download server domain news2[.]hkrevolution[.]club Watering hole server domain svr[.]hkrevolution[.]club dmsSpy C&C domain news[.]hkrevolution[.]club Watering hole server domain www[.]facebooktoday[.]cc Watering hole server domain news[.]hkrevolt[.]com Watering hole server domain www[.]messager[.]cloud Watering hole server domain TREND MICROTM RESEARCH Trend Micro, a global leader in cybersecurity, helps to make the world safe for exchanging digital information. Trend Micro Research is powered by experts who are passionate about discovering new threats, sharing key insights, and supporting efforts to stop cybercriminals. Our global team helps identify millions of threats daily, leads the industry in vulnerability disclosures, and publishes innovative research on new threats techniques. We continually work to anticipate new threats and deliver thought-provoking research. www.trendmicro.com