iOS memory management basics

iOS has several approaches to protecting applications from memory corruption vulnerabilities. These include Automatic Reference Counting (ARC), Position Indenpendent Executable (PIE) support and Address Space Layout Randomization (ASLR). Read on to find out more about how these work in iOS 6.

PIE Support

Position Independent Executable support is part of the Address Space Layout Randomization (ASLR) implementation. PIE is enabled or disabled in XCode under the compiler code generation build settings.

If an application is compiled with PIE enabled then the application loads the executable into a non-fixed virtual address space each time. If PIE is disabled, then a fixed address is used each time. We can check for the presence of PIE by using otool. Here’s otool being used on a version of the Good for Enterprise iOS binary:

canderous:good-file steve$ otool -hv Good
Good:
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
MH_MAGIC ARM V6 0x00 EXECUTE 37 4608 NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK

As can be seen here, PIE is not enabled. If PIE were enabled, you’d see the text PIE in the flags section.

While PIE doesn’t remove vulnerabilities it does require significantly more effort for a vulnerability to be actually exploitable due to dynamic address space mapping. The effectiveness of this measure depends on the type of vulnerability involved and the way it’s being exploited.

ARC Support

Automatic Reference Counting (ARC) is a relatively recent (iOS 5) introduction that replaces the traditional C-style approach to memory management. Because ARC handles memory allocation and deallocation on behalf of the programmer, in theory ARC should reduce the likelihood of memory-allocation-related vulnerabilities from being introduced. In practice, ARC isn’t yet universally used as ARC requires iOS 5.0 or later.

We can use otool to look for ARC-related symbols that would indicate it’s use. Here I’m checking the iOS Good for Enterprise client for ARC support.

canderous:good-file steve$ otool -I -v Good | grep "_objc_release"
canderous:good-file steve$ otool -I -v Good | grep "_objc_autorelease"
canderous:good-file steve$ otool -I -v Good | grep "_objc_storeStrong"
canderous:good-file steve$ otool -I -v Good | grep "_objc_retain"

As we can see, ARC is not enabled in this version of Good for Enterprise. The absence of ARC is not in itself a sign of vulnerabilities, and the conventional functions used for memory management can be used in a safe and secure manner. However, from a black box attacker’s perspective, checking for the presence (or absence) of ARC across a large number of binaries is highly useful when looking for where to focus effort.

Incidentally the full suite of symbols to check for with ARC can be found in ObjCARC.h in the llvm source. The above should be sufficient for the detection of ARC as ARC binaries will inevitably at some point need to release objects, but for completeness, those looking to script checks on binaries might want to check for the following:

_objc_retain
_objc_release
_objc_autorelease
_objc_retainAutoreleasedReturnValue
_objc_retainBlock
_objc_autoreleaseReturnValue
_objc_autoreleasePoolPush
_objc_loadWeakRetained
_objc_loadWeak
_objc_destroyWeak
_objc_storeWeak
_objc_initWeak
_objc_moveWeak
_objc_copyWeak
_objc_retainedObject
_objc_unretainedObject
_objc_unretainedPointer

Memory management and dangerous functions

Prior to iOS 5.0 memory management was handled in essentially the same way as every C application. We can look for the presence of traditional memory management mechanisms using otool. Here I check an older version of Good for Enterprise for the presence of malloc.

canderous:good-file steve$ otool -I -v Good | grep malloc
0x008595ec 4011 _malloc
0x00a3b240 4011 _malloc
0x00a3e170 4011 _malloc

The use of malloc in an iOS application indicates that the application conducts it’s own memory management (as opposed to ARC). While not an inherent weakness, the memory management is going against Apple’s current recommendations and may result in the introduction of vulnerabilities or software bugs if not properly managed.

canderous:good-file steve$ otool -I -v Good | grep free
0x00858d40 3729 ___cxa_free_exception
0x00859334 3869 _free
0x00859340 3870 _freeaddrinfo
0x0085934c 3871 _freeifaddrs
0x00859970 4088 _regfree
0x00a3c26c 3869 _free
0x00a3de8c 3729 ___cxa_free_exception
0x00a3e088 3869 _free
0x00a3e08c 3870 _freeaddrinfo
0x00a3e090 3871 _freeifaddrs
0x00a3e29c 4088 _regfree

The use of free() also indicates an application does their own memory management. It’s important not to free the same object multiple times or to use a reference after the object or parent object has already been freed as this can lead to software bugs and memory corruption vulnerabilities.

canderous:good-file steve$ otool -I -v Good | grep strcpy
0x00859b5c 4129 _strcpy
0x00a3e340 4129 _strcpy

The use of strcpy is dangerous when combined with user supplied input and no prior bounds checking. It can be used safely, but needs special attention to check for issues such as off-by-one errors.

canderous:good-file steve$ otool -I -v Good | grep printf
0x00858f14 3771 ___snprintf_chk
0x00858f20 3772 ___sprintf_chk
0x00858fbc 3788 ___vsnprintf_chk
0x00858fc8 3789 ___vsprintf_chk
0x00859004 3800 _asprintf
0x00859304 3865 _fprintf
0x00859760 4044 _printf
0x00859ac0 4116 _snprintf
0x00859ae4 4119 _sprintf
0x00859c4c 4149 _swprintf
0x00859d0c 4166 _vasprintf
0x00859d18 4167 _vfprintf
0x00859d24 4168 _vprintf
0x00859d30 4169 _vsnprintf
0x00859d3c 4170 _vswprintf
0x00a3df28 3771 ___snprintf_chk
0x00a3df2c 3772 ___sprintf_chk
0x00a3df60 3788 ___vsnprintf_chk
0x00a3df64 3789 ___vsprintf_chk
0x00a3df78 3800 _asprintf
0x00a3e078 3865 _fprintf
0x00a3e1ec 4044 _printf
0x00a3e30c 4116 _snprintf
0x00a3e318 4119 _sprintf
0x00a3e390 4149 _swprintf
0x00a3e3d0 4166 _vasprintf
0x00a3e3d4 4167 _vfprintf
0x00a3e3d8 4168 _vprintf
0x00a3e3dc 4169 _vsnprintf
0x00a3e3e0 4170 _vswprintf

The printf family aren’t inherently bad but can be dangerous if incorrectly used.

Stack Smashing Protection

iOS contains support for stack canaries via the -fstack-protector-all compiler flag. Stack canaries are used to detect the presence of a stack buffer overflow being exploited before malicious code is executed. This works by placing random values in front of local variables. When a function returns the canary is checked. If the canary was overwritten (e.g. by an overflow) the value will have probably changed, the overflow is detected and the application can protect itself from executing arbitrary code.

canderous:good-file steve$ otool -I -v Good | grep stack
0x00859790 4048 _pthread_attr_setstacksize
0x00a3e1fc 4048 _pthread_attr_setstacksize

The symbols __stack_chk_fail and __stack_chk_guard would show up if this has been enabled. Here we can see that the version of the Good for Enterprise client being examined does not implement stack canary support.

Please note – I’m not targeting Good for Enterprise in these articles for any specific reason, it’s simply the binary that I had to hand at the time. It’s also not the current version at the time of writing and Good Inc. may have implemented these measures which at the time would’ve almost certainly broken compatibility with older versions of iOS. All of the material covered in this article will have been possible prior to accepting any EULAs or agreements.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s