June 23rd 2020
Passionate Software Engineer by day, Greedy crypto enthusiast by night
Android apps are mainly composed of a bunch of binaries bundled together, built from compiled Kotlin or Java code. The original source code can be easily reconstructed by several light-weight tools, found on the top of a google search page.
If you’re a happy Android developer and you’re not careful enough to apply the best security practices on your projects, you won’t stay happy for long. All the precious secrets you think are safely hidden in your code, can and will be used against you in the wild by someone with the right motivations. That’s precisely what happened to the Android app of this chain of barber shops.
The developers’ disregard of every kind of security best practices resulted in something that, in just a matter of a couple of hours, I was able to decompile, reverse-engineer and identify several vulnerabilities that anyone can exploit to achieve what would be every hacker’s life-pursuit dream: free haircuts for life!
Here, I’ll walk you through the whole hacking process, show you how important it is to follow the security best practices and how simple decisions can greatly increase the difficulty for reverse-engineering and exploitation of your Android app.
one for free
So far so good, they all look like pretty good tasks for an app to do. However, after installing the app and looking at my profile page, there was a very explicit and somehow intriguing message: the freebies could only be generated via app – not by any other platform – and they could also be used whenever the user wants: just need to present the app when it’s paying time.
This message definitely got my attention.
So I downloaded and decompiled the apk to satisfy my curiosity.
Maybe if I looked at the code, I could, hypothetically, use the app in some creative ways.
After decompiling the app and reverse-engineering the code, I identified several vulnerabilities. Later on, I’ll go one by one and discuss what the original developer could have done to prevent it:
- No code obfuscation, making it really easy to reverse-engineer
- No certificate or any kind of app integrity checks, making it impossible for the backend to spot tampered client apps
- Important business logic implemented on client app without further confirmation checks on backend side
- Hardcoded secrets on the code making backend’s private api exposed and easily exploitable
The APK file
files, which consist of
, which uses incompatible instructions, and therefore an additional
step is required, where
files are converted into a single
Here’s the compilation process:
Decompile the APK
and convert the
file into readable Kotlin or Java code. The resulting decompiled code is the entry point for understanding the functionality of the targeted app, but will likely not be 100% usable code. In other words, you can read the source, but you can’t really modify, recompile and build the original app with it.
to decompile the apk along with its
file contents anyway (steps 1 and 2 above)
$ apktool d target_app.apk
file is the format that the platform actually understands. However, it’s not easy to read or modify binary code, so there are some other tools out there to convert it to and from a human readable representation. The most common human readable format is an assembly language known as
. This is the format
outputs and stores on a
file looks like:
files are the ones we’ll be editing later, but not clearly the ones we’d want to be looking at for reverse engineering the whole application. Not unless we’re forced to.
(step 3 above):
$ dex2jar decompiled_app/build/apk/classes.dex
and booom! I have now access to the full app source code in a very familiar format, as the original developer wrote it.
Now, let us have a look at what I found
Vulnerability 1: No code obfuscation!
Obfuscation and minification are strategies used to reduce unused code and shorten the names of your app’s classes and members – usually by replacing them by something meaningless – not only making it very hard to read but also further reducing the size of your app.
If you don’t apply any kind of code obfuscation, you’re pretty much giving access to your private repo to the hacker.
, that would at least make the hackers life much harder, and forcing them to invest the much more significant effort to reverse engineer the code.
Vulnerability 2: Hardcoded secrets
, that would later on be used to authenticate each Http call to the backend.
And suddenly, I have now access to the entire app’s private api to mess around and look for new attacking vectors.
If you really need to store secrets on your client applications to provide some kind of authentication, this can be avoided in many ways without compromising these secrets by hardcoding them on your code.
There are several well-known methods like using symmetric key derivation strategies or password-based key derivation functions.
Vulnerability 3: Important business logic on client app without confirmation checks on backend
This is where things get interesting… Continuing my quest to get the best haircut – the one I don’t have to pay for – I still had to find some vulnerability that would get the app to generate me a free haircut booking.
After spending roughly one hour looking at the code, this is what I’ve learned:
- The backend stores all the
- The backend stores the
for each user
- The client app retrieves all the
for each user via
call to backend is identified by a hardcoded
And now, the cherry on top of the cake:
- The client app determines – not the backend – when is time to grant a
, by counting how many successful bookings a
was just a standard
REST call with an additional
stating the booking is for a
to the booking api with that
set to true:
After looking at so many mistakes here, I confess I wasn’t surprised.
to call such important decisions like: when it’s time to grant a free booking, you’re literally placing your precious business rules on the most vulnerable end of the whole user-journey.
Even worse, is to not validate the REST calls on the backend when you still have the chance. It would be obviously mandatory to validate if a user has made and payed for enough bookings to get a free one, before accepting and storing on the backend anything incoming from the client’s app.
And this leads me to the last but not least vulnerability:
Vulnerability 4: No client app integrity checks
If you’re going to code important business logic decisions on the client app – which is totally wrong by the way – the least you can do is to have some mechanism in place to verify the app’s authenticity.
A lot of the vulnerabilities above could have been mitigated with some kind of client app integrity check, making it possible for the backend server to verify when the api calls are coming from legit applications and discard the ones incoming from compromised apps. This can be easily achieved by signing the requests with the app’s release certificate.
Each Android apk is signed with a release certificate. If the apk is decompiled and recompiled again with code changes, by some process similar to what I describe here, the new recompiled apk must be signed with a different certificate, as the hacker won’t have access to the original one used to sign the legit app that only the original developer would have.
Change the Smali assembly files and Recompile the APK
file per each
so it would be easy, at least, to navigate and identify where the changes should be made. However, from now on, you’re on your own: if you’re not familiar with some kind of assembly language, this can be a quite painful process.
and removing a couple of
proved to be enough.
again to recompile everything:
$ apktool b -f decompiled_app -o hacked_app.apk
Generate a random key pair to sign the recompiled app like this:
$ keytool -genkey -v -keystore some_ks.keystore -alias some_alias -keyalg RSA -keysize 2048 -validity 1000
$ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore some_ks.keystore hacked_app.apk some_alias
And you’re good to go!
This is it. This is the rejoice moment. I’ve done what no hacker has done before. I could sit back and install my tampered app, call the barbers shop private api with my tampered requests, and finally, I could book free haircuts!
And it was worth it! (hypothetically)
This was an attempt to show how easy it is to steal your secrets and how generally vulnerable your code is if you don’t take the necessary precautions to preserve both your intellectual property and your business logic. Several exploiting tools and general knowledge on how to use them properly are widely available online, meaning it’s not just a matter of luck if your app gets hacked, no, It’s just a matter of time.
So you better start investing some time learning the best practices; otherwise, you’re just leaving the door open for someone to really take advantage of your code and potentially cause significant losses to your business. Not just displaying it for didactic purposes like I did here.