Skip to main content

Burned by Fire(fox), a firefox 0day drops macOS backdoor


A little over a week ago, I received an email from a user who stated:

Last week Wednesday I was hit with an as-yet-unknown Firefox 0day that somehow dropped a binary and executed it on my mac (10.14.5)

Let me know if you would be interested in analysing the binary, might be something interesting in there wrt bypassing osx gatekeeper.

Of course, given our mission at Digita, I was inherently intrigued and ready to get to work

Want to play along?

I’ve shared the OSX.NetWire.A sample (password: infect3d)

…please don’t infect yourself!

A Firefox 0day, a firefox 0day drops a macOS backdoor

When the user contacted me, there wasn’t much information about the Firefox 0day exploit used in the attack. However now, more information is readily available!

First, I was able to obtain an email that (said user claimed) was related to the attack.

 Dear XXX,

My name is Neil Morris. I'm one of the Adams Prize Organizers.

Each year we update the team of independent specialists who could assess 
the quality of the competing projects:

Our colleagues have recommended you as an experienced specialist in this 

We need your assistance in evaluating several projects for Adams Prize.

Looking forward to receiving your reply.

Best regards,
Neil Morris

Even if an attacker has a browser 0day exploit, they still have to find a way to deliver it to the target.

When individuals are targeted, the delivery mechanism of choice is often an email that contains links to a malicious site (which will “throw” the exploit when the user visits said site).

