Binary Protections
Static analysis is the examination of an executable in its non-running state. For iOS devices, most of this can be performed on the device itself, but it may be faster to copy the binary from the device and on to a macOS host to perform the analysis. These examples are from a macOS host analyzing an iOS executable called bufferoverflow1
.
Use otool
, to analyze the binary and determine if these features are enabled:
Stack Protection
To check the binary for stack canary protections:
otool -Iv filename | grep __stack_chk
% otool -Iv bufferoverflow1 | grep __stack_chk
0x0000000100004084 2 ___stack_chk_fail
0x0000000100008000 2 ___stack_chk_fail
0x0000000100008008 3 ___stack_chk_guard
In this case, the binary has stack canary support compiled into it. If this protection had been disabled, it would simply return with no output. Xcode will automatically add stack protection during compilation, but the developer may turn it off. So, it is still good to check it.
Position Independent Executable (PIE)
To ensure that the binary has PIE enabled:
otool -hv filename
% otool -hv bufferoverflow1
bufferoverflow1:
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
MH_MAGIC_64 ARM64 ALL 0x00 EXECUTE 17 1064 NOUNDEFS DYLDLINK PIE
Under the flags section, we can see that PIE is set (the output has wrapped in this example).
Note: As the name suggests, PIE
is only applicable to executable files. Dynamic libraries do not need to enable PIE/ASLR. Dynamic libraries (dylib’s) always load at a specific offset from the main application binary. So, as long as the main binary has PIE enabled, the DyLibs will inherit it automatically.
Automatic Reference Counting (ARC)
When an application needs memory space on the Heap, it typically issues a malloc call to the kernel for allocation. Unlike the Stack, Heap space is supposed to be cleaned up (freed) by the developer once the need for the space is complete. This was the case for Objective-C programming where the developer had to keep track each retain
to ensure that a release
was accompanying it. ARC seemed like a logical step since the compiler would already alert if there were more retains than releases, so now it just adds the release code when it finds it. From a security perspective, ARC reduces the risk of heap overflows by ensuring that memory is managed consistently.
For iOS, both Objective-C and Swift have implemented ARC protections.
To check for the autorelease:
otool -Iv filename | grep -iw _obj_autorelease
% otool -Iv bufferoverflow1 | grep -iw _objc_autorelease
0x000000010054184c 29398 _objc_autorelease
0x0000000100699680 29398 _objc_autorelease
Note: this is not necessarily a finding, as the developer could properly manage memory allocations manually. However, if ARC is not enabled, it may be worth testing for heap overflows.