iOS App Security and Analysis: Part 1/2
Learn how to do a basic analysis of iOS app security and maintain the security of your users’ data with code and network tools. By Derek Selander.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
iOS App Security and Analysis: Part 1/2
30 mins
Property Lists Are Vulnerable
Now that you have the skeleton of the application, it’s time to look for low-hanging fruit. These are mistakes that developers make that attackers can easily use to their advantage.
One n00b mistake that developers can make is putting critical information in a plist file. Attackers often inspect property lists as part of application mapping because they’re easy to view and give potential insight into what technologies the application uses. You will do the same.
Provided you are still in the main application bundle, use ls
to view the directory’s contents:
> ls *.plist
Info.plist MoneyDataStore.plist
There are two plists included in the main app bundle. One is Info.plist. Run the following command to print out its contents:
> plutil -p Info.plist
{
"DTSDKName" => "iphonesimulator6.1"
"CFBundleName" => "Meme Collector"
"CFBundleDevelopmentRegion" => "en"
"CFBundleVersion" => "1.0"
... more stuff here ...
Hmm…. nothing too interesting. Take a look at the other plist with this command:
> plutil -p MoneyDataStore.plist
{
"userCurrency" => 58
}
Groovy. This plist contains a key called userCurrency
with the same value as what you currently have in your app currency. Does this mean that the application holds the currency in the plist? There’s one way to find out!
First open the file with this command:
> open MoneyDataStore.plist
Change the value of userCurrency
to a bigger number like 1234. Be sure not to touch the key name or type or anything else. Save and close the plist file.
Back in the simulator, you’ll need to remove the app from memory. The default shortcut for the home button on the Simulator is ⌘+shift+h. It acts just like a normal iOS device: one tap for the home screen, two taps to bring up the list of apps while on the home screen.
From the home screen, hit ⌘+shift+h twice to open the app switcher and remove the Meme Collector app. You need to delete the app from memory because although you’ve changed the model, you haven’t updated the view. Removing the app from memory is a sure-fire way to make the application update itself.
Once you’ve done so, re-launch the app by tapping on its icon. Sure enough, the app now has 1234 of app currency available for you to use!
User Defaults: Not Secure!
Remember how silly of an idea it was to save sensitive information in a plist? Well, guess what NSUserDefaults
is? Yep, that’s a plist as well. The physical location for the NSUserDefault plist can be found at: {App Directory}/Library/Preferences/{Bundle Identifier}.plist.
This is often a place where developers mistakenly feel a sense of security. Indie developers are not the only ones to make this mistake; large corporations will occasionally fall into this trap as well.
There are countless applications that store sensitive data in NSUserDefaults
. Why don’t you see if there is anything interesting in Meme Collector’s version?
In Terminal, make sure you are in your app directory, then cd
into the Libary/Preferences directory. In there, open the plist that contains your Bundle Identifier. For me, it is called com.selander.Meme-Collector.plist
.
> cd Library/Preferences
> open com.selander.Meme-Collector.plist
Now as an exercise for you: using the same methods previously discussed, modify the NSUserDefaults
plist so that you get a whole bunch “Y U No Memes” for free.
If attackers can access plist files even while the iOS device is locked, where is a safe place to put information? One potential solution is to encrypt the data going into NSUserDefaults
. You would then need to do a sanity check whenever you load the data to see if that data is actually valid.
Keychain Access and Best Practices
Another potential solution is to migrate sensitive data away from plists and put it in the iOS Keychain. Check out this tutorial to learn how to use it.
Keychain Access ups the ante for the attacker. Attackers will be unable to access critical information while the device is locked. However, it’s important to not put too much trust solely in the keychain system.
Here is why: Keychain is an Apple-maintained database of important information. It is encrypted with the help of the user’s password, which generally is the simple 4-digit numeric passcode.
If an attacker were to try to get past this using the hacker’s version of brute force, they could potentially figure out the password in about 20 minutes. After figuring out the password, they can simply run a command line utility that can dump the keychain database.
What can you do about this? Here are Keychain best practices:
- Encrypt the data: Although Keychain Access is more secure, it is also a high-priority target. For jailbroken iOS devices there are command line utilities that print out the Keychain Access database’s contents. Make sure you make an attacker’s life a little harder by encrypting the data using Apple’s Common Crypto APIs found in the Security Framework.
- Do NOT hardcode your encryption key to the app: A long string found in the binary data section could potentially be interesting to an attacker. Not only that, if the encryption key is hardcoded, the attacker can post it online and have this attack apply to anyone using the app. You need to make a unique encryption key for the device.
- Be aware of your methods and how an attacker can use them: Your beautiful encryption/decryption method could be the best thing out there, but attackers can control the runtime and run your decryption method on your encrypted data. You’ll see this in part 2 of this tutorial.
- Question yourself: Do you need to store it?: Since the attacker can search, modify and execute portions of your binary you did not intend, you should ask yourself, do I really have to store this on the device?
Network Penetration Testing and Mapping
Attackers also like to see what’s occurring on the network side of an application. A quick and dirty way to see if any interaction could occur on the device is to check for any URLs in the application binary.
Make sure you are in your main bundle directory and in Terminal, type:
> strings Meme\ Collector
Whoa, too much data! The strings
command will go through a binary’s different sections and print out items that qualify as strings. Let’s filter the noise a bit. In Terminal, type:
> strings Meme\ Collector | grep http
http://version1.api.memegenerator.net/Generator_Select_ByUrlNameOrGeneratorID
It looks like there’s some point in the app where it talks to that meme generator URL. If you were an attacker, you would want to explore this further by looking at the app’s network traffic.
To do this, you’ll need a network monitor to intercept all the incoming and outgoing requests. Charles is a good option for this. If you haven’t already, download Charles here.
Once Charles is installed, launch it. Make sure that Charles is working on the Simulator by checking if it reads an active network. One easy way to do this is to navigate to the Maps Application – if you see a brigade of information start showing up in Charles, then you’re good to go.
If not, go to Charles/Proxy/Mac OS X Proxy and make sure that is enabled. If you see a bunch of other network communication you did not authorize, it is likely various programs on your Mac.
Note: Charles can also work as good tool for intercepting private information by enabling SSL queries. If you do not set this up, Charles will only be able to act as a proxy for HTTP requests – not HTTPS. As you saw using the strings
command, no HTTPS requests are being made, so you don’t need to set up the SSL proxy. However, this step is required for a lot of other apps since they use HTTPS.
Note: Charles can also work as good tool for intercepting private information by enabling SSL queries. If you do not set this up, Charles will only be able to act as a proxy for HTTP requests – not HTTPS. As you saw using the strings
command, no HTTPS requests are being made, so you don’t need to set up the SSL proxy. However, this step is required for a lot of other apps since they use HTTPS.
While Charles is activated, clear the Meme Collector app from memory and then restart it so you can see what network interactions are taking place. Upon running the app, you’ll see that the application makes three requests to http://version1.api.memegenerator.net
by expanding the arrow next to the URL.
Each of these requests to the Meme Generator subdomain contains a different parameter in the URL. For example, click on the first of the children requests of the hostname, and you’ll find that the single parameter is urlName=Foul-Bachelor-Frog. Examine the response to this request by clicking the Response tab in Charles.
The JSON is returning a bunch of key values including a title, description and image URL. The application seems to be requesting information from a third-party API and getting JSON back that is specific to a certain type of meme. After the initial JSON download, the app asks for the image’s URL returned in the JSON response and downloads the image.
Look at the next set of requests. The full URL path can be found in the Overview section.
Looking at the next hostname, http://cdn.memegenerator.net
, you can see that the app is downloading a series of images from the Meme Generator subdomain. Confirm this by looking at the response object and seeing what type of content is being returned. Sure enough, selecting the Response tab and viewing it in image format returns the memes that you saw in the application.
With this information, one can (somewhat accurately) hypothesize that these memes are downloaded from a third-party API, and that the app then does some logic to format the memes into purchasable content. Since there is no logic in the request or response to check if the memes were the original intended memes designed for the application, one can potentially purchase whatever content they want, provided they can manipulate the URL.
You are already bored of these memes, and you want to see if you can get the Success Kid meme instead of the standard default memes displayed by the application.
Select Tools/Rewrite in the menu. Rewrites intercept incoming or outgoing requests and can modify them based upon the settings you choose. In order to set it up, make sure you Enable Rewrite is checked, then click Add to add a set. You can leave the set name as-is. Click Add in the Rules section to add the rewrite rule.
Set the type to Modify Query Param. You want to rewrite the urlName
parameter, so fill in urlName as the name to both match and replace. Finally, set the replace value to success-kid. The final rule screen should look like this:
Click OK to return to the Rewrite Settings screen, and click Apply there to start rewriting.
Now hard-close and re-open the app. Success! There are Success Kids everywhere. You can now purchase a Success Kid meme.
It’s interesting, though, that there is still a valid cost for this new unspecified meme. How did this happen? The application must determine the cost based upon the JSON return value.
Open the Response tab in Charles and look at the JSON that is returned. Scan the app for potential candidates that could determine the cost in the app. Some promising JSON keys include: generatorID
, totalVotesScore
, instancesCount
, templatesCount
and ranking
. As an exercise for you, find the correct key which influences the cost of the meme.
To do this, go to Proxy/Breakpoints. Click on Enable Breakpoints and add a new breakpoint. When the edit breakpoint window appears, enter version1.api.memegenerator.net as the the Host and select the Response checkbox.
Now hard-close the app and rerun it. Once the responses return from the Meme Generator, the breakpoints should hit. When this happens, click on the Edit Response tab and the JSON Text sub-tab.
From here you can modify the JSON returned to the application. Play around with the return parameters and try to figure out which key modifies the cost of the item. After you make your changes to the JSON, click Execute to send the response. Remember, there are three API requests so you’ll need to click Execute three times.
Did you figure out which key in the JSON affects the price?
[spoiler title=”JSON Return Key That Modifies Price”]
The correct answer is ranking
. A higher ranking number will lower the price.
[/spoiler]
Important: Be quick! AFNetworking has a timeout of 30 seconds before calling the failure block. When intercepting the response, if you fail to make the changes in the appropriate time, the AFNetworking logic will return a request timeout error and execute the appropriate block – which in this particular project does nothing. If you run out of time, just hard close and re-open the app to try again.