Home > Mobile Pentesting > Android > Android Static Analysis

Android Static Analysis
Android Mobile Pentesting

Static Analysis


It’s about examining the code without executing the program

We are going to see the important files within the android application and how can we benefit from them

Android Manifest.xml


It contains the basic info about the application

  • minSDKVersion : which will give us info about the version and then we can detect vulnerability depending on just the version
  • package name : It’s a unique identifier for the application on the device
    As an attacker we address the application through this identifier in our tools and scripts, so it’s important information for us

  • Permissions
    What data & HW components the app needs access to: (camera, BT, internet, etc..)
    You can see list of permissions here
    Permissions other than uses-permission are more interesting for us. These permissions are defined by the application and if you perform specific task and want to share it with other application then they can request this permission to exchange the data between each other.

  • Queries
    It may contain application we want to exchange data with and to do that we need to be visible to it
<queries>
    <package android:name="com.mwr.example.sieve"/>
</queries>

In this example we became visible to sieve application so we can exchange data with it.
We can use that when we write our attacking app to bypass login screen as example.

  • application
    We can see many entries like:
    • allow backup="true" : This means that the data can be backed up (may be senstive or not)
    • debuggable="true" : we are allowed to debug this process of the application
    • extractNativeLibs="false" : If we have native libs included they can’t be compressed (not important for us), we will need it to be false when we inject frida
    • neworkSecurityConfig=".." : Important in dealing with ssl pinning and if it’s not included we can define it (we will talk more about it later)

In the appliction we have many components (what the app has in the background)

  • Activities
    UI element that represents screens in the application (some of them need to be protected)
    protection is done using intent-filters which says (before you go to this screen you need to go to through this screen based on factors like cookies)
    exported="True" means that this activity is exposed and can be accessed from outside the app

  • Intent-Filter
    They are filters listening to specific intents and we will discuss them later

  • service
    It gots executed in the background as it may be resource intensive and not allowed to be done on UI

  • broadcast receiver
    It receive info to the application like receiving sms or any data from other application
    It’s an important attack surface

  • Content Providers
    serve data from ur app to other apps
    most of the time it’s related to database
    exported content provider can be very dangerous and expose data to any user or app
    the xml tag is provider

You may find API keys defined
At the top of the manifest you can see backup option

We will discuss these parts in more details

permissions


Each app in android system is sandboxed which means that app1 can’t access the data of app2
This is implemented by making use of the linux core of android by creating user for each application, so each app can’t access the others because it willnot have the privileges by default.

  • /etc/permission/platform.xml : this file defines the users’ IDs which are individual for each app

But what if app wants to access another app or utility like photos, etc….

  • this is defined in /etc/system/packages.xml

There are users who should be active before the system starts (hardcoded in kernel) like adb shell.

  • this is defined in android_filesystem_config.h

Now we need to know how can we ask for permission ???
we knew that we can see that defined permissions in AndroidManifest.xml file
and these permission will be added to /etc/system/packages.xml

What about custom permissions ??
It makes the apps exchange data between each others

  • This permission is defined not used directly
    as example
    ```xml
This permission allows reading keys table in sieve application
When we go to content provider we will understand this more
and this defined permission can be used in another app like this
```xml
<uses-permission ns0:name="com.mwr.example.sieve.READ_KEYS"/>

permissions

protection of data is depending on choosing the suitable protection level
We have levels:

  • dangerous : The user is informed about this and the user decides to allow or deny (depending on user decision isn’t the best case)
  • normal : You won’t be informed and this isn’t considered a protection for senstive data, but it’s important for things like internet which doesn’t need to be decision before being used
  • signature : Each apps signed with the same key can access the data (to access database as example which is protected by signtature level then we need the private key of the sieve to access it) This isn’t practicle approach as private key can’t be shared.

activity

basically they are the screens of the application.
as example we may have login activity, profile activity, settings activity, code activity
There are activities like profile as example who need us to login first before accessing it
We see that the only visible activity or the frontline of our app is login page as example, so what if we want to reach the code activity directly by scanning QR code from QR scanner app directly?
The QR scanner must be able to see the code activity and this moves us to exported: which means that the activity is reachable by outside world (dangerous and handled by intents which will be discussed later)

We can know if the activity is exported or not from AndroidManifest.xml file and exported can be implicit and explicit:

  • explicit : occures when the activity has the attribute exported="true"
  • implicit : occurs if the activity has intent-filter so it’s also reachable from outside world
    Exported scheme doesn’t only affect activities
    activities

we can make access exported activity using malicious app or adb shell
after getting adb shell we use the command am which is activity manager but we need to identify the activity to be opened
the activity is identified to be <package name>/<activity name(keep the dot)> so the command becomes am start-activity -n <package name>/<activity name(keep the dot)>

intents

It’s a messaging object and it can be implict and explicit

  • implicit : We don’t know the exact destination application like we know it’s email app but we don’t know which app exactly
  • explicit : We know the destination we want to call and the destination can be in the same app or in a different app
    It doesn’t work with activities only but also services and BroadcastReceivers
    intents

  • Explicit Intents
    Consider you have login activity and you want to login to reach profile activity, this can be implemented like this in login activity source code
    Intent myIntent = new Intent(this,ProfileActivity.class); // defining the intent by passing the source and the target
    this.startActivity(myIntent);
    

If we want to start the new activity with parameters

Intent myIntent = new Intent(this,ProfileActivity.class); // defining the intent by passing the source and the target
myIntent.putExtra("username","admin"); // passing username parameter with value = "admin"
this.startActivity(myIntent);

and the parameter is used in the profile activity like this

@Override
Protected void onCreate(Bundle savedInstanceState){
    Intent intent = getIntent();
    String user = intent.getStringExtra("username"); // get the passed parameter username
}
  • Implicit Intents
    Here we said that we don’t know the destination exactly as example we did an action that send mail and the exact mail app isn’t choosed.
    The system sends out this intent and we want to open mail app to do this action (gmail app may be opened as example) but we will know more details in BroadcastReceiver.
    If parameters are passed with the intent then there’s an attack vectorif the passed data are sensitive as we can create malicious app that listens to this type of intents (mail as example) and it will sniff the system for this information and this is called Intent - Sniffing

  • Intent-Filter
    As example the main activity will have an intent-filter with action:MAIN and category:LAUNCHER.
    It’s just listening to bus system and filtering out the info related to the app we need to react with and ignore the others
    It has certain tags : action, category and data
    we can know more about them and the possible values here

Note: if you want to interact with intent-filter which is considered implicit use this command am start-activity -a <action> -c <category> --es <key> <value>
–es is to pass key value pair data as string

If there’s many apps with the same action, category the user will choose which app to use

BroadcastReceiver

They are a kind of notification system for ur applications.

There’s a specific events like connecting the headphone, connecting to wifi, etc…. these events are sent as broadcast to all apps
Not all apps react to this, some of them ignore it and the other can react to it through onReceive method.

  • ordered priority
    It was an issue at android 4.3
    each app is assigned a specific priority (as example sms has the highest priority = 200)
    So it will be the first app to receive the msg and can decide to leave it to the next app with lower priority or not

The problem that in android <= 4.3 The attacker was able to assign it’s app a priority up to 999.
This is fixed in android 4.4 and the priority assignment is limited now.

To find which event each app is listening to, There are 2 approaches:

  • old approach (until Android version 8): in AndroidManifest.xml
  • new approach: Finding onReceive function within the Java code

In some broadcast events important data are passed so if we could receive them we may be able to move forward in the target.

Local broadcast manager can’t be exploited because we can’t interact with it, It’s just a messages bewtween classes as example.

adb.exe logcat: to show logging of the app
to open local console we use adb.exe local => this is console at which we can get the output of print function

  • Sending broadcast through adb: am broadcast -a <action in the intent filter or from the source code depending on android version>
    we may not have the permission, so make sure u r root

pm -U: List packages with uid so we can broadcast to specific uid and replcace a with 10, so the broadcast command can be am broadcast --user <uid> -a <action>

If there’s string info to be sent with the Intent filter we can use --es option as we know.

Services

Consider you have a game and it needs some processes like rendering the game and organization of objects (in UI) and we also need network requesting as example.
If all these components are in the main activity we will have a problem: The network request may stop the UI thread until the request is done which may result in closing the app (the app in android is closed if the UI thread isn’t working for some seconds).
The solution is to make the components which isn’t in the UI to be services in background

We have 2 types of services: start service & bound service

  • start : very simple just the service started when we need it to do some task then close it.
  • bound : binding client to service (we may have client interacting with the app) then we need to wait for the clients to disconnect to shutdown this service
    serviceTypes

The services can’t make UI updates as they work in the background
If we want to do so we may make use of BroadcastReceiver and make them trigger the service via onReceive()
This was the common behvior till android 8 cause this background execution isn’t allowed any more.

What to look for ?

  • start : here you will look into intent
  • bound : look into the message object
    to attack the service it needs to be Exported and Permissions to interact with this one.
    serviceAttack

ContentProvider

It’s simply provide a content and usually it’s used with databases, we can interact with them via content URI.
as example The Contacts they are stored in db and there’s content provider for this data base.
content URI is used to interact with them and in consists of:

  • prefix : which is content:// and it’s a good keyword to search for when reversing the app
  • authorty : It’s a unique identifier for the content provider
  • table entry
  • row in table

Content provider are excluded from intents so interaction with them is done in different way like using adb shell and then the command will be $ content query --uri content://com.android.contacts/contacts/1

What if we don’t have adb shell access, so we will write our malicious app.
in our app we want to define ContentResolver that asks the content provider to provide the content to us
we need 2 use cases:

  • Having permissions
  • Exported: True

Then after asking, thew content provider returns a Cursor(pointer to data in db) to our app
The content provider must include query, update, insert and delete methods and we will use them in the resolver for interaction with the provider

The data base of custom content provider are stored in data/data/<package>/db

query
Here we see the structure of query function and how it’s mapped to sql query.

sqli
and here we see a basic application of sqli.

content providers aren’t invoked by intents so to interact with it, it must be exported.

ContentProvider can also read and write files this moves us to path traversal vulenerability.
Let’s look in accessing file using content provider
path traversal
we see that the file is vulenrable to path traversal so the app can get pin.xml file in this example

The content provider is exported if we have read or write permissions, but the protection level may prevent us from exploitation if it’s signature.
protection level dangerous can be ok if the data is encrypted.

from the source code we can reach the implementation information of db like the database.db file and the tables’ names.

note: if table name is db structure source code = key and it’s refrenced in the manifest as keys we will use key in sqli.

interaction with content provider using adb shell is done using content query|insert|... --uri <content provider uri>
OFC the table must be accessable (we should have Read or write permissions), but we have another accessable table we can make use of it to reach the protected table.

Example: passwords table is accessable but key isn’t and we have this command content query --uri content://.....DBContentProvider/passwords we can make use of projection for sqli like this content query --uri content://.....DBContentProvider/passwords --projection "* from key--" so the query became select * from key -- from passwords ... but all after the – is commented so we can ignore it now and celebrate with the key data we got.

If the protected uri isn’t defined in regex we can query it directly like this content query --uri content://.....DBContentProvider/keys/

Insertion in DB: we can see its syntax from the help of adb shell content.

Deep Links can be implicit or explicit
They are links to specific part of an application, so with links you can access apps not only browsers.
And this is the reason for opening youtube app when we click on a youtube video link instead of opening the browser.

The app which is able to handle deep links has intent filter with category : browsable.
The intent filter may also contain one or more data tags.
this is example:

<intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="hex"/>
                <data android:host="open"/>
                <data android:host="flag"/>
</intent-filter>

we can access this component using the url hex://open or hex://flag

Example
acode is IDE for programming using the phone and when you decompile its apk you will find that it has an intent filter with a category browsable and the data scheme is acode and after digging in the source you can see that if we want to install a plugin we the url should be acode://plugin/install/<plugin ID>.
If you can register a malicious plugin then this functionality will be critical as you can force the victim to install it if he clicked the link.

The speech above is implicit deeplink.

Chrome on Android implements a custom scheme intent: with a lot more features than the regular deep links.

It has many features
- It’s a generic intent
- Control the action and category
- Target specific app and class
- Add extra values

This is called explicit deeplink at which only specified app can handle the deep link which lowers the risk.

There another way to decrease the risk which is app links
apps with httpor https schemes maybe used to hijack websites, but this can avoided with app links.
by making this intent filter <intent-filter android:autoVerify="true"> we will avoid the vulnerability
but this link must be registered through Google Search Console.

If the website is requested, assetlinks.json file must be included under /.well-known/ directory, and the app itself should be declared as an authorized app.

Web View

It’s a component that allows developers to embed web content into the app (acts as browser within the app).
It consists of
- Browser Engine: render HTML, CSS , JS to create the UI
- JS Engine: resposible for js execution and DOM manipulation
- Android Networking Framework: establish connections and handle https requests and fetching HTML, CSS, JS pages from a server
- performance enhancement: done by using GPU to enhance visual quality and responsiveness

signing

It’s needed in many things like keeping track if there’s an update for the app in the store
package name isn’t enough because it’s unique on the phone only but we can create apps with the same package name of other apps in the store.

So the developer signs the application with a private key and the signature with the package name are used to know that the app on our phone is the one on the store and this update is for it.

Private keys used for signing applications must be super safe.

This process is for verifing the author of the app.

we can sign 2 different apps with the same signature so they can share data together without the need of exported components so the components are no longer exposed to the whole world, but the app we need to exchange the data with only.

If we want to modify app.

  • use apktool to decompile the apk using apktool d game.apk
  • you will get the decompiled files and you can modify what you want
  • build the apk again apktool b game.apk
  • We can’t install the apk if it isn’t signed so we sign it using zipalign for preparing offset for the certificate to be placed in the correct place and apksigner for signing the app
  • before android 11 we could use jarsigner directly for signing

Let’s see the process in more details

  • generation the key
    keytool -genkey -v -keystore <path/to/keystore file> -alias alias_name -keyalg RSA -keysize 2048 -validity 365
    • -getkey for generating the private key
    • -v make the certificate human readable
    • keystore is a file which is the place at which the key will be stored
    • alias it’s the way we will define the key

      note that many keys can be saved in the same keystore but of course the alias will be different

  • for signing
    zipalign -v 4 base.apk out.apk
    apksigner sign --ks-key-alias <alias name> -ks <path/to/keystore file> out.apk
    alias is needed if there are more than a key in the keystore

METAINF dir contains all signing information so it must exist after signing

Common Application Strings


We may find useful strings to get more info

  • Hardcoded Strings
    can be found in: resources/strings.xml, activity source code
    we may find: login creds, api key, exposed url, firebase URLs