MOBILE

Mobile Application Security: Security Testing

1/13/2011 9:14:45 AM
The threat of classic C exploits is reduced, but not eliminated, by using high-level Objective-C APIs. This section discusses some best practices, such as using NSString rather than legacy string operations like strcat and strcpy to protect against buffer overflows. However, there are a few more subtle ways that things can go wrong.

Buffer Overflows

The buffer overflow is one of the oldest and most well-known exploitable bugs in C. Although the iPhone has some built-in preventative measures to prevent buffer overflow exploitation, exploits resulting in code execution are still possible (see http://www.blackhat.com/presentations/bh-europe-09/Miller_Iozzo/BlackHat-Europe-2009-Miller-Iozzo-OSX-IPhone-Payloads-whitepaper.pdf). At their most basic level, buffer overflows occur when data is written into a fixed-size memory space, overflowing into the memory around the destination buffer. This gives an attacker control over the contents of process memory, potentially allowing for the insertion of hostile code. Traditionally, C functions such as strcat() and strcpy() are the APIs most often abused in this fashion. Sadly, these functions are still sometimes used in iPhone applications today.

The simplest way for an Objective-C programmer to avoid buffer overflows is to avoid manual memory management entirely, and use Cocoa objects such as NSString for string manipulation. If C-style string manipulation is necessary, the strl family of functions should be used (see http://developer.apple.com/documentation/security/conceptual/SecureCodingGuide/Articles/BufferOverflows.html).

Integer Overflows

An “integer overflow” occurs when a computed value is larger than the storage space it’s assigned to. This often happens in expressions used to compute the allocation size for an array of objects because the expression is of the form object_size × object_count. Listing 1 shows an example of how to overflow an integer.

Listing 1. How to Overflow an Integer
int * x = malloc(sizeof (*x) * n);
for (i = 0; i < n; i++)
x[i] = 0;

If n is larger than 1 billion (when sizeof(int) is 4), the computed value of

sizeof (*x) * n

will be larger than 4 billion and will result in a smaller value than intended. This means the allocation size will be unexpectedly small. When the buffer is later accessed, some reads and writes will be performed past the end of the allocated length, even though they are within the expected limits of the array.

It is possible to detect these integer overflows either as they occur or before they are allowed to occur by examining the result of the multiplication or by examining the arguments. Listing 2 shows an example of how to detect an integer overflow.

Listing 2. Detecting an Integer Overflow
void *array_alloc(size_t count, size_t size) {
if (0 == count || MAX_UINT / count > size)
return (0);

return malloc(count * size);
}

It’s worth noting at this point that NSInteger will behave exactly the same way: It’s not even actually an object, but simply an Objective-C way to say “int.”

Format String Attacks

Format string vulnerabilities are caused when the programmer fails to specify how user-supplied input should be formatted, thus allowing an attacker to specify their own format string. Apple’s NSString class does not have support for the “%n” format string, which allows for writing to the stack of the running program. However, there is still the threat of allowing an attacker to read from process memory or crash the program.


Listing 3 shows an example of passing user-supplied input to NSLog without using a proper format string.

Listing 3. No Format Specifier Used
int main(int argc, char *argv[]) {

NSString * test = @"%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x";
NSLog(test);
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);

[pool release];
return retVal;
}

Running this results in the following:

[Session started at 2009-03-14 22:09:06 -0700.]
2009-03-14 22:09:08.874 DemoApp[2094:20b]
000070408fe0154b10000bffff00cbfffef842a4e1bfffef8cbfffef94bffff00c0

Whoops! Our user-supplied string resulted in memory contents being printed out in hexadecimal. Because we’re just logging this to the console, it isn’t too big a deal. However, in an application where this output would be exposed to a third party, we’d be in trouble. If we change our NSLog to format the user-supplied input as an Objective-C object (using the “%” format specifier), we can avoid this situation, as shown in Listing 4.

Listing 4. Proper Use of Format Strings
int main(int argc, char *argv[]) {

NSString * test = @"%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x";
NSLog(@"%@", test);
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}

NSLog makes for a good demo but isn’t going to be used that often in a real iPhone app (given that there’s no console to log to). Common NSString methods to watch out for are stringByAppendingFormat, initWithFormat, stringWithFormat, and so on.

One thing to remember is that even when you’re using a method that emits NSString objects, you still must specify a format string. As an example, say we have a utility class that just takes an NSString and appends some user-supplied data:

+ (NSString*) formatStuff:(NSString*)myString
{
myString = [myString stringByAppendingString:userSuppliedString];
return myString
}

When calling this method, we use code like the following:

NSString myStuff = @"Here is my stuff.";
myStuff = [myStuff stringByAppendingFormat:[UtilityClass
formatStuff:unformattedStuff.text]];

Even though we’re both passing in an NSString and receiving one in return, stringByAppendingFormat will still parse any format string characters contained within that NSString. The correct way to call this code would be as follows:

NSString myStuff = @"Here is my stuff.";
myStuff = [myStuff stringByAppendingFormat:@"%@", [UtilityClass
formatStuff:unformattedStuff.text]];

When regular C primitives are used, format strings become an even more critical issue because the use of the “%n” format can allow for code execution. If you can, stick with NSString. Either way, remember that you, the programmer, must explicitly define a format string.

Double-Frees

C and C++ applications can suffer from double-free bugs, where a segment of memory is already freed from use and an attempt is made to deallocate it again. Typically, this occurs by an extra use of the free() function, after a previously freed memory segment has been overwritten with attacker-supplied data. This results in attacker control of process execution. In its most benign form, this can simply result in a crash. Here’s an example:

if (i >= 0) {
...
free(mystuff);
}
...
free(mystuff);

In Objective-C, we can run into a similar situation where an object allocated with an alloc is freed via the release method when it already has a retain count of 0. An example follows:

id myRedCar = [[[NSString alloc] init] autorelease];
...
[myRedCar release];

Here, because myRedCar is autoreleased, it will be released after its first reference. Hence, the explicit release has nothing to release. This is a fairly common problem, especially when methods are used that return autoreleased objects. Just follow the usual development advice: If you create an object with an alloc, release it. And, of course, only release once. As an extra precaution, you may wish to set your object to nil after releasing it so that you can explicitly no longer send messages to it.

Note

See http://weblog.bignerdranch.com/?p=2 for more information on debugging retain counts. The majority of information here is applicable to both OS X and the iPhone.


Static Analysis

Most commercial static analysis tools haven’t matured to detect Objective-C-specific flaws, but simple free tools such as Flawfinder (http://dwheeler.com/flawfinder/) can be used to find C API abuses, such as the use of strcpy and statically sized buffers. Apple has documentation on implementing “static analysis,” but it seems to have misunderstood this to mean simply turning on compiler warnings (see http://developer.apple.com/TOOLS/xcode/staticanalysis.html).

A more promising application is the Clang Static Analyzer tool, available at http://clang.llvm.org/StaticAnalysis.html. Like any static analysis tool, the clang analyzer has its share of false positives, but it can be quite useful for pointing out uninitialized values, memory leaks, and other flaws. To use this tool on iPhone projects, you’ll need to do the following:

  1. Open your project in Xcode. Go to Project | Edit Project Settings.

  2. Go to Build. Change “Configuration” to “Debug.”

  3. Change the Base SDK to “Simulator” of the appropriate OS version. “Valid Architectures” should change to “i386.”

  4. Go to Configurations and change the default for command-line builds to be “Debug.”

  5. In a terminal, cd to the project’s directory and run scan-build–view xcodebuild.

  6. When the build completes, a browser window will be opened to a local web server to view results.

Consult the clang documentation for more details on interpreting the results. Of course, you should not assume that the use of a static analysis tool will find all or even most of the security or reliability flaws in an application; therefore, you should consider developing fuzzers for your program’s various inputs.

The role of a fuzzer is to expose faults through violating program assumptions. For instance, given an application that parses an HTTP response and populates a buffer with its contents, over-long data or format strings can cause the program to fail in a potentially exploitable fashion. Similarly, integers retrieved via external sources could fail to account for negative numbers, thus leading to unexpected effects. You can craft fuzzers in the form of fake servers, fake clients, or programs that generate many test cases to be parsed by an application. For more information on fuzzers, see http://en.wikipedia.org/wiki/Fuzz_testing.

Other  
  •  Mobile Application Security: The Apple iPhone - Development
  •  Building Android Apps : Installing KiloGap in the Emulator
  •  Building Android Apps : Build KiloGap
  •  Building Android Apps: Create an Android Virtual Device
  •  Building Android Apps: Going Native - Setting Up the Environment
  •  Building Android Apps: Introduction to PhoneGap
  •  iPhone Application Development : How Xcode and Interface Builder Implement MVC
  •  iPhone Application Development : Understanding the Model-View-Controller Paradigm
  •  Building Android Apps: Going Offline - Debugging
  •  Building Android Apps: Creating a Dynamic Manifest File
  •  Building Android Apps: Online Whitelist and Fallback Options
  •  The Basics of the Offline Application Cache
  •  Building Android Apps: Web SQL Database (part 4) - Deleting Rows
  •  Building Android Apps: Web SQL Database (part 3) - Selecting Rows and Handling Result Sets
  •  Building Android Apps: Web SQL Database (part 2) - Inserting Rows
  •  Building Android Apps: Web SQL Database (part 1) - Creating a Database
  •  Building Android Apps: Web Storage
  •  Building Android Apps : Simple Bells and Whistles
  •  Building Android Apps : Traffic Cop
  •  iPhone Application Development : Exploring Interface Builder - Connecting to Code
  •  
    Top 10
    Building Out Of Browser Silverlight Applications - Controlling the Application Window
    Exchange Server 2010 : Maintaining Reliability and Availability - Recover Data
    jQuery 1.3 : Table Manipulation - Sorting and paging (part 1) : Server-side sorting & JavaScript sorting
    jQuery 1.3 : Developing plugins - Adding new global functions
    SQL Server 2008 : Programming Objects - Implementing Stored Procedures
    Windows Azure : Messaging with the queue - Decoupling your system with messaging
    Tools to Manage Access Control Lists
    Mobile Application Security : The Apple iPhone - Local Data Storage: Files, Permissions, and Encryption
    Design and Deploy High Availability for Exchange 2007 : Create Bookmark Create Note or Tag Implement Standby Continuous Replication (SCR)
    Deploying the Client for Microsoft Exchange Server 2010 : Pushing Outlook Client Software with Group Policies
    Most View
    ASP.NET 4 in VB 2010 : The Security Controls
    iPhone 3D Programming : Adding Textures to ModelViewer (part 4) - Enabling Textures with ES2::RenderingEngine
    Outlining AD DS Changes in Windows Server 2008 R2 (part 3) - Auditing Changes Made to AD Objects
    SharePoint 2010 : SQL Server Database Mirroring for SharePoint Farms
    Building LOB Applications : Data Validation through Data Annotation
    Navigating Architecture Changes in Windows Vista
    iPhone 3D Programming : Adding Depth and Realism - Shaders Demystified
    jQuery 1.3 : Modifying table appearance (part 2) - Tooltips
    Design and Deploy High Availability for Exchange 2007 : Design Edge Transport and Unified Messaging High Availability
    Personalizing Windows 7 (part 3) - Choosing and Configuring Your Screensaver
    SQL Server 2008 : Service Broker - Message Types
    Designing and Implementing Mobility in Exchange Server 2010 : Securing Access to ActiveSync Using Internet Security and Acceleration (ISA) Server 2006
    SQL Server 2008 : What Is the Base Class Library?
    Creating and Managing Views in SQL Server 2008 : Creating Views
    Examining Integration Points Between SharePoint and Public Key Infrastructure
    jQuery 1.3 : Modifying table appearance (part 3) - Collapsing and expanding sections
    Advanced ASP.NET : Creating a Component
    Installing SharePoint 2010 Using PowerShell
    iPhone 3D Programming : Holodeck Sample (part 4) - Replacing Buttons with Orientation Sensors
    iPhone 3D Programming : Reflections with Cube Maps