Finding private API keys for iPhone apps - iphone

I've been conducting quite a few MITM attacks as of late on my iPhone with Charles. Many of the apps that I've been yearning to get a better understanding of are easy to reverse engineer simply using cURL to mimic the requests that are being sent to the server. Others however, require a signature that I'm guessing uses a secret key that is used to generate a hash of the payload. When I reverse engineered Instagram's API a while back, it went something like this
function GenerateSignature($data) {
return hash_hmac('sha256', $data, 'b4a23f5e39b5929e0666ac5de94c89d1618a2916');
}
$data = '{"device_id":"'.$device_id.'","guid":"'.$guid.'","password":"'.$pass.'","username":"'.$username.'","email":"'.$email.'","_csrftoken":"c3a1648209b1eeefd7bd8d404211be5e", "Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"}';
$sig = GenerateSignature($data);
$new_data = 'signed_body='.$sig.'.'.urlencode($data).'&ig_sig_key_version=4';
And then, obviously the query string would be sent along with the request to the server. But, I can't seem to find the API key using Charles for an app that I'm really interested in. My iPhone is jailbroken, but I'm not quite sure where to look inside of the var/mobile/applications folder to find the API key? Any ideas?

You can use https://code.google.com/p/droidbox/ to find the key. Or use a Java disassembly tool.
There is also a webservice where you can upload an APK and scan it for malware. The report includes ADB logcat output with the key, for example see this scan-report (scroll to the bottom "Output generated by ADB logcat").

Related

Determining if a request came from an iPhone app

I have an iPhone app that communicates with a server (both of which I own and wrote the code for). I need to way to determine if a request on my server came from an iPhone (or any mobile device running the app I wrote for that matter). Basically, I only want to allow apps that I wrote to communicate with the server and need a way to verify that. Since I'm writing the apps, I can modify the headers and what not any way I need to.
I read up a little on Public Key Encryption, but I don't think it'd work. If I sent some sort of secret hashed word in my headers to verify it, couldn't some outside party simply get those headers and use the same ones in their request?
You can use a checksum. Let's say that you have something like:
date
subject
and calculate the checksum by using, let's say MD5 of (date + "string" + subject),
and you calculate the MD5 in the very same way on the server. If they match, they are from the mobile client.
This will work until someone figures out your algorithm.
You could have your server send a message to your app that contains a random code. This message and code changes every time it is sent.
Your app then does some kind of algorithm on that message to "encrypt" it and send it back to the server and the server can then check it. That way there's nothing to intercept and use without knowing your "encryption".
Of course, they could intercept the message from each direction and then work out your algorithm, but if you make it sufficiently difficult, then that would be a while.
You could just use HTTP Basic authentication, unless you needed something more secure.

How to send data to webserver and know it comes from a certain application?

