iOS App Security
iOS App Security
Tampering and Reverse Engineering
This article considers key areas in mobile app security and popular anti-reverse engineering protection techniques. There are several approaches to analyzing software:
- Data exchange analysis using a packet sniffer to analyze data exchanged over a network.
- Software binary code disassembly to get its listing in assembly language.
- Decompilation of binary or byte-code to recreate source code in a high-level programming language.
Let’s start with understanding few basic terms used through out the article.
Data tampering is the act of deliberately modifying (destroying, manipulating, or editing) data through unauthorized channels. Data exists in two states: in transit or at rest. In both instances, data could be intercepted and tampered with. Digital communications are all about data transmission.
For example, in the instances where data packets are transmitted unprotected, a hacker can intercept the data packet, modify its contents, and change its destination address. With data at rest, a system application can suffer a security breach and an unauthorized intruder could deploy malicious code that corrupts the data or underlying programming code. In both instances, the intrusion is malicious and the effects on the data always dire. It’s one of the biggest security threats that any application, program, or organization can face.
Reverse engineering a mobile app is the process of analyzing the compiled app to extract information about its source code. The goal of reverse engineering is comprehending the code.
Key Areas in Mobile Application Security
- Local Data Storage
The protection of sensitive data, such as user credentials and private information, is crucial to mobile security. If an app uses operating system APIs such as local storage or inter-process communication (IPC) improperly, the app might expose sensitive data to other apps running on the same device. It may also unintentionally leak data to cloud storage, backups, or the keyboard cache. Additionally, mobile devices can be lost or stolen more easily compared to other types of devices, so it’s more likely an individual can gain physical access to the device, making it easier to retrieve the data.
2. Communication with Trusted Endpoints
Mobile devices regularly connect to a variety of networks, including public WiFi networks shared with other (potentially malicious) clients. This creates opportunities for a wide variety of network-based attacks ranging from simple to complicated and old to new. It’s crucial to maintain the confidentiality and integrity of information exchanged between the mobile app and remote service endpoints. As a basic requirement, mobile apps must set up a secure, encrypted channel for network communication using the TLS protocol with appropriate settings.
3. Code Quality and Exploit Mitigation
Traditional injection and memory management issues aren’t often seen in mobile apps due to the smaller attack surface. Mobile apps mostly interact with the trusted backend service and the UI, so even if many buffer overflow vulnerabilities exist in the app, those vulnerabilities usually don’t open up any useful attack vectors.
This protection from injection and memory management issues doesn’t mean that app developers can get away with writing sloppy code. Following security best practices results in hardened (secure) release builds that are resilient against tampering. Free security features offered by compilers and mobile SDKs help increase security and mitigate attacks.
4. Anti-Tampering and Anti-Reversing
There are three things you should never bring up in polite conversations: religion, politics, and code obfuscation. Many security experts dismiss client-side protections outright. However, software protection controls are widely used in the mobile app world, so security testers need ways to deal with these protections. We believe there’s a benefit to client-side protections if they are employed with a clear purpose and realistic expectations in mind and aren’t used to replace security controls.
iOS Application Attack surface
The iOS application attack surface consists of all components of the application, including the supportive material necessary to release the app and to support its functioning. The iOS application may be vulnerable to attack if it does not:
- Validate all input by means of IPC communication or URL schemes, see also:
2. Validate all input by the user in input fields.
3. Validate the content loaded inside a WebView, see also:
- Testing iOS WebViews
- Determining Whether Native Methods Are Exposed Through WebViews
4. Securely communicate with backend servers or is susceptible to man-in-the-middle (MITM) attacks between the server and the mobile application, see also:
- Testing Network Communication
- iOS Network APIs
5. Securely stores all local data, or loads untrusted data from storage, see also:
- Data Storage on iOS
6. Protect itself against compromised environments, repackaging or other local attacks, see also:
- iOS Anti-Reversing Defenses
Testing Network Communication
Practically every network-connected mobile app uses the Hypertext Transfer Protocol (HTTP) or HTTP over Transport Layer Security (TLS), HTTPS, to send and receive data to and from remote endpoints. Consequently, network-based attacks (such as packet sniffing and man-in-the-middle-attacks) are a problem.
Intercepting HTTP(S) Traffic
In many cases, it is most practical to configure a system proxy on the mobile device, so that HTTP(S) traffic is redirected through an interception proxy running on your host machine. By monitoring the requests between the mobile app client and the backend, you can easily map the available server-side APIs and gain insight into the communication protocol. Additionally, you can replay and manipulate requests to test for server-side vulnerabilities.
Several free and commercial proxy tools are available. Here are some of the most popular:
Using a proxy breaks SSL certificate verification and the app will usually fail to initiate TLS connections. To work around this issue, you can install your proxy’s CA certificate on the device.
Preventing Man-in-the-Middle Attacks in iOS with SSL Pinning
Testing Local Data Storage
As little sensitive data as possible should be saved in permanent local storage. However, in most practical scenarios, at least some user data must be stored. Fortunately, iOS offers secure storage APIs, which allow developers to use the cryptographic hardware available on every iOS device. If these APIs are used correctly, sensitive data and files can be secured via hardware-backed 256-bit AES encryption.
The Keychain
The iOS Keychain can be used to securely store short, sensitive bits of data, such as encryption keys and session tokens. It is implemented as an SQLite database that can be accessed through the Keychain APIs only.
The Keychain API includes the following main operations:
-
SecItemAdd
-
SecItemUpdate
-
SecItemCopyMatching
-
SecItemDelete
Data stored in the Keychain is protected via a class structure
that is similar to the class structure used for file encryption.
Items added to the Keychain are encoded as a binary plist and
encrypted with a 128-bit AES per-item key in Galois/Counter Mode
(GCM). Note that larger blobs of data aren’t meant to be saved
directly in the Keychain-that’s what the Data Protection API is
for. You can configure data protection for Keychain items by
setting the
kSecAttrAccessible
key in the call to
SecItemAdd
or
SecItemUpdate
.
iOS Anti-Reversing Defenses
- Jailbreak Detection
- Anti-Debugging Checks
- File Integrity Checks
- Device Bindings
1. Jailbreak Detection
File-based Checks
Check for files and directories typically associated with jailbreaks, such as:
/Applications/Cydia.app
/Applications/FakeCarrier.app
/Applications/Icy.app
/Applications/IntelliScreen.app
/Applications/MxTube.app
/Applications/RockApp.app
/Applications/SBSettings.app
/Applications/WinterBoard.app
/Applications/blackra1n.app
/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist
/Library/MobileSubstrate/DynamicLibraries/Veency.plist
/Library/MobileSubstrate/MobileSubstrate.dylib
/System/Library/LaunchDaemons/com.ikey.bbot.plist
/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist
Checking File Permissions
Another way to check for jailbreaking mechanisms is to try to
write to a location that’s outside the application’s sandbox.
You can do this by having the application attempt to create a
file in, for example, the
/private directory
. If the file is created successfully, the device has been
jailbroken.
Checking Protocol Handlers
You can check protocol handlers by attempting to open a Cydia URL. The Cydia app store, which practically every jailbreaking tool installs by default, installs the cydia:// protocol handler.
Calling System APIs
Calling the
system
function
with a "NULL" argument on a non-jailbroken device will
return "0"; doing the same thing on a jailbroken
device will return "1". This difference is due to the
function's checking for access to
/bin/sh
on
jailbroken devices only.
Bypassing Jailbreak Detection
Once you start an application that has jailbreak detection enabled on a jailbroken device, you’ll notice one of the following things:
- The application closes immediately, without any notification.
At this point, bypassing jailbreak detection with Cycript is trivial.
Note:
# Cycript allows developers to explore and modify running applications on either iOS or Mac OS X using a hybrid of Objective-C++ and JavaScript syntax through an interactive console that features syntax highlighting and tab completion.
(It also runs standalone on Android and Linux and provides access to Java, but without injection.)
http://www.cycript.org/
2. A pop-up window indicates that the application won’t run on a jailbroken device.
At this point, Frida that we will use to bypass jailbreak detection is so-called early instrumentation, that is, we will replace function implementation at startup.
Note:
#Frida Inject JavaScript to explore native apps on Windows, Mac, Linux, iOS, Android, and QNX.
https://github.com/frida
2. Anti-Debugging Checks
Debugging and exploring applications are helpful during reversing. Using a debugger, a reverse engineer can not only track critical variables but also read and modify memory.
There are several anti-debugging techniques; a few of them are discussed below.
Using ptrace
iOS runs on an XNU kernel. The XNU kernel implements a
ptrace
system
call that's not as powerful as the Unix and Linux
implementations. The XNU kernel exposes another interface via
Mach IPC to enable debugging. The iOS implementation of
ptrace
serves
an important function: preventing the debugging of processes.
This feature is implemented as the PT_DENY_ATTACH option of the
ptrace
syscall.
Using PT_DENY_ATTACH is a fairly well-known anti-debugging
technique, so you may encounter it often during iOS pentests.
Using sysctl
Another approach to detecting a debugger that’s attached to the
calling process involves
sysctl
.
According to the Apple documentation:
The sysctl
function retrieves system information and allows processes with appropriate privileges to set system information.
sysctl
can also
be used to retrieve information about the current process (such
as whether the process is being debugged). The following example
implementation is discussed in ["How do I determine if
I'm being run under the debugger?"](https://developer.apple.com/library/content/qa/qa1361/_index.html
"How do I determine if I'm being run under the
debugger?")
3. File Integrity Checks
There are two topics related to file integrity:
- Application source code integrity checks: In the “Tampering and Reverse Engineering” chapter, we discussed the iOS IPA application signature check. We also saw that determined reverse engineers can easily bypass this check by re-packaging and re-signing an app using a developer or enterprise certificate. One way to make this harder is to add an internal run-time check that determines whether the signatures still match at run time.
-
File storage integrity checks:
When files are stored by the application, key-value pairs in
the Keychain,
UserDefaults
/NSUserDefaults
, a SQLite database, or a Realm database, their integrity should be protected.
When you generate an HMAC with CC:
-
Get the data as
NSMutableData
. - Get the data key (from the Keychain if possible).
- Calculate the hash value.
- Append the hash value to the actual data.
- Store the results of step 4.
// Allocate a buffer to hold the digest and perform the digest.
NSMutableData* actualData = [getData];
//get the key from the keychain
NSData* key = [getKey];
NSMutableData* digestBuffer = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, [actualData bytes], (CC_LONG)[key length], [actualData bytes], (CC_LONG)[actualData length], [digestBuffer mutableBytes]);
[actualData appendData: digestBuffer];
When verifying the HMAC with CC, follow these steps:
-
Extract the message and the hmacbytes as separate
NSData
. -
Repeat steps 1–3 of the procedure for generating an HMAC on
the
NSData
. - Compare the extracted HMAC bytes to the result of step 1.
NSData* hmac = [data subdataWithRange:NSMakeRange(data.length - CC_SHA256_DIGEST_LENGTH, CC_SHA256_DIGEST_LENGTH)];
NSData* actualData = [data subdataWithRange:NSMakeRange(0, (data.length - hmac.length))];
NSMutableData* digestBuffer = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, [actualData bytes], (CC_LONG)[key length], [actualData bytes], (CC_LONG)[actualData length], [digestBuffer mutableBytes]);
return [hmac isEqual: digestBuffer];
4. Device Binding
The purpose of device binding is to impede an attacker who tries to copy an app and its state from device A to device B and continue the execution of the app on device B. After device A has been determined trusted, it may have more privileges than device B. This situation shouldn’t change when an app is copied from device A to device B.
Since iOS 7.0, hardware identifiers (such as MAC addresses) are off-limits.
The ways to bind an application to a device are based on
identifierForVendor
, storing something in the Keychain, or using Google's
InstanceID for iOS.
For more: App Obfuscator for iOS Apps
References: