Basic MacOS Static Malware Analysis
Information and Techniques for Static Analysis of MacOS Malware and Persistence Mechanisms
This doc has been heavily informed by the Objective by the Sea MacOS conference. I highly suggest you check out these links:
Objective See Foundation
Objective by the Sea Security Conference
The Art of Mac Malware Vol.1 (read for free online!)
The Art of Mac Malware Vol.2 (read for free online!)
Objective See Foundation Discord
Special thanks to for helping with this documentation.
Introduction
Before we dive into the exciting details, I would like to take a moment to discuss the present threat landscape for MacOS, as there are significant distinctions from the Windows threat landscape that we are more accustomed to.
In general, most of the malware you encounter on MacOS will consist of adware and info stealers. This is likely related to the heightened difficulty in running malicious code on the MacOS platform, along with the fact that MacOS threat actors are typically useless scrubs who lack creativity.
The purpose of adware is fairly obvious. Display ads or hijack clicks to generate revenue for threat actors. Adware is generally categorized as low but does come with persistence mechanisms on disk.
Stealers are much more severe in priority and will usually come in as high or crit, but generally do not drop persistence on disk as they have but one goal, steal creds, cookies, tokens, keychains, etc, then exfil and dip out. (“dip out” is philly for “leave”)
One quick note is that stealers are generally unimpressive and are most if not all running the same stolen Amos code under the hood, however, there has been some activity that shows us stealers may be expanding their capabilities to include remote control, but this is still incredibly rare. (But should be noted as a possible evolution of threat actor techniques.)
So let’s get into it, nerds.
General Info
Infection Vectors
Understanding common infection vectors is just as important as understanding the malware’s activity once on disk.
Sponsored Ads
- We have observed this on Windows. Sponsored advertisements for otherwise genuine software being offered as a trojanized variant on a deceptive yet credible website.
- Advanced IP Scanner
- RoyalTSX
- etc
Pirated Software
- Pirated software is, to us, a fairly obvious way to catch an infection.
- Keygens packed with malware.
- Trojanized versions of the legitimate software.
- Generally speaking, the method of providing the software for free will be packed with malware, whatever that method may be.
Fake Company Websites or Communications
- Website, emails, or other communications offering books, documents, new tools, etc. to the target user end in phishing, trojanized tools, or malicious documents.
Word / Excel Macros
- malicious_file.doc
- Word or excel docs that come packed with malicious macros
Custom URL schemes
- custom url schemes allow a threat actor to define a custom url path on disk to point url hooks like
zoom://to their own malicious path. These are usually packed into a.apppackage and fairly easy to find.- https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app
whatsapp://,zoom://, etc.- Usually stored in the
.apppackage in a file calledinfo.plistinstructing the OS “heyzoom://should actually point tobadstuff.evil”.
- Usually stored in the
- https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app
- Removal (one method):
- Quit chrome
- Open
~/Library/Application Support/Google/Chrome/Default/Preferences - Search for the
protocol_handlerentry - Find the offending app/url and remove it
<https://evil.bad>":{"zoom":true}
Supply Chain Attacks
- Targets the organization or software itself, instead of end user.
- Can hit one target (zoom) and affect 758930275 end users.
- Costs like 20 gold but gives like 400 victory points
- See 3CX, NPM, etc
Etc.
Payload Types
AdInjectors
- AppleScript (osascript) Injector
- Loads ads into browser, usually with JS
- Usually persists in the browser
CryptoMiners
- Usually Monero
- shocking -__-
- Drops persistence
Remote Shells (interactive)
- Example: OSX.Dummy
- OSX.Dummy Objecive See Blog
- Provides adversary the ability to run remote code or commands on the system
- May drop persistence
Remote Execute
- Example: OSX.Komplex
- OSX.Komplex
- Russian backdoor to execute binaries on system
- May drop persistence
Collection / Exfil
- Collects, zips, then yeets files (usually)
- Easily observable process with Process Monitor
- Can be financial in nature (extortion)
Ransomware
- Encrypts files on disk
- Financial in nature
Etc.
Great end-of-year-2023 report by Patrick Wardle: The Mac Malware of 2023
Persistence
My other blog on this subject: Mac the Ripper - Persistence
Persistence Types
- Login Items
- If you see usage of
LSSharedFileListin malicious code, this API is most likely being used to create launch items.
- If you see usage of
- LaunchDaemons
- Requires root privs to create, executes as root
- LaunchAgents
- Root not required to write, does not execute as root
- Cron Jobs
- Login and Logout Hooks
- Kernel Extensions (Kexts)
- Browser Extensions
- etc, see above documents for an in depth look at persistence.
Malware Examples:
- OSX.SilverSparrow
- Clipping Silver Sparrow’s wings: Outing macOS malware before it takes flight by Tony Lambert
- uses plistbuddy to build
.pliston host.
- OSX.AppleJeus
- AppleJeus: Analysis of North Korea’s Cryptocurrency Malware
- delivered with pre-created
.plist, then copied to LaunchDaemon or LaunchAgent directory.
Tools to Check Persistence:
- DumpBTM
- KnockKnock
PIDs (Special mention because they’re annoying on Mac)
The Mac operating system not only uses PIDs and parent PIDs like windows and linux systems, but there are a few more you may not be so familiar with. You can find these detailed below.
PID: This is your run of the mill process ID. Keep in mind, Launchd will always have a PID of 1. This is an issue you will see detailed below.
PPID: This is the Parent PID. It works exactly as you would expect.
PGID: This one is fun. This is the Process Group ID. This one is a lot easier to explain with an example. Let’s say you have a command with several pipes, like so: cmd.exe echo textfile.txt | grep something | sort | uniq. In this example each base command cmd.exe, grep, sort, and uniq will all have their own PIDs and you will have to climb the process tree to see the whole command. However, if we search for the PGID of any of these commands, we can find the rest of the pieces and put together the full command line. An example of how some of these may show in you log source:
| Command Line: | cmd.exe echo textfile.txt |
| PID: | 5520 |
| PPID: | 5519 |
| PGID: | 5518 |
| Â | Â |
| Command Line: | grep something |
| PID: | 5521 |
| PPID: | 5520 |
| PGID: | 5518 |
| Â | Â |
| Command Line: | sort |
| PID: | 5522 |
| PPID: | 5521 |
| PGID: | 5518 |
| Â | Â |
| Command Line: | uniq |
| PID: | 5523 |
| PPID: | 5522 |
| PGID: | 5518 |
Note how they all have the same PGID.
responsible_PID: This is another fun one. Explaining exactly what happens here may be a little difficult but I’ll give it my best shot. Lets pretend we have this process tree:
launchd > process 1 > process 2 > process 3 > process 4
Next let’s pretend process 2 was forked, and the original tree was killed. This would created a forked process 5 with a parentPID of launchd not process 2 since process 2 has been killed and our new process 5 is orphaned. The Mac operating system will identify an orphaned process and assign launchd to be its new adoptive parent. (aww so sweet).
That is not very good for our investigation as it completely nullifies our visibility of Process 1. In comes the responsible_PID. Even if a process is forked and the parent tree is killed, the responsible_pid will give us the PID information for the original parent tree, retaining our ability to properly investigate.
Here’s some beautiful art to help illustrate:
Static Analysis Techniques
Identifying File Type
We cannot always trust the file extension, determining the actual file type is easily step 1 of static analysis.
We can use the file command to determine the file type. (however, this can give you some weak info sometimes.)
A better tool is WhatsYourSign (Referred to beyond this point as WYS) which provides more clear file type info, as well as signing and notarization info.
Analyzing Common File Types
.dmg disk images
- Needs to have
.dmgfile extension.- .dmg disk images cannot launch without the .dmg extension so these will usually always be distributed with this extension.
- Might not be signed. a lot of legitimate disk images don’t get signed because reasons
- May also be “legitimately” signed AND notarized by Apple proper, this does not imply legitimacy.
- file command may fail here, WYS helps, see image:
- Mount disk image for analysis purposes
hdiutil attach [file.dmg]- mounts to
/volumes/and can be cd’d into forpokin'andproddin'.
- mounts to
.pkg packages
pkgutil --expand file.pkg path/to/expand- This will export the package contents to the chosen path for you to poke around.
- will usually have the
.pkgextension filemay struggle, useWYSfor more consistent results.- double clicking a
.pkgfile will execute it along with any pre/post install scripts embedded with it- plz dont
- Tool: SuspiciousPackage can help you drill down into .pkg files with a graphical user interface
- Package Scripts
- executed when a .pkg is launched
- can be used to pack legitimate software with malicious scripts
- can be viewed in
SuspiciousPackageby going to thepreinstallorpostinstalltab.
This postinstall script installs a cryptominer with Launchdaemon persistence
Python Scripts
.pyor.pycfile extension- generally human readable
- with base64, decode with cyberchef (or if
execexists at the end of the script, replace withprintto receive output easily.)
Repacing this exec with print would give you human readable output for the script
- Simply decoding base64 blocks within scripts can sometimes give you quick wins. In this case we get a URL as well as a persistence name and location.
- For compiled python files (
.pyc) we can leverage https://decompiler.com.
Decoded pythin featuring a base64 block
Decoded b64 block for a vaguely human readable output and an IOC
Application Bundles
.appfile extension- Easily viewed with
right click>view contents- directories, files, and subdirectories
info.plistfile is interesting- points to apps binary
- build envitonment
- oldest supported os version
- if the
info.plistcontents begin withbplistit has been compressed, you can uncompress wkth:defaults read info.plistplutil -convert xml11
- Easily viewed with
For example, if we have the below compressed Info.plist, we can read it with defaults read info.plist
1
2
3
4
> file badness.app/Contents/Info.plist Info.plist: Apple binary property list
> cat badness.app/Contents/Info.plist
bplist00<DE>^A^B^C^D^E^F^G^H^K^L^M^N^O^P^Q^R^S^T^W^V^Y^ZESC^\^]^S_^P^ZCFBundleShortVers
ionString_^P^RCFBundleIdentifier_^p^] CFBundleInfoDictionaryVersion_^P^OCFBundleVersion ^p^RCFBundleExecutable_^p^VNSAppTransportSecurity_^P^PNS PrincipalClass [LSUIElement]...
Decompressed:
1
2
3
4
5
6
7
8
9
10
> defaults read badness.app/Contents/Info.plist
{
CFBundleDevelopmentRegion = en;
CFBundleExecutable = DropBox;
CFBundleIconFile = "AppIcon.icns";
CFBundleIdentifier = "inc.dropbox.com";
CFBundleInfoDictionaryVersion = "6.0";
CFBundleName = DropBox;
...
}
Conclusion
It’s no mistake that MacOS is growing in popularity for enterprise applicztions and eventually people who have only ever used windows and linux will have to investigate their first incident on a MacOS system. This blog only scratches the surface, and serves as more of a personal reference for myself than a formal blog, but I hope you find some of the information helpful.
Cheers, nerds! ^_^