I'm trying to send data from my application to a webserver.
The catch is that I want to encrypt the sent data and also make sure it comes from my application. (From what I've read so far it's easy to decrypt the data if it's not sent via HTTPS. On the unique application I didn't find anything except the Application ID Apple provides for each application but I don't know how that works).
The data sent are various highscores and achievements that the user can enable. The thing is that the user doesn't have to type in anything but his username and having that associated with the UDID it should be enough.
But how to make sure it's from my application?
Edit:After reading some responses around here I still didn't understand something:
If someone extracts the IPA and reverse engineers it wouldn't he have access to all my .h and .m so he can look up anything in there?
You could generate a client certificate for the iPhone app, and use mutual authentication for your SSL handshake. Then you know that data submitted to the server come from a particular user of the app, and that your app is communicating with the correct server.
Regarding your edited update, yes if someone reverse-engineeres your app they can probably work out how the high-scores are protected. But seriously, how much trouble do you expect most people to go to in order to fake a high score in a game? Are you offering a cash prize or something?
If you are using HTTPS then you can just put an identifier key in your POST to the web server, either as a field or perhaps an HTTP header that identifies your app. The entire connection is encrypted so it will be protected. If you do this you will want to encrypt the key even in your binary and decrypt it as you send it over the connection, that way no one will be able to pull it out with a hex editor.
Another approach if all you want is to know that the data comes from an authentic instance of your application is to use message authentication codes (MAC). I leave deciding how strongly the key needs protecting as an exercise to the reader - you know your security requirements better than I do.
You can use CommonCrypto - part of the iOS SDK since forever - to actually generate the MAC.

Is it worth while to hide/obfuscate server connections in an iPhone app? If so how?

If I have an app that connects to Amazon's S3 service, is it worth my time to hide/obfuscate the connection strings and API keys? I'm guessing that most hackers won't care all that much, but it would be financially painful if someone found this information and was able to upload data to my account!
For instance, if I store a username/password (or Twitter/Facebook API key and secret), these may be easily found using "strings". A hacker could see the functionality, grab the secrets and use them for nefarious purposes. I've seen people suggest using a simple Rot13, or storing the strings backwards or something like that in the app binary. Are these useful?
Has anyone done this or have any ideas/patterns/code to share?
-dan
You can hide your secrets in a webserver you have full control over, and then having this server relay the query to Amazon. You can then use whatever encryption/validation method you like, since you are not relying on what is supported by Amazon.
Once you have validated that the request is from your own application, you then rewrite the query including your secrets and then forward this to Amazon. The result from Amazon could then be relayed directly back to the application.
In php this could for instance be done using something similar to this snippet (not showing your url rewrite):
$fp = fopen($amazon_url,'r',false);
fpassthru($fp);
fclose($fp);
You dont really need to hide them...what you should do is have an extra key such as a secret, that one IS hidden and is only present in the signature of the call (which can be an MD5 hash or sha (or whatever)) without that secret key people wont be able to just make calls since the signatures created by the server and the offender wont match since they dont know the secret key used...
I'm guessing that most hackers won't
care all that much
It just takes one who's bored enough.
Has anyone done this or have any
ideas/patterns/code to share?
This is what SSL is for. You can encrypt all your transmissions or just the login process (which would return a session id that can be used for subsequent requests during the session).

How to ensure access to my web service from my code only?

I am writing a very simple web service for my iPhone app. Let's say this is a http page that returns a random number at http://mysite/getRand. How do I ensure that this page can only be accessed from my iPhone app and not from other clients? I've thought of doing some simple password mechanism but that can easily be sniffed by capturing what my app sends out.
The reason for this is to lower the load of my server by only allowing legitimate requests.
You can't really do this. Your application can be disassembled and whatever secret is in the binary can be replicated in a malicious application.
Another attack you should be aware of is people settings the hosts file to a location they control and then installing a root certificate that allows them to provide a signature for that domain. Your application would do the post with the secret, and they'd just be able to read out the secret. They could extract the password from any complicated encryption system within the binary in this way.
Most of the ideas in this thread are vulnerable to this attack.
That said, the likelihood of somebody caring enough to disassemble your application is probably fairly remote.
I'd just keep it simple. Have a password that's hardcoded in to your application. To prevent someone just looking at the resources and trying every string, make it the XOR of two strings or the result of an AES decrypt of a particular fixed string.
Obviously, you should do the request over SSL otherwise an attacker can just sniff the traffic.
Yes, a determined attacker will circumvent the scheme but like any DRM scheme, that's always been the case. The trick is to make it too much effort to be worth it.
To follow up on Simon's idea, you could very easily have a key string in your application, then send the device ID, and then the DeviceID XOR'ed (or some other simple algorithm for string encryption) with your key string.
Since you know the key value to use, it's trivial for you to "decrypt" this string on the sever side and verify that the values match.
This way, the password is different for each user's device, and the "key" string is never sent over the wires of the great unwashed internets. :-)
Yes, this would by no means be impossible to figure out, but like others have said, the idea is not to make it impossible. The idea is to make it more trouble than it is worth.
I would use the https protocol with client-side keys too. You can use one client key for everyone or you can even generate a different key for each client and "register" them at your server.
I suppose that it's a lot of work for small project, but it sounds like the appropriate thing to do if you need authentication.
You should check that keys aren't seen easily by mobile phone owner. And remember that somebody will be able to hack it in any case.
Here's one thought - send up the device ID along with requests from your app.
Monitor the device ID's used - if you see a ton of requests from different IP's near or at the same time, that device is probably being used as a fixed key in the requests sent to you - block it.
For those that actually send the real device ID from other apps (not yours), you can monitor usage trends to see if the calls match the pattern of how your app performs - like one call being used by a device before some initialization call you would normally expect, and so on - block those too.
Basically by being able to shift rules around patterns of use, you can better adjust to someone trying to use your service by making sure it's not a fixed target like some random use key would be.
You may also want to use a simple use key as well as a first line of defense, and then layer on the traffic analysis approach. Also custom http header values you look for are another simple way to trip up a naive attacker.
I am assuming you don't want to use SSL? If you do then you can open HTTPS session and then pass some secret key in the request.
If you don't want SSL your options are limited: to have pseudo security I suggest both authentication and authorization methods and a third to reduce overall traffic:
Authentication: Generator in client application that creates secret keys by combining with a key file. The keyfile can be updated every so often for greater security: lets say you update the key file once a week. To re-cap: Generator combines in app secret with out of app key file to generate a 3rd key for transmission used in authentication. The server would then be able to authenticate.
Authorization: Of course you also want to lock out rogue applications. Here it would be best to have authorization mechanism with the site. Don't replace keyfiles for unless the client logs in. Track key files to users. etc.
Traffic reduction:
If you are receiving obscene amount of traffic or if you suspect someone trying to DOS your server, you can also have both the server and clients sync to request/response on a procedurally generated URL that can change often. It is wasteful to open/close so many HTTPS sessions if someone is just flooding you with requests.
I'm not sure what web technology you are using, but if you are using Ruby on Rails, it uses a secret authentication token in all of its controllers to make sure malicious code isn't accessing destructive methods (via PUSH, POST, or DELETE). You would need to send that authentication token to the server in your request body to allow it to execute. That should achieve what I think you are looking for.
If you're not using Ruby on Rails, that method of code authentication might be a good one to research and implement yourself in whatever technology you are using.
Take a look at the Rails Security Guide, specifically section 3.1 (CSRF Countermeasures).
You could do something like encrypting the current time and IP address from the iPhone, and then decrypt it on the server. The downside is that you need the iPhone app to know the "secret" key so that only it can generate valid access tokens... and once the key is in the wild, it will only be a matter of time before it's hacked if your app is really worth the effort.
You could encrypt the response using some random portion of the application which is meant to be using it, specifying the location of the binary in an unencrypted bit of the response. Then at least only clients with access to your binary would be able to decrypt it... but again, that's hardly 100% secure.
Ultimately you need to ask yourself how much effort you want to put into securing the service vs how much effort you think hackers will put into abusing it.
I'm not an Cocoa Touch developer, but I think HTTP Authentication over SSL would be easy to implement and it's probably exactly what you're looking for.
All you need to do is setup HTTP Authentication on the server side (you haven't mentioned what you're using on the server side) and create a self-signed SSL cert on your webserver. Done. :)
Tell us more about your setup and we will be able to help you further.
As some of the answers have stated, closing your web service off to everyone else will be a major hassle. The best you can hope for is to make it easier for the hackers to use another web service, than to use yours...
Another suggestion to do this, is to generate random numbers from a random seed on both the server and the client. The server would need to track where in the sequence of random numbers all of the clients are, and match that number to the one sent by the client.
The client would also have to register to be given access to the server. This would also serve as a authentication mechanism.
So:
//Client code:
$sequence = file_get_contents('sequence.txt');
$seed = file_get_contents('seed.txt');
$sequence++;
//Generate the $sequence-th random number
srand($seed);
for ($i = 0; $i <= $sequence; $i++) {
$num = rand();
}
//custom fetch function
get_info($main_url . '?num=' . $num . '&id' = $my_id);
This will generate a request similiar to this:
http://webservice.com/get_info.php?num=3489347&id=3
//Server Code: (I'm used to PHP)
//Get the ID and the random number
$id = (int)$_REQUEST['id'];
$rand = (int)$_REQUEST['num'];
$stmt = $db->prepare('SELECT `sequence`, `seed` FROM `client_list` WHERE `id` = :id');
if ($stmt->execute(array(':id' => $id)) {
list($sequence, $seed) = $stmt->fetch(PDO::FETCH_ASSOC);
}
$sequence++;
//Generate the $sequence-th random number
srand($seed);
for ($i = 0; $i <= $sequence; $i++) {
$num = rand();
}
if ($num == $rand) {
//Allow Access
} else {
//Deny Access
}
By using a a different seed for each client, you ensure that hackers can't predict a random number by tracking previous numbers used.

How would you keep secret data secret in an iPhone application?

Let's say I need to access a web service from an iPhone app. This web service requires clients to digitally sign HTTP requests in order to prove that the app "knows" a shared secret; a client key. The request signature is stored in a HTTP header and the request is simply sent over HTTP (not HTTPS).
This key must stay secret at all times yet needs to be used by the iPhone app.
So, how would you securely store this key given that you've always been told to never store anything sensitive on the client side?
The average user (99% of users) will happily just use the application. There will be somebody (an enemy?) who wants that secret client key so as to do the service or client key owner harm by way of impersonation. Such a person might jailbreak their phone, get access to the binary, run 'strings' or a hex editor and poke around. Thus, just storing the key in the source code is a terrible idea.
Another idea is storing the key in code not a string literal but in a NSMutableArray that's created from byte literals.
One can use the Keychain but since an iPhone app never has to supply a password to store things in the Keychain, I'm wary that someone with access to the app's sandbox can and will be able to simply look at or trivially decode items therein.
EDIT - so I read this about the Keychain: "In iPhone OS, an application always has access to its own keychain items and does not have access to any other application’s items. The system generates its own password for the keychain, and stores the key on the device in such a way that it is not accessible to any application."
So perhaps this is the best place to store the key.... If so, how do I ship with the key pre-entered into the app's keychain? Is that possible? Else, how could you add the key on first launch without the key being in the source code? Hmm..
EDIT - Filed bug report # 6584858 at http://bugreport.apple.com
Thanks.
The goal is, ultimately, restrict access of the web service to authorized users, right? Very easy if you control the web service (if you don't -- wrap it in a web service which you do control).
1) Create a public/private key pair. The private key goes on the web service server, which is put in a dungeon and guarded by a dragon. The public key goes on the phone. If someone is able to read the public key, this is not a problem.
2) Have each copy of the application generate a unique identifier. How you do this is up to you. For example, you could build it into the executable on download (is this possible for iPhone apps)? You could use the phone's GUID, assuming they have a way of calculating one. You could also redo this per session if you really wanted.
3) Use the public key to encrypt "My unique identifier is $FOO and I approved this message". Submit that with every request to the web service.
4) The web service decrypts each request, bouncing any which don't contain a valid identifier. You can do as much or as little work as you want here: keep a whitelist/blacklist, monitor usage on a per-identifier basis and investigate suspicious behavior, etc.
5) Since the unique identifier now never gets sent over the wire, the only way to compromise it is to have physical access to the phone. If they have physical access to the phone, you lose control of any data anywhere on the phone. Always. Can't be helped. That is why we built the system such that compromising one phone never compromises more than one account.
6) Build business processes to accommodate the need to a) remove access from a user who is abusing it and b) restore access to a user whose phone has been physically compromised (this is going to be very, very infrequent unless the user is the adversary).
The simple answer is that as things stand today it's just not possible to keep secrets on the iPhone. A jailbroken iPhone is just a general-purpose computer that fits in your hand. There's no trusted platform hardware that you can access. The user can spoof anything you can imagine using to uniquely identify a given device. The user can inject code into your process to do things like inspect the keychain. (Search for MobileSubstrate to see what I mean.) Sorry, you're screwed.
One ray of light in this situation is in app purchase receipts. If you sell an item in your app using in app purchase you get a receipt that's crypto signed and can be verified with Apple on demand. Even though you can't keep the receipt secret it can be traced (by Apple, not you) to a specific purchase, which might discourage pirates from sharing them. You can also throttle access to your server on a per-receipt basis to prevent your server resources from being drained by pirates.
UAObfuscatedString could be a solution to your problem. From the docs:
When you write code that has a string constant in it, this string is saved in the binary in clear text. A hacker could potentially discover exploits or change the string to affect your app's behavior. UAObfuscatedString only ever stores single characters in the binary, then combines them at runtime to produce your string. It is highly unlikely that these single letters will be discoverable in the binary as they will be interjected at random places in the compiled code. Thus, they appear to be randomized code to anyone trying to extract strings.
If you can bear to be iPhone OS 3.0-only, you may want to look at push notifications. I can't go into the specifics, but you can deliver a payload to Apple's servers along with the notification itself. When they accept the alert (or if your app is running), then some part of your code is called and the keychain item is stored. At this point, that is the only route to securely storing a secret on an iPhone that I can think of.
I had the same question and spent a lot of time poking around for an answer. The issue is a chicken and egg one: how to pre-poluate the keychain with data needed by your app.
In any case, I found a technique that at least will make it harder for a jailbreaker to uncover the information - they'll at least have to disassemble your code to find out what you did to mask the info:
String Obfuscation (if the link breaks search for "Obfuscate / Encrypt a String (NSString)")
Essentially the string is obfuscated before placed in the app, then you unobfuscate it using code.
Its better than doing nothing.
David
EDIT: I actually used this in an app. I put a base coding string into the info.plist, then did several operations on it in code - rot13, rotate/invert bytes, etc. The final processed string was used to decode the obfuscated string. Now, the three letter agencies could for sure break this - but at a huge cost of many hours decoding the binary.
I was going to say that this is the best technique I've come across, but I just read Kiran's post on UAObfuscatedString (different answer), which is a completely different way to obfuscate. It has the benefit of no strings saved anywhere in the app - each letter is turned into a method call. The selectors will show up as strings, so a hacker can quickly tell that your class used that technique though.
I think that this similar question, and my answer, may be relevant to your case too. In a nutshell, there was some talk of a trusted platform module being present in an iPhone. This would allow your service to trust an iPhone, even in the hands of an attacker. However, it looks like using the keychain is your best bet.
Did you consider/try the Push Notification suggestion, for initially transmitting the secret to the app & keychain? Or end up finding some other method to achieve this?
I'm going have my iphone app upload images to Amazon S3. Instead of putting the AWS credentials in the app, I am going to have the app phone home to my server for the URI and headers to use in the S3 upload request. My server will generate the S3 URI, proper signatures, etc. I can then implement a tighter, more specific security model on my app's webservice than AWS offers by itself and not give away my AWS keys to anyone with a jailbroken iphone.
But there still has to be some trust (credentials or otherwise) given to the app, and that trust can be stolen. All you can ever do is limit the damage done if someone jailbreaks an iphone and steals whatever credentials are in the app. The more powerful those credentials are, the worst things are. Ways to limit the power of credentials include:
avoid global credentials. make them per-user/application
avoid permanent credentials. make them temporary if possible
avoid global permissions. give them only the permissions they need. for instance, write permissions might be broken down into insert, overwrite, delete, write against resource group A or B, etc, and read could be broken into read named resources, read a list of all existing resources, read resource groups A or B, etc.
I would recommend creating a key at run time if possible. This way if the key were to get apprehended during a particular session, once the session ends, the key will be worthless. They could still apprehend the key from memory if they are smart enough, but it wouldn't matter since the key would become invalid after a period of time.
Sounds wonky. Would use HTTPS and maybe an encryption package to handle the key.
I think CommonCrypto is available for iPhone.
EDIT: Still sounds wonky. Why would anyone pass a secret key in an HTTP header? Anyone who traces your network traffic (via a logging wifi router, for instance) would see it.
There are well-established security methods for encrypting message traffic...why not use them rather than invent what is basically a trivially flawed system?
EDIT II: Ah, I see. I would go ahead and use the Keychain...I think it is intended for just these kinds of cases. I missed that you were generating the request using the key. Would still use HTTPS if I could though, since that way you don't risk people deducing your keygeneration scheme via inspection of enough signatures.