Before you go, check out these stories!

0
Hackernoon logoI Hacked An Android App To Get A Free Haircut by@pedro.costa

I Hacked An Android App To Get A Free Haircut

Author profile picture

@pedro.costaPedro Costa

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.

The app of this chain of barber shops has a very simple purpose: Users can book haircuts on the available shops and per each 10 haircuts, the user gets

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.

And I was right: what I found was a complete mess.

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:

  1. No code obfuscation, making it really easy to reverse-engineer
  2. No certificate or any kind of app integrity checks, making it impossible for the backend to spot tampered client apps
  3. Important business logic implemented on client app without further confirmation checks on backend side
  4. Hardcoded secrets on the code making backend's private api exposed and easily exploitable

The APK file

Kotlin and Java sources are first compiled to 

.class
 files, which consist of
bytecode
instructions. Traditionally, these bytecode instructions would be executed directly on the JVM. However, Android apps are executed on the 
Android Runtime
, which uses incompatible instructions, and therefore an additional
Dexing
step is required, where 
.class
 files are converted into a single 
.dex
 file.

Here's the compilation process:


Decompile the APK

There are several tools online that decompile

apks
and convert the
.dex
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.

The apk

decompilation
steps:

However, as I was really motivated on snooping into the code, I used

apktool
to decompile the apk along with its
.dex
file contents anyway (steps 1 and 2 above)

$ apktool d target_app.apk

I as stated before, the

.dex
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
Smali
. This is the format
apktool
outputs and stores on a
/smali
folder.

This is what a

smali
file looks like:

Smali
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.

To look at more friendly code that could help me on the reverse engineering process, I converted the these smali files into standard

.class
files using
dex2jar
(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.

In this particular case, It was straightforward to understand and follow the code, identify third party technologies, but also the secrets used to authenticate them.

This could be all easily avoided by using some kind of obfuscation tool, like

ProGuard
, 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. 

This is what the code looks like before and after applying obfuscation [2]:

Vulnerability 2: Hardcoded secrets

The first class I looked at, had literally an hardcoded variable called

CLIENT_SECRET
, 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.

A very good and detailed view on these methods can be found here.

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
    Shops
    and
    User
    info
  • The backend stores the
    Bookings
    for each user
  • The client app retrieves all the
    Bookings
    for each user via
    REST
    calls
  • Each
    REST
    call to backend is identified by a hardcoded
    client_secret
    and user-specific
    basic_auth
    creds

And now, the cherry on top of the cake:

  • The client app determines - not the backend - when is time to grant a
    freebie
    , by counting how many successful bookings a
    User
    has made
  • The
    freebie
    was just a standard
    booking
    REST call with an additional
    flag
    stating the booking is for a
    free haircut

At this point, you can guess what was the backend response when I promptly issued a

POST
to the booking api with that
flag
set to true:

> 200 OK!  

After looking at so many mistakes here, I confess I wasn't surprised.

What we can learn from this is that by letting your

client app
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.

Alternatively, Google itself provides App Licensing services to help you validate if the current app was installed from the Play Store or from some other unknown source (the hacker).

Change the Smali assembly files and Recompile the APK

So, now that we're happy with our hacking and we know what and where we should place our malicious code, it's time to edit some assembly files and recompile the whole thing. Remember

smali
?

apktool
outputs one
.smali
file per each
.class
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.

I'm glad I didn't have to change too much for my tampered booking requests to work. Just adding couple of

flags
and removing a couple of
:gotos
proved to be enough.

When your done editing, just use the

apktool
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

Sign the recompiled app, I used

jarsigner

$ 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)

In Conclusion

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.

Tags

Become a Hackolyte

Level up your reading game by joining Hacker Noon now!