Android app security —where do attacks come from?
Often I see penetration testing reports which have a very generalised description of the risk. Things like ‘insufficient data encryption’ are usually unhelpful without the context of how that might be exploited. For example, if my application stores data on the SD card but encrypted, the avenue for stealing that data is different from if it is stored in my application’s private area but unencrypted. Neither is necessarily more serious than the other, but the way you mitigate against both threats will differ considerably.
This article is aimed at giving an overview of the different ways your application might be exploited if you did or did not address a particular security issue.
One of the most common mistakes that have historically been present in application security is to use HTTP for your web requests, or HTTPS but with allow all certificates enabled — sometimes this was added for development so you can use self signed certs, other times for debugging. Whatever the reason, it’s interesting to note the difference in challenge of exploiting one over the other.
Traffic sent unencrypted over http may expose to an attacker personal data, credentials, security tokens etc. The way of exploiting this requires an attacker to view the details of your web requests. This is really easy in a lab environment which is one reason why these issues are easily caught, because you can set up a proxy and point your phone at it. Voila, I can see all your web traffic. It does not follow that an attacker would have such an easy time of this. If I’m sitting at home on my network typing this post now and sending my keystrokes over a non-https connection — you can’t see it. There are ways that someone can snoop on your traffic, there’s the often touted risk associated with being on a public WiFi, there’s a complex web of networking behind a private WiFi network which could be exploited or intercepted in other ways, there are a few different ways of redirecting my phone to your network sniffer which I’ll cover shortly but it’s not quite as easy as the you might think. Another thing here is there are more ways of passively reading unencrypted http traffic than of reading incorrectly configured Https traffic.On the subject of poorly configured Https traffic, we can follow the security onion down to its next level. If an app is send SSL traffic but accepting all certificates, the passive interception I’ve talked about won’t work. Instead, exploiting your vulnerability will first involve tricking a phone or at least your app into talking to an attacker owned proxy and modifying the response to provide an attacker controlled certificate. This is again, somewhat harder to achieve by forcing the attacker to exploit some other hole in order to intercept and modify your traffic.
The default position for most apps is the next layer of the onion. Here we would have a straight call over Https to a server with a certificate signed by a trusted root certificate authority. At this level our only real risks are from an otherwise trusted certificate authority being compromised or from a user installing an attackers certificate on their device. Both avenues are tough and the risk of this type of attack bring carried out is fairly low.
The final step we can take to protect data in transit is to pin our certificate. When we do this, without the attacker comprising your specific certificates, they will not be able to read or modify SSL traffic (with the caveat that another vulnerability like heartbleed would cause similar issues)
When your app is installed on a device, it is broadly vulnerable to the types of attacker.
- Non privileged malicious software
- Lost or stolen devices
- Privileged malicious software
Which I’ve deliberately listed in order of likelihood, although I suppose you could dispute the first two.
Non privileged malicious software
Broadly speaking this is an app like any other on your phone that is able to see the same non private information as your app, and request the same sorts of permissions. These permissions are varied and so I’ll take a few of the key ones and show what kind of attack can happen as a result.
The first and easiest piece of malicious software to write is one that can scan your SD card and other non-protected storage and steal it. For your apps point of view, don’t store things on the sdcard and if you absolutely must, then store them encrypted. The next step is to check your inputs — this malicious app could call any receivers, services, start any activities, call any broadcast receivers, query content providers etc… that you have left exposed. Finally with this type of 'normal' app check for anything else that can be read or modified. Does your app rely on an SMS code that might be intercepted by this app? Do you read a file from the sdcard which might have been tampered with? Do you load Jar files from the sdcard? In short, your first check should be on these easily developed and easily installed applications and in summary your exposure to them is by looking at what they could feasibly do if they were running.
The sophistication of these apps, and the challenge with them being installed are things which need extra permissions enabled by the user post installation. Think about your screen recording apps, your custom keyboards, launcher screens that read notifications etc. Each of these types of attacking apps have a lower risk of being installed and enabled, but their impact on you as an app developer is greater. Imagine you’re writing a banking app and you allow a user to input their card details. What are the threat if the user has a custom keyboard that’s actually malware. What about if the screen is being recorded? If you have an app posting two factor authentication codes to notifications what are the risks if the user has malware capable of reading notifications on the phone? These can be tricky problems to solve, not allowing screen recording in app is fairly straightforward, but blocking custom keyboards is somewhat more of a time consuming work around.
It’s fair to say that the most likely and complicated fixes to security vulnerabilities found in apps are to mitigate against this threat, but some things are only an issue further down our list of likeliness. For example, encrypted databases inside private app directories aren’t necessary for the kinds of threats I’ve mentioned here.
Stolen and lost devices
It’s not as sexy as protecting your apps against malware but it’s quite likely at some point for a phone to get stolen. It’s less likely that someone will use that stolen phone to try and specifically compromise your app, but it’s still one to watch out for. At this point the user may have had a pin, or may not — or picked a guessable pin — but if we assume that an attacker has full access to the phone, we are exposed to a number of risks. First, is there a pin or lock specifically for your app? If not, all of a user details might be exposed to an end user. For apps like Facebook, this might be a tolerable level of risk, for a bank perhaps not.
Whether or not you have a PIN to protect your app, there are still a number of other potential vulnerabilities. One that gets raised by lint is that this type of attacker might back up your phone to their machine, exposing everything in the private application directory. On a similar note, an attacker with explicit access to a phone has more options to attempt to root the phone and extract the application directory. It is at this point that encrypting your application data becomes valuable. Naturally, a static key is less useful than a user entered password, but if requesting a user PIN or password is not conducive to a good user experience of your application, a compromise is to generate a key at runtime and store it in the Android keystore. Whilst the attacker theoretically has access to the keystore as they have the phone, actually extracting that key from the keystore is a huge challenge when compared with extracting a hardcoded or re-creatable key.
Another risk specifically for this attacker is that you are logging sensitive credentials to logcat. Say, you’re logging a JWT to logcat, an attacker here might be able to get that information. Leaving your app as debuggable is a fairly unlikely thing to happen, but always possible and again a user with access to the phone could exploit this avenue if you managed to release your app with debug enabled.
High privileged malware
The most import thing to mention here is that, ultimately, this attack is only ever a case of slowing down an attacker that is operating at a high — or highest — privilege level. Whilst heavily publicised when malware like this is found, it is an incredibly difficult to create and exceptionally rare. An application with ‘root’ permissions, either pre-installed by a sinister mobile carrier or exploited using an unpatched or undisclosed security vulnerability or one un-knowingly installed by a user who has previously rooted their own phone for their personal use, can theoretically read the screen without your ability to detect it to capture personal information and can hook your application in order to intercept keystrokes or generated encryption keys rendering even encrypted databases with a user entered password largely redundant.
Your ability as an application developer to combat these threats is extremely limited, and the best option — though not infallible — is to use root detection, in particular google’s safetynet. Some other minor mitigations might be to use character arrays to hold sensitive strings in memory and clearing them as soon as they are used to limit the ability of malware to attack, and using anomaly detection on and off the device to monitor for potentially compromised phone. My advice on this latter attack type is not to worry too much about it unless you really need to.
Hopefully you will by now have a broad understanding when you look at your next penetration testing report of why certain things are flagged up, and just as important the ability to challenge the amount of effort required to mitigate against certain, unlikely events.