Hiding from the Investigator: Understanding OS X and iOS Code Signing to Hide Data
Hiding from the Investigator:
Understanding OS X and iOS Code Signing to Hide Data
Abstract
To hide data from a the forensic practitioner you need to exploit either a gap in their knowledge, their processes, and/or their tools. This is a talk about all three in regards to Apple OS X and iOS code signing. Much research has been conducted around code signing with respect to preventing malicious code execution at binary load time. This strictly about forensics, binary tampering, and data smuggling.
Content
Imagine you are an incident responder for a large organization that is predominantly OS X users that have Admin rights and you allow all connections to the internet with no filtering. When something deemed as an incident happens, you use OSXCollector from Yelp to collect data from the workstation in question and you just reformat the drive depending on the suspected activity. You look at OSXCollector data and you give any program a pass that comes back as signed by Apple or a reputable developer. After all, OS X signing is solid. Right?
Well, it depends on how you do your analysis, but OS X signing may not work the way you think it does.
Mach-O Binaries
On Windows with the PE file format, if you issue the following command to a signed windows binary, it will no longer be signed:
On OS X and iOS, if you do the same it will still be signed:
Try for yourself, on OS X versions prior to El Capitan, do the following:
Go ahead and execute the binaries. You will notice that they both execute. But why? If I issue the following command, codesign will note that they are no longer signed:
By default, Mach-O binaries that are part of an application bundle (like Firefox.app) need to either have a valid signature or not be signed at all to execute. Stand alone binaries like /bin/ls do not need to have a valid signature, they will execute either way, by default. So why did the Firefox.app still execute while not having a valid signature? Because the operating system does not use strict signature checking for binary execution. The codesign equivalent is made using the the --no-strict flag.
What's the difference between strict and no-strict checks?
Strict checking involves the entire Mach-O on disk and other resources in the application bundle, if an application. No strict checking involves from the Mach header to the end of the last section that is mapped into memory via the Load Commands, usually that is the Code Signature section and as of 10.10.4, there could be no sections outside of the Code Signature section. Since appended data is not mapped into memory via a load command, it goes unchecked. Meaning you can hide data and the OS will not complain by default.
This also works on iOS. Yes, iOS applications are installed with only no-strict checking.
I reported this issue and a resource issue to Apple in April 2015 and was assigned CVE-2015-3714 https://support.apple.com/en-us/HT204942.
The most significant change from 10.10.2 to 10.10.4 (the security update patch for Mach-O loading) was the following section of code:
This check, from within the load_segment() function in bsd/kern/mach_loader.c, verifies if a section is outside of the code signature section. It is a great check as one could have modified code outside of the signature on a fully signed application. As it stands now, apple verifies code that is loaded into memory, so appending code to the end of the binary will not result in direct code execution. And that is a great thing! Now onto data hiding.
Universal Binaries
Universal binaries (Fat binaries) are Apple's way of deploying a single binary everywhere so it can execute anywhere on multiple chip architectures.
Notice the padding between Mach-O binaries. Here is the code from bsd/kern/mach_loader.c, that parses the fat file for Mach-O binaries.
This code means the data between Mach-O binaries are not checked. Mach-O binaries are mapped in memory and checked independently, for the architecture that is being executed. Again there is no direct code execution from patching data into these sections. And the sections can vary in size, but average about 0x1000 bytes. What happens if we insert data in the unprotected sections? From an execution standpoint, nothing. After adding a single byte in one unprotected section and using codesign we get an interesting error:
Apple was not expecting this. Using OSXCollector, no error is returned on this.
Code Execution
The data hiding aspects are now obvious. How can this be abused to get code execution to perhaps add extra functionality to your application? For iOS, you would need to find a way to create RWX memory to execute foreign code in your application. That and bypass the App Store checks, like Charlie Miller.
On OS X, creating RWX memory is still possible. There are two scenarios and both involve post exploitation / installation. The first via post exploitation, an in memory infection scans Mach-O binaries for a magic number in an egg hunter way - once it finds its egg, executes the following payload. The second, is an App Store application that checks itself on disk for an egg and executes the payload afterwards. A user or an attacker could extend the functionality of the application in this manner. Both POCS are available here.
Solutions
After talk with Apple Security Engineers in November 2015 about the appending data issue. They feel that users should use only strict checks to determine if binaries are actually signed and have not been tampered with. As such, I do not see strict checking by default being pushed info the kernel (beyond gatekeeper) to enforce strict checking at mach-o load time - anytime soon.
Introducing Kyphosis
Kyphosis is the medical term for hunchback, because the original thought was you could have a 64KB Mach-O binary with 4GB+ file appended on it. The goal of kyphosis is to find data that is appended to a Mach-O file or hidden between Mach-O files in a Universal binary - whether the binary is signed or not. It's a python class and it can be used standalone to pull data from a binary. It uses Macholib and a Universal binary parser from my side project BDF.
To help the OS X forensic/incident response personnel, I've submitted a pull request (link) to OSXcollector, that will collect this extra data, put it into a dict, base64 encode it, and add it to the dict that OSXcollector uses for data storage. I've tested this on my own OS X instances, but I don't run a lot of applications. However, I did find one application that kept code outside of Mach-O Load Commands. It's from IDA PRO:
And it's a Tcl script:
This application is not signed (all of IDA PRO) and I did not dive into what this script does in particular. Maybe I will at a later date.
In addition, patches were submitted and accepted to Knockknock and OSQuery to do strict checking by default.
Questions and feedback: https://twitter.com/midnite_runr
Happy Hunting!
Metadata
Tags: forensics, anti-forensics, Apple, OS X, iOS, codesigning
Primary Author Name: Josh Pitts
Primary Author Affiliation: N/A
Primary Author Email: the.midnite.runr@gmail.com
Last updated