At my day job I’ve been diving into macOS malware, and it turns out that quite a lot of macOS malware gets distributed through installer packages like DMG images and PKG archives. I work on malware using REMnux or a Pop!_OS Linux system, so I wanted to share how I work with these files to analyze them on Linux.
PKG files are xar archive files you can open with bsdtar. I tried to use tar, but bsdtar actually did the trick where tar didn’t. To get bsdtar I ran sudo apt update && sudo apt install libarchive-tools
because it didn’t exist on my system by default. To show how it works, in this post I unpack the legitimate AnonymousVPN client PKG.
To get started, make a folder for the contents of the PKG file and unpack it.
$ mkdir anonymous-vpn
$ bsdtar xvf AnonymousVPN.pkg -C anonymous-vpn/
x Bom
x Payload
x Scripts
x PackageInfo
$ cd anonymous-vpn/
$ ls -lah
total 13M
drwxrwxr-x 2 forensicitguy forensicitguy 4.0K Jan 31 20:54 .
drwxrwxr-x 5 forensicitguy forensicitguy 4.0K Jan 31 20:54 ..
-rw-r--r-- 1 forensicitguy forensicitguy 43K Nov 10 10:04 Bom
-rw-r--r-- 1 forensicitguy forensicitguy 347 Nov 10 10:04 PackageInfo
-rw-r--r-- 1 forensicitguy forensicitguy 13M Nov 10 10:04 Payload
-rw-r--r-- 1 forensicitguy forensicitguy 46K Nov 10 10:04 Scripts
Within the folder you should find multiple files, including a Bill of Materials
(BOM) file, a PackageInfo
file, a Payload
archive, and a Scripts
archive. The last two archive files are gzipped CPIO archives. To unpack the contents of the archives you can execute these commands.
$ cat Payload | gunzip | cpio -i
62475 blocks
$ cat Scripts | gunzip | cpio -i
294 blocks
$ ls -lah
total 13M
drwxrwxr-x 4 forensicitguy forensicitguy 4.0K Jan 31 20:58 .
drwxrwxr-x 5 forensicitguy forensicitguy 4.0K Jan 31 20:54 ..
drwxrwxr-x 3 forensicitguy forensicitguy 4.0K Jan 31 20:57 Applications
-rw-r--r-- 1 forensicitguy forensicitguy 43K Nov 10 10:04 Bom
drwxr-xr-x 6 forensicitguy forensicitguy 4.0K Jan 31 20:57 Library
-rw-r--r-- 1 forensicitguy forensicitguy 347 Nov 10 10:04 PackageInfo
-rw-r--r-- 1 forensicitguy forensicitguy 13M Nov 10 10:04 Payload
-rwxr-xr-x 1 forensicitguy forensicitguy 178 Jan 31 20:58 postinstall*
-rwxr-xr-x 1 forensicitguy forensicitguy 147K Jan 31 20:58 preinstall*
-rw-r--r-- 1 forensicitguy forensicitguy 46K Nov 10 10:04 Scripts
After unpacking Payload, you may find one or more folders that include MACH-O binaries and other data for an application. If the Payload doesn’t unpack into anything, it means that you have an empty, payload-free package. In these packages, the installation relies on the contents of the Scripts archive.
After unpacking Scripts, you may find a preinstall
and postinstall
file. As their names suggest, these files contain content run before the PKG installer runs and then after. While they live within the Scripts archive, they can be any form of executable content for macOS. They may exist as Bash shell scripts, Python or Ruby scripts, or even MACH-O binaries. We can observe the file types by executing file
.
$ file *
Applications: directory
Bom: Mac OS X bill of materials (BOM) file
Library: directory
PackageInfo: ASCII text
Payload: gzip compressed data, from Unix, original size modulo 2^32 31987200
postinstall: Bourne-Again shell script, ASCII text executable
preinstall: Mach-O 64-bit x86_64 executable, flags:<NOUNDEFS|DYLDLINK|TWOLEVEL|WEAK_DEFINES|BINDS_TO_WEAK|PIE>
Scripts: gzip compressed data, from Unix, original size modulo 2^32 150528
From here you can dive into analyzing the scripts and binaries as you please.
DMG files are like ISO/IMG/VHD files on Windows platforms. They contain a filesystem that house multiple files and folders. DMGs can be compressed or not, and they can contain one of multiple filesystem types. To show opening a DMG, I’ll open up a DMG containing a cracked version of Little Snitch you can find passed around pirated app sites.
$ 7z x Little_Snitch_4.0.3_CR2_\[TNT\].dmg
7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,8 CPUs Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz (806EA),ASM,AES-NI)
Scanning the drive for archives:
1 file, 52963974 bytes (51 MiB)
Extracting archive: Little_Snitch_4.0.3_CR2_[TNT].dmg
--
Path = Little_Snitch_4.0.3_CR2_[TNT].dmg
Type = Dmg
Physical Size = 52963974
Method = Copy Zero2 CRC
Blocks = 15
----
Path = 4.hfs
Size = 59727872
Packed Size = 52920832
Comment = disk image (Apple_HFS : 4)
Method = Copy Zero2 CRC
--
Path = 4.hfs
Type = HFS
Physical Size = 59727872
Method = HFS+
Cluster Size = 4096
Free Space = 6807552
Created = 2017-10-02 07:19:41
Modified = 2017-10-01 23:19:58
Everything is Ok
Folders: 5
Files: 6
Size: 50799922
Compressed: 52963974
$ ls -lah
total 51M
drwxrwxr-x 3 forensicitguy forensicitguy 4.0K Jan 31 21:08 .
drwxrwxr-x 3 forensicitguy forensicitguy 4.0K Jan 31 21:08 ..
drwx------ 6 forensicitguy forensicitguy 4.0K Oct 2 2017 'Little Snitch 4.0.3 CR2'
-rw-rw-r-- 1 forensicitguy forensicitguy 51M Feb 9 2019 'Little_Snitch_4.0.3_CR2_[TNT].dmg'
$ cd Little\ Snitch\ 4.0.3\ CR2/
$ ls -lah
total 424K
drwx------ 6 forensicitguy forensicitguy 4.0K Oct 2 2017 .
drwxrwxr-x 3 forensicitguy forensicitguy 4.0K Jan 31 21:08 ..
drwx------ 2 forensicitguy forensicitguy 4.0K Sep 22 2017 .background
-rw-rw-r-- 1 forensicitguy forensicitguy 11K Sep 22 2017 .DS_Store
-rw-rw-r-- 1 forensicitguy forensicitguy 1.6K Sep 22 2017 Help.txt
drwx------ 2 forensicitguy forensicitguy 4.0K Oct 2 2017 '[HFS+ Private Data]'
drwx------ 2 forensicitguy forensicitguy 4.0K Oct 2 2017 '.HFS+ Private Directory Data'$'\r'
drwx------ 2 forensicitguy forensicitguy 4.0K Sep 22 2017 'Manual install'
-rw-rw-r-- 1 forensicitguy forensicitguy 1.6K Sep 22 2017 'Open Gatekeeper friendly'
-rw-rw-r-- 1 forensicitguy forensicitguy 377K Sep 22 2017 .VolumeIcon.icns
Sometimes DMGs get maddening and you can’t unpack them easily with 7z. In these cases I recommend dmg2img, which this blog post talks about. When looking at DMGs, keep in mind they can contain any form of content needed for an application, including PKG installer files, scripts, folders, and more.
Good hunting!