In the last post, we’ve seen some security issues which exist in the Android password manager gbaSafe version 1.1.0a, by analyzing the security description provided in its web site. As described there, even though the system depends on a “master key” which might be secure, the security of the system is seriously compromised by the encouragement of very weak keys (a few digits only) in what is named an “unlock key”, used to encrypt the master key itself. All of that in an application which claims to strongly protect people’s data from unwanted eyes.
In this post, we will play a bit with the Linux-based Android OS to actually explore these security deficiencies, demonstrating that such issues are very real, and that the claims of being hard to unveil the data is unfounded. Since the most serious weakness lies in the key itself, we’ll run a simple brute force attack to try to find arbitrary unlock keys.
This procedure is actually mentioned by the author of gbaSafe himself in the web page, except he overestimates the work involved in producing such a mechanism:
Theoretically, somebody could write a program that tries to decrypt the master key by trying all possible values of the short key (with 4 digits there are only 10000 possibilities), but this would still be much work, as more information about the crypting algorithm is needed (e.g. salt bytes, iteration count).
So let’s get started.
As a first step, we’ll need the Android SDK with a working emulator (I’ve used API level 5, revision 1), and a copy of the application itself. I got a trial version of the application from AndAppStore.com.
The application downloaded is bundled within that .apk file, which is really a .zip file that may be opened up normally with any tool which understands this file format.
Once that’s done, we get access to all the information needed to run the application, including icons, interface layouts, and most importantly in this case, the bytecode which targets the Dalvik VM. This bytecode is the end result of a sequence of translations which happen when the program’s Java source code is compiled, so that’s what we’ll have to fiddle with to figure details of the application we want to investigate.
The bytecode is located inside the classes.dex file, and as expected it’s not easy to read in its native format. Luckily, though, a smart guy has already written a couple of tools, smali and baksmali, which allow people to decompile and recompile that bytecode format to/from something that is much easier to understand.
After downloading these tools, the following command should decompile the file:
$ java -jar baksmali.jar –output classes classes.dex
We now have a classes/ directory full of .smali files.
Before going any further, let’s ponder for a moment about what we want to do. A brute force attack is when we attempt sequentially many possible keys, and given the context already presented, what we’re looking after is to attempt different “unlock keys”. With that in mind, we’ll introduce a very small modification in the application so that it will attempt to enter the unlock key automatically, rather than reporting an error when the key entered in the unlock dialog is invalid.
With that in mind, after some quick research, it looks like the onClick() method within the DlgUnlock.smali file is a pretty good candidate. This is supposedly called when the button in the unlock dialog is clicked, so something interesting about the password being correct or not must happen there.
Before doing anything there, I’ve increased the number of registers in the function to 12, to get some additional registers to play with, and then initialized a register with the value zero, to serve as a monotonically increasing number (our keys!):
.method public onClick(Landroid/view/View;)V
– .registers 9
+ .registers 12
.parameter “view”
+ const/16 v9, 0x0
Then, we have to instruct the program to use these keys rather than whatever is typed in the dialog box. Some lines down, we’ll see a call to the checkUnlockKey() method, which is certainly what we’re looking after. Let’s do this now:
+ :mykey
+ invoke-static {v9}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
+ move-result-object v2
invoke-static {v2}, Lcom/gbizapps/safeA/Crypt;->checkUnlockKey(Ljava/lang/String;)I
Now, what if this key is wrong? We don’t want the master key to be removed as mentioned in the software description. We want to simply attempt the next key. With some analysis, we see that in case of errors, the next couple of lines below the above code will instruct the VM to jump to an error branch. Rather than following up with the normal error logic, we’ll increment the key, and jump back to the above code:
:cond_6c
+ add-int/lit8 v9, v9, 0x1
+ goto :mykey
Now we just have to rebundle this and put it into the emulator. I won’t go over it in too much detail here, since there’s plenty of information available online, but the steps to do that are:
- Recreate a modified classes.dex with smali
- Recreate a modified .apk file by just zipping the modified content
- Sign and zipalign the new .apk file
- Install it
And that’s it, seriously! This would be enough to break the software security if it was working correctly.
Interestingly, though, the software wasn’t working correctly with this change. Instead, it was Force Closing on certain keys. To test it out, use the master key “master key”, and the unlock key “999999”, and then once you close and open the application again, try to unlock it with the key “1175”. Instead of showing an error message, it will break badly.
Now, for the proof of concept to work, I actually had to fix the bug, which felt a bit funny to do given the context.
Looking at the traceback trough adb logcat, I found out that there was a null being dereferenced in the file Crypt.smali, so I fixed the problem by injecting some error checking at this position and jumping the flow into an existing error branch:
+ if-eqz v3, :cond_5a
const-string v4, “ucpmhkexov85MDKhdfdfFGQPYxywq7209fcrqhghjkuiopy”
With this in place came the biggest surprise of the experiment. The keys which were crashing the application were special, in the sense that they actually decode the master key successfully! That’s right: whatever the algorithm is doing, that six-digit “999999” encrypts the master key in such a way that attempting the “1175” key works, so even big keys are rendered extremely weak with the logic used to encrypt the master key.
At this point, I added some trivial logic to display the key found with a Toast, just to ensure the whole thing was working correctly:
Note that the key generation implemented above is a bit simplistic, in the sense that it doesn’t attempt keys with leading zeros, but this would be trivial to implement, and my intention here isn’t to actually break any keys for real, but just to show how the promised security in this application is not to be trusted at all. Just the logic above will already be enough for a brute force attack against the application, and has broken all the keys I’ve tried in mere seconds, in a slow emulator.
As a conclusion, if you want to put your data in a secure place, rather than picking an application which promises security because the salt is hidden somewhere or because it’s too much work to figure its logic, pick an open source application with logic which is publicly verifiable and has already had many eyes over it. Chances are that doing something like what was described in this post won’t be so trivial. Then, choose your keys wisely! The most secure application won’t be enough if you pick a bad key.
Thank you for your good work. It shows how easy it is to manipulate and change the Java apps of Android, so it is useless trying to implement a copy protection or registration solution. I’m looking for a good obfuscator, as it might make it more difficult for hackers.
I released version 1.2.0 of gbaSafe where the problem with the weak key is solved.
Nice job Gustavo.
I hope the author of this app will read some Bruce Schneier before making bold security claims the next time.
Thanks David. Unfortunately, given the comments right above, it looks like he didn’t read it yet.
Gunter, you seem to blame Android for a problem that actually lies in the application itself. No matter how hard it is to disassemble an application (and trust me, other platforms aren’t much harder than this), if the application security depends on not having the source code, the application is badly engineered. Using an obfuscator will just serve to prove that its security is not to be trusted.
Following David’s suggestion and reading some papers and articles by people like Bruce Schneier might be enlightening.
By the way I own and have read parts of the books “Practical cryptography” and “Applied Cryptography” by Bruce Schneier. Even with open source solutions you are forced to enter a long and cryptic master key if you want to defeat brute force attacks.I guess that 99.5% of the users would not do it, but use a weak master key that can be broken with brute force attacks. My solution with the “unlock key” was wrong, but right now I see no real solution to the problem, because the Java cryptography API implemented in the Android system helps brute force attacks by throwing an exception when trying to decode with a wrong master key. If it did not, but just gave back some randomly decoded bytes, I could develop a much more clever solution similar to one that was developed by a German research institute: http://www.mobilesitter.de/en/result.htm
I’d like to see your analysis of keepassdroid.
Really good job Gustavo. I am impressed by your patience with gbrors.
Gustavo, thanks for a couple of really interesting and enlightening posts. I have a question though: you say that the third step in breaking into this application was to “sign and zipalign the new .apk file”. How did you manage to convince the package manager to install your .apk? Assuming you don’t have access to the original author’s signing keys, the PM should refuse to install your modified application (or at least install it as a new one, in which case breaking into it would serve little purpose as there would be no secret data to find).
I mean, what the application author is trying to prevent is that if someone steals a phone with his application installed and protecting sensitive data, he can’t get access to the data. Even if he could do what you did and manage to have the PM install his hacked version of the app, the data wouldn’t be there to see. I guess he could copy the database from the original app’s storage to the hacked one’s, but that might (I’m unsure about this) only be possible if the phone is rooted, in which case at least some of the blame would have to fall on the phone owner?
Emil,
It’s trivial to get data out of the phone, even if the phone is not rooted by the owner. If it was hard to do this, one wouldn’t need to encrypt the data in the first place.