Unfortunately the link ( currently returns a 404 Not Found:

 $ curl

404 Not Found

Not Found

The requested URL /nm603/awards/Adams_Prize was not found on this server.

Apache/2.4.7 (Ubuntu) Server at Port 80

Of course it’s possible that the site will only serve up (“throw”) the exploit if the user browses to the site via a vulnerable version of Firefox, or perhaps from a certain IP address, etc. More than likely though, the attacker has moved on, and taken down the exploit site.

Though I don’t have access to the exploit code, the user was able to provide me with persistent malware the exploit installed on the system (named Woohoo!

The remainder of this blog will cover the analysis of this malware (hash: 23017a55b3d25a2597b7148214fd8fb2372591a5).

 $ shasum -a 1 ~/Attack/ 
23017a55b3d25a2597b7148214fd8fb2372591a5 Finder

Interestingly, a security researcher at Coinbase, Philip Martin, posted an interesting thread on twitter:

Note that the hash mentioned by Phil, 23017a55b3d25a2597b7148214fd8fb2372591a5 matches malicious file which the user sent me. Moreover the user confirmed that he was “involved with a cryptocurrency exchange until fairly recently.” Thus it seems reasonable to assume we’re all talking about the same Firefox 0day.

This 0day, has now been patched as CVE-2019-11707, and covered in various articles such as:

However, the details of the persistent malware used in the attack are scant (non-existent?). The hash was first uploaded on 6-6-2019, so though this issue is patched via firefox there is plenty to uncover to further contribute to the behaviors of malware within the macOS architecture, so let’s dive into that now!

A Persistent Mac Backdoor (OSX.NetWire.A!?)

As noted, the infected user was kind enough to send me the malware ( that the attacker persistently installed on the system (via the Firefox 0day).

Within the enterprise, Digita’s GamePlan would quickly detect this applications and flag it as “unsigned”.If you are looking for a tool to run on your personal machine, you can use the open-source “WhatsYourSign” utility to quickly determine it’s an unsigned application:

Searching for the hash (23017A55B3D25A2597B7148214FD8FB2372591A5) on VirusTotal found a exact match and shows that the file was submitted on 2019-06-06 but currently is only detected by one AV engine (Tencent). The behavior of this application being unsigned alone should raise an eyebrow. This is the importance of understanding the malicious or unwanted behaviors versus relying on known-sigantures:

The full application bundle,, was just submitted to VirusTotal today.

It is similarly only detected by one AV engine.

Interestingly the engine detecting the malware flags it as OSX.Netwire.

OSX.Netwire (or OSX.Wirenet) was first discovered in 2012(!) by Dr Web. In their writeup “Wirenet: The Password-Stealing Trojan Lands on Linux and OS X” they state that it is “the first Trojan in history to steal Linux and Mac OS X passwords.”

Passwords were stolen via keylogger logic and/or directly from files on disk (i.e. saved browser logins):

 $ strings malware/2012/OSX.Netwire



%s/Library/Application Support/Firefox



select * from moz_logins

But that was OSX.Netwire from 2012. Is this new sample really (still) OSX.Netwire!? I personally had not heard anything about OSX.Netwire since 2012, so decided to poke around.

First, via simple “string” matching, it’s easy to confirm the 2012 sample and the 2019 are in someway related.

For example, note the string: "\x03\x04\x15\x1A\r\nexit\r\n\r\nexit\n\n" in both the 2012 sample:

 esi = "/bin/sh";
if(access(esi) != 0x0) {
 esi = "/bin/bash";


eax = write$UNIX2003(*0x140d0, "\x03\x04\x15\x1A\r\nexit\r\n\r\nexit\n\n", 0x15);

and in the 2019 sample:

 if(stat("/bin/sh", edi) != 0x0) {
 ebp = "/bin/bash";

write$UNIX2003(ebx, "\x03\x04\x15\x1A\r\nexit\r\n\r\nexit\n\n", 0x15)

…other rather unique strings are also found in both samples.

Whilst chatting about this with one my close (security researcher) friends he noted that this new sample was (already) detected by XProtect.

XProtect is Apple’s rudimentary anti-malware system, baked in to macOS.

For a good overview, see XProtect Explained: How Your Mac’s Built-in Anti-malware Software Works

I was rather skeptical of this claim (as I didn’t recall any recent XProtect updates for OSX.Netwire), but turns out he was absolutely right!

In 2016, Apple added a Yara signature to detect something called OSX.Netwire.A. The signature (found in /System/Library/CoreServices/XProtect.bundle/Contents/Resources/XProtect.yara) is presented below:

 rule NetwireA
 description = "OSX.Netwire.A"
 $a = "exitexit"
 $b = "User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like GeckoAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
 all of them

Digita’s UXProtect utility can graphically display this signature as well:

We can also utilize UXProtect to confirm this signature (from 2016) still detects the malware used in the Firefox 0day attack:

Wow! kudos to Apple for writing a signature that (IMHO) rather surprisingly detects this “new” threat!

Interestingly Apple’s signature does not detect the sample from 2012 (as it does not contain the User-Agent: Mozilla... string). This is first (of many) indicator that these samples, while somehow related are unsurprisingly not the same.

I’m personally not aware of any (public) details related to this new sample.

Interestingly Intego, in 2016, wrote a brief article about Apple’s XProtect: “Apple Updates XProtect Malware Definitions for NetWeirdRC”.

However, they (and others) all reference or link back to the 2012 sample, which (as already noted) is not detected by Apple’s OSX.Netwire.A YARA signature.

As is often the case, Apple tends to know things which don’t become common knowledge

Or atleast, common knowledge to me:

While the sample from 2012 and 2019 are clearly related, they are also very different. We’ll get into this more in part two of this blog post, when we dive into the capabilities of the (new) malware sample. However in short, the 2012 and 2019 samples have totally different objectives. If I had to guess, they are both written by the same author (or team), but serve unique purposes (i.e. the 2012 sample is only concerned with stealing passwords).

Before we wrap up part one of this post, let’s look at how the new sample, OSX.NetWire.A, persists.

A quick peek at the malware’s disassembly reveals an embedded launch agent plist:

 memcpy(esi, "\n\n\n\n Label\n %s\n ProgramArguments\n\n %s\n \n RunAtLoad\n \n KeepAlive\n <%s/>\n\n");
eax = getenv("HOME");
eax = __snprintf_chk(&var_6014, 0x400, 0x0, 0x400, "%s/Library/LaunchAgents/", eax);

eax = __snprintf_chk(edi, 0x400, 0x0, 0x400, "%s%s.plist", &var_6014, 0xe5d6);

Seems reasonable to assume the malware will persist as launch agent.

However, it also appears to contain logic to persist as a login item (note the call to the LSSharedFileListInsertItemURL API):

 eax = __snprintf_chk(&var_6014, 0x400, 0x0, 0x400, "", &var_748C, &var_788C);
eax = CFURLCreateFromFileSystemRepresentation(0x0, &var_6014, eax, 0x1);

eax = LSSharedFileListCreate(0x0, **_kLSSharedFileListSessionLoginItems, 0x0);

eax = LSSharedFileListInsertItemURL(eax, **_kLSSharedFileListItemLast, 0x0, 0x0, edi, 0x0, 0x0);

Hopping into a VM and running the malware, turns out it persists twice! First as launch agent (, and then as a login item.

 $ cat ~/Library/LaunchAgents/
 KeepAlive = 0;
 Label = "";
 ProgramArguments = (
 RunAtLoad = 1;

As the launch agent ( has the RunAtLoad is set (to 1), the OS will automatically launch the specified binary, .defaults/ each time the user logs in.

The login item will also ensure the malware is launched. Login items however show up in the UI, clearly detracting from the malware’s stealth:

Is persisting twice better than once? Not really, especially if you are using GamePlan and detecting on both persistence attempts by monitoring launch items and login items.

If you are not an enterprise and would like to defend against these attacks personally, we’re big fans of the Objective-See tools! KnockKnock & BlockBlock which will detect both persistence attempts and can also reveal the infection (after the fact).

For details on persisting as a login item (and the role of backgroundTaskManagementAgent), see my recent blog post on “Block Blocking Login Items” over at Objective-See’s blog page.

Maybe the malware author wanted to be extra extra sure about gaining and/or maintaining persistence!?


In today’s post, we discussed the persistent payload of a rather sophisticated targeted attack against cryptocurrency exchange(s). Via a Firefox 0day, the attackers persistently deployed a macOS binary.

Rather interestingly this malware was OSX.NetWire.A, which bares some relation to a 2012 specimen of the same name. Similarly intriguingly we showed how a signature deployed by Apple in 2016 would detects this malware. Or would it?

Recall that in his original email, the infected user noted that the malware bypassed Gatekeeper. This is actually unsurprising as the malware was delivered by a remote 0day exploit. Gatekeeper only scans applications that have a quarantine attribute set. This is added by the application (i.e. browser) or OS only when the application is downloaded via normal means (i.e. by the user). Exploit code that downloads a payload (such as malicious application) will not set a quarantine attribute (or can remove it), thus will not trigger Gatekeeper!

I highlighted this (well known) fact in a 2016 presentation, “Gatekeeper Exposed; Come, See, Conquer“:

XProtect similarly only operates on files that have the quarantine bit set.

However, this may be changing in macOS 10.15 (Catalina).

Thus, thanks to the infection vector (a Firefox 0day), neither Gatekeeper nor XProtect would protect the user. At Digita Security, we’re proud to be able to detect such infections within the enterprise and help further the privacy efforts being made in Cupertino to enhance the security of the macOS platform.

Part II: Burned by Fire(fox), a firefox 0day drops a macOS backdoor (osx.netwire.a)

As mentioned above, in the section above of this blog post, we discussed the attack and identified the malware as OSX.Netwire.A. Morever, we discussed the malware’s methods of peristence (launch agent and login item):

 $ cat ~/Library/LaunchAgents/
 KeepAlive = 0;
 Label = "";
 ProgramArguments = (
 RunAtLoad = 1;

As the malware’s launch agent ( has the RunAtLoad is set (to 1), the OS will automatically launch the specified binary, .defaults/ each time the user logs in.

The malware’s login item will also ensure the malware is launched when the user logs in. Login items however show up in the UI, clearly detracting from the malware’s stealth:

In this part of the post we’re going to take a technical “deep-dive” into the malicious binary and discuss its:

  • installation logic
  • decryption of its hidden “settings” file
  • decryption of its embedded C&C server address
  • remotely taskable capabilities and features (screenshots, synthetic events, and more!)

Analyzing OSX.NetWire.A

The specimen of OSX.NetWire.A we’re looking at today is named

  • MD5: DE3A8B1E149312DAC5B8584A33C3F3C6
  • SHA1: 23017A55B3D25A2597B7148214FD8FB2372591A5
  • SHA256: 07A4E04EE8B4C8DC0F7507F56DC24DB00537D4637AFEE43DBB9357D4D54F6FF4

We’ll use Hopper to reverse the malware’s binary, and lldb to debug it (in a VM!).

The malware is a standard macOS application, albeit with compatibly for rather ancient versions of OSX! For example it contains only 32-bit code, and according to MachOView and can run on versions of OSX all the way back to OSX 10.5:

We can also note from the MachOView’s output, there aren’t any Objective-C sections (__objc*), meaning the malware was not written in Objective-C. (This is rather rare, and usually indicates the malware author is not a native Mac programmer; perhaps more comfortable coding on Windows of Linux).

The class-dump utility confirms this as well (note it outputs: This file does not contain any Objective-C runtime information.):

 $ class-dump 
// Generated by class-dump 3.5 (64 bit).
// class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.

// File:
// UUID: 8C8AC65D-8F7C-368A-AD5A-E7FD9D58E63C
// Arch: i386
// Minimum Mac OS X version: 10.5.0
// SDK version: 10.12.0

// This file does not contain any Objective-C runtime information.

When analyzing a piece of malware, it’s always a good idea to dump any embedded strings (as this often gives you valuable insight in the functionality and capabilities of the malware). Using the built-in strings utility (with the -a flag) we can extract all printable (ascii) strings from a binary:

 $ strings -a

CONNECT %s:%d HTTP/1.0
Host: %s:%d
GET / HTTP/1.1
Current IP Address: 
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.8




Clearly some interesting strings, which immediately reveal insight to the malware capabilities (such as networking, persistence, and more!). Unfortunately this doesn’t reveal information such as the path of the malware’s installation directory (a valuable IOC), nor the address of the command & control server. To be fair, this is unsurprising as malware authors often encrypt such information to hinder analysis. To uncover these, we’ll have to dig deeper!

The entry point of the malware can be found in the LC_UNIXTHREAD load command, specifically in the EIP register. Using otool we can dump this command and extract the address (0x0000196c):

 $ otool -l 


Load command 9
 cmdsize 80
 flavor i386_THREAD_STATE
 eax 0x00000000 ebx 0x00000000 ecx 0x00000000 edx 0x00000000
 edi 0x00000000 esi 0x00000000 ebp 0x00000000 esp 0x00000000
 ss 0x00000000 eflags 0x00000000 eip 0x0000196c cs 0x00000000
 ds 0x00000000 es 0x00000000 fs 0x00000000 gs 0x00000000

Hopping into a disassembler, the code at 0x0000196c contains the standard entry-point logic for the C-runtime:

 1int EntryPoint() {
 2 ebx = *(&stack[-4] + 0x4);
 3 ebx = (ebx + 0x1 << 0x2) + &stack[-4] + 0x8;
 4 do {
 5 eax = *ebx;
 6 ebx = ebx + 0x4;
 7 } while (eax != 0x0);
 8 eax = main();
 9 eax = exit(eax);
10 return eax;

Note the call to the malware’s main function (line 8). We’ll continue analysis there.

The malware main function (0x00006b17) begins by decrypting various embedded data.

 int main() {
 edx = *dword_e700;
 edi = *dword_e6f0;
 esi = edi << 0xb ^ edi;
 edi = *dword_e6fc;
 ebx = *dword_e6f4;
 *dword_e6f4 = edi;
 ecx = eax & edx ^ edi >> 0x13 ^ esi ^ edi ^ esi >> 0x8 ^ *dword_e704;
 *dword_e704 = ecx;
 *dword_e6f0 = *dword_e6f8;
 *dword_e6f8 = edi >> 0x13 ^ esi ^ edi ^ esi >> 0x8;
 *dword_e6fc = (ebx << 0xb ^ ebx) >> 0x8 ^ ebx << 0xb ...) >> 0x13;
 *dword_e700 = (ebx << 0xb ^ ebx) >> 0x8 ^ ebx << 0xb ...) >> 0x13 ^ edx ^ (ecx | eax);
 ecx = 0x1;
 eax = 0x0;
 do {
 ebx = ecx & 0xff;
 *(int8_t *)(esp + ebx + 0x1898) = eax;
 esi = eax;
 *(int8_t *)(esp + eax + 0x5898) = ecx;
 ecx = 0x0;
 do {
 eax = ebx + ebx;
 ecx = ecx ^ HIBYTE(HIBYTE(ebx) & -(ebx & 0x1));
 edx = (HIBYTE(ebx) & -(ebx & 0x1)) >> 0x1;
 ebx = eax ^ 0x11b;
 if ((HIBYTE(eax) & 0x1) == 0x0) {
 ebx = eax;
 } while (edx != 0x0);
 eax = esi + 0x1;
 } while (eax != 0xff);

This encrypted data is found near the start of the __data section (address 0x0000D2F0 on disk, 0x0000E2F0 in memory):

Generally speaking, it’s easiest to set a breakpoint (in a debugger) after the decryption logic and then simply dump the decrypted data. This is trivial to do in lldb. (We choose to set a breakpoint on address 0x00007658, which follows the decryption logic)

 $ lldb

(lldb) process launch --stop-at-entry
(lldb) b 0x00007658
Breakpoint 1: where = Finder`Finder[0x00007658], address = 0x00007658

(lldb) c
Process 1130 resuming
Process 1130 stopped (stop reason = breakpoint 1.1)

(lldb) x/100xs 0x0000e2f0 --force
0x0000e2f0: ""
0x0000e2f8: ";"
0x0000e4f8: "Password"
0x0000e52a: "HostId-%Rand%"
0x0000e53b: "Default Group"
0x0000e549: "NC"
0x0000e54c: "-"
0x0000e555: "%home%/.defaults/Finder"
0x0000e5d6: ""
0x0000e607: "{0Q44F73L-1XD5-6N1H-53K4-I28DQ30QB8Q1}"

Clearly some interesting (now decrypted!) strings including what appear to be:

  • command and control server: (port: 443)
  • format string for uniquely identifying the host: HostId-%Rand%
  • format string for the malware’s installation location: %home%/.defaults/Finder
  • name of the malware’s launch agent:

Continuing onwards, the malware persistently installs itself by copying its application bundle to the ~/.defaults/ directory. This is accomplished by the function at address 0x000034de, which first opens (via open$UNIX2003) a file handle to the malware’s own binary image:

 (lldb) c
Process stopped

-> 0x3501: calll 0xc930 ; symbol stub for: open$UNIX2003

(lldb) x/x $esp
0xbfff8190: 0xbfff95f0
(lldb) x/s 0xbfff95f0
0xbfff95f0: "/Users/user/Desktop/Wirenet (NetWeirdRC)/A/"


…and then copies itself to the ~/.defaults/ directory via a read$UNIX2003 / write$UNIX2003 loop:

 do {
 if (ebx != 0x0) {
 memset(ebp, 0x0, ebx);
 esp = (esp - 0x10) + 0x10;
 esp = (esp - 0x10) + 0x10;
 esi = read$UNIX2003(edi, ebp, ebx);
 if (esi <= 0x0) {
 esp = (esp - 0x10) + 0x10;
 } while (write$UNIX2003(var_20, ebp, esi) == esi);

Copying a file in this manner (i.e. a read/write loop) is a rather traditional C/linux approach.

Using Cocoa APIs, this entire function could have been replaced with a single line of code: [[NSFileManager defaultManager] copyItemAtPath:source toPath:destination error:nil];

To passively observe the malware installing itself we can use macOS’s built-in file monitor utility: fs_usage:

 # fs_usage -w -f filesystem | grep Finder

mkdir /Users/user/.defaults Finder.7868
mkdir /Users/user/.defaults/ Finder.7868
mkdir /Users/user/.defaults/ Finder.7868
mkdir /Users/user/.defaults/ Finder.7868

WrData[A] /Users/user/.defaults/ Finder.7868
WrData[A] /Users/user/.defaults/ Finder.7868
chmod /Users/user/.defaults/ Finder.7868

After installing itself the malware spawns this installed copy (~/.defaults/, then exits.

We can observe the launch of the installed copy via our open-source ProcInfo utility:

 # procInfo
process monitor enabled...

process start:
pid: 865
path: /Users/user/.defaults/

The installed copy of the malware first persists itself. As we noted early in the post, and in part one, persistence is achieved as a launch agent (~/Library/LaunchAgents/ and a login item.

The code for persisting the malware as a launch agent can be found at 0x000079f3:

 memcpy(esi, "\n\n\n\n Label\n %s\n ProgramArguments…", 0x190);

eax = getenv("HOME");

eax = __snprintf_chk(&decodeBuffer, 0x400, 0x0, 0x400, "%s/Library/LaunchAgents/", eax);
if (mkdir_forAgent() != 0x0) {
 eax = __snprintf_chk(edi, 0x400, 0x0, 0x400, "%s%s.plist", &decodeBuffer, 0xe5d6);
 eax = asprintf(&var_688C, esi);
 edi = open$UNIX2003(edi, 0x601);
 if (edi >= 0x0) {
 write$UNIX2003(edi, var_688C, ebx);
 esp = (esp - 0x10) + 0x10;

In short, it configures then writes out an embedded plist to ~/Library/LaunchAgents/ Recall the name of the plist, was decrypted when the malware was launched.

The code for persisting as a login item immediately follows. It utilizes the LSSharedFileListCreate and LSSharedFileListInsertItemURL APIs to add the malware to the user’s list of login items:

 eax = __snprintf_chk(&decodeBuffer, 0x400, 0x0, 0x400, "", &var_748C, &var_788C);
eax = CFURLCreateFromFileSystemRepresentation(0x0, &decodeBuffer, eax, 0x1);
if (eax != 0x0) {
 eax = LSSharedFileListCreate(0x0, **_kLSSharedFileListSessionLoginItems, 0x0);
 if (eax != 0x0) {
 eax = LSSharedFileListInsertItemURL(eax, **_kLSSharedFileListItemLast, 0x0, 0x0, edi, 0x0, 0x0);

Once the installed malware has persisted itself (in two different ways), it parses (or creates, if not present), the .settings.conf. This file contains 0x7F bytes of encrypted data, that’s save’d into the malware’s application bundle:

 $ hexdump -C ~/.defaults/ 
00000000 4d b8 e4 95 77 1e a5 8a 25 a9 f8 9b af 73 30 e4 |M...w...%....s0.|
00000010 6e 6f 1d d8 da e9 36 a9 f2 76 ab 25 db e7 65 79 |no....6..v.%..ey|
00000020 95 0d 01 c9 fe 3e 2b 5d 51 c8 05 d5 0d 5d 66 14 |.....>+]Q....]f.|
00000030 fb 59 56 bd ed 74 ea 2b fe 31 35 c0 54 70 65 f2 |.YV..t.+.15.Tpe.|
00000040 16 16 10 8b cb 87 93 99 6b 4a cb 91 19 f0 7b f3 |........kJ....{.|
00000050 e3 32 2b ad 11 d8 70 3c 01 aa 1d c8 89 2b b8 0c |.2+...p<.....+..|
00000060 09 0e 62 ca 9a b8 5e 30 ad 65 82 b0 57 65 a1 a6 |..b...^0.e..We..|
00000070 89 d6 b2 d6 a8 d8 03 fd 23 8e 1e 4b 09 59 b6 |........#..K.Y.|

…. but again, we can simply use a debugger to decrypt! We just have to find where to set a breakpoint, after the data has been decrypted.

At 0x00007dbf the malware invokes the open$UNIX2003 API to open the .settings.conf file and then at 0x00007e82, reads in the entire contents (size: 0x7f). Two decryption functions are invoked: sub_9487 and sub_950.

Here’s a snippet from the sub_950 function:

 do {
 edx = edx + 0x1 & 0xff;
 eax = *(int8_t *)(esp + edx + 0x8) & 0xff;
 ebx = ebx + eax & 0xff;
 ecx = *(int8_t *)(esp + ebx + 0x8);
 *(int8_t *)(esp + edx + 0x8) = ecx;
 *(int8_t *)(esp + ebx + 0x8) = eax;
 *(int8_t *)edi = *(int8_t *)edi ^ *(int8_t *)(esp + (ecx + eax & 0xff) + 0x8);
 edi = edi + 0x1;
 esi = esi - 0x1;
} while (esi != 0x0);

In reality though, we don’t really care how the data is decrypted. We can simply set a breakpoint after the call to the 2nd decryption function, and dump the (now) decrypted data:

 $ lldb


(lldb) c
Process stopped (stop reason = breakpoint 1.1)
 frame #0: 0x00007ec0 Finder
-> 0x7ec0: addl $0x10, %esp

(lldb) x/0x7fx 0x0002eb8c
0x0002eb8c: 0x48 0x6f 0x73 0x74 0x49 0x64 0x2d 0x65
0x0002eb94: 0x79 0x6d 0x38 0x49 0x67 0x00 0x00 0x00
0x0002eb9c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x0002eba4: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x0002ebac: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x0002ebb4: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x0002ebbc: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x0002ebc4: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x0002ebcc: 0x44 0x65 0x66 0x61 0x75 0x6c 0x74 0x20
0x0002ebd4: 0x47 0x72 0x6f 0x75 0x70 0x00 0x00 0x00
0x0002ebdc: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x0002ebe4: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x32
0x0002ebec: 0x30 0x31 0x39 0x2f 0x30 0x36 0x2f 0x32
0x0002ebf4: 0x32 0x20 0x30 0x36 0x3a 0x32 0x33 0x3a
0x0002ebfc: 0x32 0x35 0x00 0x00 0x00 0x00 0x00 0x00
0x0002ec04: 0x00 0x00 0x00 0x00 0x00 0x00 0x00

(lldb) x/100s 0x0002eb8c
0x0002eb8c: "HostId-eym8Ig"

0x0002ebcc: "Default Group"

0x0002ebeb: "2019/06/22 06:23:25"

Lovely! We now can see exactly what’s stored in the malware’s encrypted .settings.conf file:

  • the infected host’s unique ID
  • the infected host’s group (groups allow an attacker organize hosts, perhaps based on companies, etc.)
  • what appears to be a “time of infection” timestamp (that is generated when the malware first runs).

Next the malware connects to its command & control server for tasking. Recall we previously uncoverd what appeared to be the decrypted IP address and port of this server: ";" (found at address: 0x0000e2f8).

At 0x1f30 the malware invokes the gethostbyname API. By setting a breakpoint on this call we can confirm that is indeed the c&c address:

 $ lldb

(lldb) b gethostbyname
Breakpoint 1: where = libsystem_info.dylib`gethostbyname

(lldb) c
Process stopped (stop reason = breakpoint 1.1)
 frame #0: 0xa7bd3540 libsystem_info.dylib`gethostbyname
-> 0xa7bd3540 <+0>: pushl %ebp

(lldb) x/wx $esp+4
0xbfff79c0: 0x00112240

(lldb) x/s 0x00112240
0x00112240: ""

The gethostbyname function takes a single argument; a string representing a hostname or ip address.

As such, when the breakpoint hit, we can print this string by examining the pointer at at $esp+4 (i.e the first argument).

A connection to the server, is then attempted, on port 443:

 int 0x00009a78 {
 edi = socket(0x2, 0x1, 0x6);
 esi = 0xffffffff;
 if (edi != 0xffffffff) {
 esp = (esp - 0x10) + 0x10;
 connect$UNIX2003(edi, ebx, 0x10);


A firewall, such as LuLu, can generically detect this connection attempt:

Unfortunately (for our analysis efforts), this connection (now) fails, as apparently the malware’s c&c server is offline:

 $ lsof -i TCP
Finder 843 user IPv4 TCP> (CLOSED)

Attempting to manually connect to the c&c server also fails:

$ curl
curl: (7) Failed to connect to port 443: Connection refused

Not to worry, via static code analysis, we can uncover the code and logic within the malware that responds to tasking from the c&c server. This in turn allows to ascertain the malware’s capabilities that a remote attacker can invoke.

At address 0x000021cb the malware invokes a function that invokes the receive API (recv$UNIX2003). Assuming (valid) tasking data is received, a function at 0x00004109 is then called to act on said tasking.

Before this function is invoked, the first byte of data (received from the c&c server), is moved into the dl register:

 mov dl, byte [esp+ecx+0x78ac+dataFromServer] 

In the 0x00004109, this (same) byte is used as an index in a switch table:

 0x00004125 dec dl
0x00004127 cmp dl, 0x42
0x0000412a ja loc_6a10


0x00004145 movzx eax, dl
0x00004148 jmp dword [switch_table_d1b0+eax*4] 

This is a fairly common “protocol pattern” in malware, where the first byte received instructs the malware has task or action to perform. In other words it’s the “command” to execute.

To uncover the remotely taskable capabilities of the malware, let’s see what functions are referenced in the switch table (0x0000d1b0).

Due to time constraints, we won’t comprehensively discuss all the malware’s (remote) capabilties.

However, we’ll highlight some basics, and some more intriguing ones.

Most fully-featured backdoors (or implants) have logic to perform a survey of an infected host. OSX.NetWire.A is no different. Via “command” 0x23 (35d) the malware gathers various information about the system on which is finds itself running. This includes:

  • user information (via geteuid, getpwuid, and %USER%)
  • host information (via gethostname)
  • cpu information (via machdep.cpu.brand_string/sysctlnametomib)
  • system version information (via CFCopySystemVersionDictionary)
  • malware’s binary image (via getpid / proc_pidpath)
  • environement variables (such as %HOME% via getenv)
  • system architecture (via uname)
  • malware’s pid (via getpid)
  • system time information (via localtime)

This survey information is encoded (encrypted?) and formatted into the following (rather massive) format string:

 eax = asprintf(&var_1024, "%s\x07%d\x07%s\x07%s\x07%s\x07%llu\x07%llu\x07%llu\x07%c\x07%s\x07%s\x07%s\x07%s\
x07%d\x07%s\x07%d\x07%s\x07%d\x07%d\x07%s\x07%d\x07%s\x07%d\x07%s\x07%d\x07", &var_284C, var_3484, &var_1224, &var_1424, &var_304C, var_3494, 0x0, var_34A0, var_3498, var_348C, var_3490, ebx, &var_344C, &var_244C, &var_346C, &var_1444, 0x20, 0xe2f8, edi, 0xe3f8, esi, var_3488, 0xe4f8, ebp, &var_2C4C); 

…this is then sent back to the malware’s c&c server.

As expected the malware can also be remotely tasked to perform various file actions such as rename, make directory, delete etc.

For example “commamd” 0x1A (26d) will rename a file:

 0x00004f37 push ebx 
0x00004f38 push edi
0x00004f39 call imp___symbol_stub__rename

…while “command” 0x1B (27d) will delete a file via the unlink API:

 0x00004f5e sub esp, 0xc 
0x00004f61 push esi
0x00004f62 mov edi, ecx
0x00004f64 call imp___symbol_stub__unlink

Other commands appear to be related to the reading, writing, and creating of files:

 ; case 0x23,
0x0000514e sub esp, 0x4
0x00005151 push ebp
0x00005152 push esi
0x00005153 push dword [dword_eb68]
0x00005159 call imp___symbol_stub__write$UNIX2003 

OSX.Netwire.A also can be remotely tasked to interact with proceses(s), for example listing them (“command” 0x42, 66d):

 ; case 0x42,
push esi
push edi
push 0x0
push 0x1
call imp___symbol_stub__proc_listpids

…or killing them (“command” 0x2C, 44d):

 ; case 0x2C,
0x000056fa push 0x9
0x000056fc push eax
0x000056fd call imp___symbol_stub__kill$UNIX2003

Via “command” 0x19 (25d) the malware will invoke a helper method, 0x0000344c which will fork then execv a process:

 1eax = fork();
2if (((eax == 0xffffffff ? 0x1 : 0x0) != (eax <= 0x0 ? 0x1 : 0x0)) && (eax == 0x0)) {
3 execv(esi, &var_18);
4 eax = exit(0x0);

When the malware receives “command” 0x22 (34d), it invokes a helper function (0x00002652) to execute a shell commands via either /bin/sh or /bin/bash/:

 1if (stat("/bin/sh", edi) != 0x0) {
 2 ebp = "/bin/bash";
 4else {
 5 ebp = "/bin/bash";
 6 if ((var_109C & 0xffff & 0xf000) == 0x8000) {
 7 ebp = "/bin/sh";
 8 }
11ebx = posix_openpt(0x2);
18ioctl(0x0, 0x20007461);
20execvp(ebp, &var_A4);

With the ability to execute arbitrary commands the malware is essentially infinitely extensively and gives a remote attacker full control over the infected system.

The malware can also interact with the UI, for example to capture a screen shot. When the malware receives “command” 0x37 (55d), it invokes the CGMainDisplayID and CGDisplayCreateImage to create an image of the user’s desktop:

 0x0000622c movss dword [esp+0x34ac+var_101C], xmm0
0x00006235 call imp___symbol_stub__CGMainDisplayID
0x0000623a sub esp, 0xc
0x0000623d push eax
0x0000623e call imp___symbol_stub__CGDisplayCreateImage

Interestingly it also appears that OSX.Netwire.A mayb be remotely tasked to generate synthetic keyboard and mouse events. Neat!

Specifically synthetic keyboard events are created and posted when “command” 0x34 (52d) is recieved from the c&c server. To create and post the event, the malware invokes the CGEventCreateKeyboardEvent and CGEventPost APIs.

Synthetic mouse events (i.e. clicks, moves, etc) are generated in response to “command” 0x35 (53d):

 1void sub_9a29() {
2 edi = CGEventCreateMouseEvent(0x0, edx, ...);
3 CGEventSetType(edi, edx);
4 CGEventPost(0x0, edi);
5 return;

Finally, via “command” 0x7 it appears that the malware can be remotely instructed to uninstall itself. Note the calls to unlink to remove the launch agent plist and the malware’s binary image, and the call to LSSharedFileListItemRemove to remove the login item:

 1__snprintf_chk(&var_284C, 0x400, 0x0, 0x400, 
 2 "%s/Library/LaunchAgents/%s.plist", getenv("HOME"), 0xe5d6);
 3eax = unlink(&var_284C);
 5if (getPath() != 0x0) {
 6 unlink(esi);
 9LSSharedFileListItemRemove(var_34A4, esi);


Via a Firefox 0day, attackers targeted employees of various cryptocurrency exchange(s) in order to persistently deploy a macOS binary (OSX.NetWire.A).

In today’s post, we tore apart the malware to reveal (for the first time!), its inner workings and complex capabilities.

Specifically we illustrated how to recover the encrypted address of the command & control server, how to decrypt the malware’s hidden .settings.conf file, and most importantly detailed the many capabilities of this threat!

Part III: Burned by Fire(fox), a firefox 0day drops another macOS backdoor (osx.mokes.b)

This second sample (sha1: b639bca429778d24bda4f4a40c1bbc64de46fa79) was initially undetected by every engine on VirusTotal

As such, I’ve decided to triage it and share my findings!

It should be noted that for any particular AV engine (on VirusTotal), said engine may only be one (small?) piece of a more complete security product.

That is to say, a company’s comprehensive security product would presummingly include a behavior-based engine (not included on VirusTotal) that perhaps could generically detect this new threat (i.e GamePlan).

Static Analysis

Named mac the sample we’re analyzing today was submitted to VirusTotal on 2019-06-20:

  • MD5: AF10AAD603FE227CA27077B83B26543B
  • SHA1: B639BCA429778D24BDA4F4A40C1BBC64DE46FA79
  • SHA256: 97200B2B005E60A1C6077EEA56FC4BB3E08196F14ED692B9422C96686FBFC3AD

Currently it’s unnamed by the anti-virus community, although Vitali Kremez notes that it appears to be related to OSX.Mokes (more on this later).

Let’s begin by statically analyzing the binary.

As shown by the file utility, the malware is a 64-bit mach-O binary, albeit a rather large one (13M!):

 $ file ~/Downloads/mac 
~/Downloads/mac: Mach-O 64-bit executable x86_64

$ du -h ~/Downloads/mac 

13M ~/Downloads/mac

Via “WhatsYourSign” utility we can quickly see the malware is unsigned:

We can also note from the MachOView’s output, it’s linked against a variety of “interesting” libraries/frameworks:

Prefer the terminal?

Use macOS’s built-in otool utility (with the -L flag) to view linked libraries/frameworks

Such “interesting” libraries/frameworks, that may shed some insight into the malware’s capabilities include:

  • IOKit.framework
  • AVFoundation.framework
  • CoreWLAN.framework
  • OpenGL.framework
  • CoreVideo.framework

Next, let’s extract embedded (ascii) strings via the built-in strings utility (with the -a flag):

 $ strings -a ~/Downloads/mac





There are also many strings related to statically linked libraries (such as OpenSSL).

This (likely) explains the massive size of the binary!

As noted, a limitation of the strings utility is that is only extracts ascii strings. To extract unicode (or “wide”) strings, I used a modified version of the following python script:

 1def unicode_strings(buf, n=4):
2 reg = b"((?:[%s]\x00){%d,})" % (ASCII_BYTE, n)
3 uni_re = re.compile(reg)
4 for match in uni_re.finditer(buf):
5 try:
6 yield String("utf-16"), match.start())
7 except UnicodeDecodeError:
8 pass

Turns out there are some interesting unicode (wide) strings embedded in the binary:

 $ python ~/Downloads/ ~/Downloads/mac

0x8f0018: :/keys/bot
0x8f01a8: %1/Library/LaunchAgents/%2.plist
0x8f0210: powershell.exe 
0x8f0248: dd*.ddt
0x8f0270: kk*.kkt
0x8f0298: aa*.aat
0x8f02c0: ss*.sst
0x8f02e8: application/octet-stream
0x8f0338: Content-Type
0x8f0378: JPEG
0x8f03c0: auto-file-search
0x8f0400: :/file-search
0x8f0478: search%1
0x8f0910: s://
0x8f0957: keys
0x8f0963: dbot

Embedded in the binary are also 5000+ urls! A few are included here: …

Moving on, via class-dump utility we can reconstruct embedded Objective-C class information. Thought it’s mostly QT related classes there are some interesting AVFoundation protocols that are generally associated with recording off the webcam:

 $ class-dump ~/Downloads/mac 
// Generated by class-dump 3.5 (64 bit).
// File: ~/Downloads/mac
// Arch: x86_64
// Minimum Mac OS X version: 10.7.0
// SDK version: 10.9.0

@protocol AVCaptureFileOutputRecordingDelegate 
- (void)captureOutput:(AVCaptureFileOutput *)arg1 didFinishRecordingToOutputFileAtURL:(NSURL *)arg2 fromConnections:(NSArray *)arg3 error:(NSError *)arg4;

- (void)captureOutput:(AVCaptureFileOutput *)arg1 willFinishRecordingToOutputFileAtURL:(NSURL *)arg2 fromConnections:(NSArray *)arg3 error:(NSError *)arg4;
- (void)captureOutput:(AVCaptureFileOutput *)arg1 didResumeRecordingToOutputFileAtURL:(NSURL *)arg2 fromConnections:(NSArray *)arg3;
- (void)captureOutput:(AVCaptureFileOutput *)arg1 didPauseRecordingToOutputFileAtURL:(NSURL *)arg2 fromConnections:(NSArray *)arg3;
- (void)captureOutput:(AVCaptureFileOutput *)arg1 didStartRecordingToOutputFileAtURL:(NSURL *)arg2 fromConnections:(NSArray *)arg3;

Dynamic Analysis

Ok, time to hop in to Virtual Machine (VM) to execute the malware and see what it does! Generally when dynamically analyzing malware, I’m looking for things such as:

  • files created
  • processes started
  • network connections

To passively observe file events related to the malware, we can use macOS’s built-in file monitor utility: fs_usage:

 # fs_usage -w -f filesystem | grep mac 

mkdir /Users/user/Library/Dropbox mac.14997

open /Users/user/Desktop/mac
RdData[A] /Users/user/Desktop/mac mac.14997

open /Users/user/Library/Dropbox/quicklookd
WrData[A] /Users/user/Library/Dropbox/quicklookd mac.14997

execve /Users/user/Library/Dropbox/quicklookd 

The fs_usage will capture file events for the entire system, which quickly can become overwhelming.

As such, it’s recommended to filter the output, for example by piping it into grep and matching on the malware (i.e. ‘mac’).

From the fs_usage output, it’s fairly clear to see that the malware is installing itself to the ~/Library/Dropbox/ directory as quicklookd

A quick check (of the hashes) confirms that the “new” binary quicklookd is indeed simply a copy of the malware:

 $ shasum -a 1 ~/Desktop/mac ~/Library/Dropbox/quicklookd 
b639bca429778d24bda4f4a40c1bbc64de46fa79 /Users/user/Desktop/mac
b639bca429778d24bda4f4a40c1bbc64de46fa79 /Users/user/Library/Dropbox/quicklookd

We can also see in the file monitor output that after making a copy of itself, the malware executes (via execve) this copy. Our process monitor, ProcInfo, also captures this event:

 procInfo[730:14509] process start:
pid: 733
path: /Users/user/Library/Dropbox/quicklookd
user: 501
args: (
 signing info: {
 signatureStatus = "errSecCSUnsigned (-67062)";

In terms of networking, a firewall such as LuLu, detects a connection attempt to what is likely the malware’s command & control server,

The built-in command lsof utility also shows this connection attempt:

 lsof -i TCP

quicklookd 733 user IPv4 TCP> (SYN_SENT)

After a few seconds the malware persists:

Apparently the (installed copy of the) malware persists itself as a launch agent. Let’s dump it’s launch agent plist (quicklookd.plist):

 $ defaults read ~/Library/LaunchAgents/quicklookd.plist 
 KeepAlive = 1;
 Label = quicklookd;
 ProgramArguments = (
 RunAtLoad = 1;

As the launch agent (quicklookd.plist) has the RunAtLoad key set (to 1), the OS will automatically launch the specified binary, /Users/user/Library/Dropbox/quicklookd each time the user logs in. This provides the malware persistence.

At this point, our dynamic analysis as provided valuable insight into the malware including its:

  • installation
  • persistence (launch agent)
  • address of command & control server

Let’s dig a little deeper into the installation logic, as (as we’ll see) the malware implements some “variability” to perhaps complicate detection.

Recall that when extracting embedded strings, we saw strings such as quicklookd and Dropbox that the malware used during the installation and persistence process (that is to say the malware installed itself as quicklookd into a directory named Dropbox).

In the malware binary, in close proximity, there are other embedded strings:

Rather interestingly, restoring the VM to a pristine state and (re)running the malware, results in the malware selecting one of the other strings pairs (e.g. App Store / storeaccountd) for installation and persistence purposes. For example, here’s another persistence alert (note the malware installed itself this time, as storeaccountd):

With a general understanding of the malware’s installation and persistence, and recovering an address of (the likely) command & control server, let’s talk about the malware’s capabilities.

However, first we need to talk about OSX.Mokes …as this will, in actuality, reveal the malware’s capabilities!

The OSX.Mokes Connection

A day or two ago, Vitali Kremez tweeted the following, stating that this malware is “is likely linked to…Backdoor.OSX.Mokes“:

…as we’ll see, I believe he’s 100% correct.

OSX.Mokes is cross-platform, fully-featured backdoor that was discovered by Kaspersky in 2016. In an excellent writeup, “The Missing Piece – Sophisticated OS X Backdoor Discovered”, they detailed OSX.Mokes installation, persistence, network comms and rather impressive capabilities (screen capture, audio capture, document discovery & exfiltration, and more).

I’ve blogged about OSX.Mokes before and even mentioned it several conference talks:

Stop me if this sounds familiar but in a previous blog, from 2017, I stated:

…the malware [OSX.Mokes] may install itself into multiple locations.

Besides the standard storeuserd name, the malware may install itself to:

  • ~/Library/
  • ~/Library/Dock/
  • ~/Library/Skype/SkypeHelper
  • ~/Library/Dropbox/DropboxCache
  • ~/Library/Google/Chrome/nacld
  • ~/Library/Firefox/Profiles/profiled

And take a look at a side-by-side comparison of OSX.Mokes (from 2016) and the sample from today:

Though names (in some cases) been slightly changed, clearly there is a ton of overlap.

Many other commonalities between the two samples are present, for example when we start comparing the malware’s capabilities. In Kaspersky’s original writeup on OSX.Mokes they state:

This malware…is able to steal various types of data from the victim’s machine (Screenshots, Audio-/Video-Captures, Office-Documents, Keystrokes).”

Looking at the original OSX.Mokes sample, we find several hard-coded file search constants:

 10x0000001C unicode :/file-search
20x0000000E unicode *.xlsx
30x0000000C unicode *.xls
40x0000000E unicode *.docx
50x0000000C unicode *.doc

In the malware we’re looking at today, as noted by Vitali Kremez, we find the exact same file search constants (albeit in a slightly different order):

 10x00000001008f0400 unicode :/file-search
20x00000001008c0fed unicode *.doc
30x00000001008c0ff3 unicode *.docx
40x00000001008c0ffa unicode *.xls
50x00000001008c1000 unicode *.xlsx

Kaspersky also noted in their writeup from 2016 that:

“Just like on other platforms, the malware creates several temporary files containing the collected data if the C&C server is not available.

  • $TMPDIR/ss0-DDMMyy-HHmmss-nnn.sst (Screenshots)
  • $TMPDIR/aa0-DDMMyy-HHmmss-nnn.aat (Audiocaptures)
  • $TMPDIR/kk0-DDMMyy-HHmmss-nnn.kkt (Keylogs)
  • $TMPDIR/dd0-DDMMyy-HHmmss-nnn.ddt (Arbitrary Data)”

Again, in the malware we’re looking at today we see a clear overlap; specifically in the file extensions used to store collected data:

Many other commonalities (such as unique strings, cross-platform static compiled libraries, capabilities, and even size (approximately 13M)) IMHO confirm that the malware we analyzed today is new variant of OSX.Mokes.

I’m calling it OSX.Mokes.B


Via a Firefox 0day, attackers targeted employees of various cryptocurrency exchange(s) in order to persistently deploy two macOS backdoors.

In part one and part two of this blog post series, we discussed the attack and analyzed the first backdoor (OSX.Netwire.A).

In today’s post, we analyzed the second specimen (SHA1: B639BCA429778D24BDA4F4A40C1BBC64DE46FA79) and discussed its:

  • installation
  • method of persistence
  • command & control server
  • capabilities

Most interestingly we confirmed that (as noted by others) the second persistent backdoor is undoubtedly a new variant of OSX.Mokes

To conclude, recall that although this sample, OSX.Mokes.B is closely related to the original OSX.Mokes sample (from 2016) not a single anti-virus engine on VirusTotal detected it as malicious!

This is (yet another) example of why it’s far better to detect malicious behaviors versus static signatures.