Firebase Applications – The Untold Attack Surface

Introduction

In this blogpost, we will review some of the basic components of a Firebase application from a Security Perspective and talk about common issues that don’t get enough attention.

What is Firebase?

Firebase is a complete backend as a service with many different features that we can plug straight into our applications. For example:

  • Firestore – This is a NoSQL database, allowing you to store your application’s data
  • Firebase Auth– A service that allows you to add authentication & authorization into your firebase applications by configuring the Firebase Security Rules
  • Cloud Functions– Serverless service. Allows you to run a backend code (usually this is where you’d place your REST API backend)
  • Cloud storage – for storing files

There are more services such as Cloud Messaging, Analytics, Machine Learning, etc.

The list of features is really big and won’t be covered here. In this post, we’ll focus on the first two: Firestore DB and Firebase Auth.

Firebase Auth

Firebase Authentication is used to authenticate users to your app. It provides easy-to-use SDKs, and ready-made UI libraries. It also supports authentication using passwords, phone numbers and identity providers such as Google, Facebook and Twitter, and more.

Security Fun Fact: As long as the Firebase Auth service is enabled: registration is always open, meaning that it’s possible to create new users even if the application itself doesn’t have a registration page (for example: Internal Organization Applications where the accounts created by the company’s employees and not remote clients)

You can create a new account in your target application by utilizing the Client SDK and call createUserWithEmailAndPassword() directly with your own JavaScript code:

<script src="https://www.gstatic.com/firebasejs/7.15.5/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.15.5/firebase-auth.js"></script>

<script>
    /*
    * Insert your target's application config in the firebaseConfig object below.
    * This information exists in the client-side and visible to everyone.
    */
const firebaseConfig = {
    apiKey: 'redacted',
    authDomain: 'redacted',
    databaseURL: 'redacted',
    projectId: 'redacted'
};

app = firebase.initializeApp(firebaseConfig);
authService = firebase.auth();
authService.createUserWithEmailAndPassword('appsec@pwn.com', '123456789').then( creds => {
    console.log(`Account created (${creds.user.email})`);
});
</script>

The snippet above will create a new account. This allows a remote attacker to test not only Authentication issues, but also Authorization issues, for example: Since the attacker now has a new account in the application, he can obtain a JWT using signInWithEmailAndPassword() and start performing queries in the Firestore DB that are not available to un-authenticated clients.

How to prevent
As it is right now (2020), you cannot disable sign-up without disabling the whole Auth service (meaning, disabling sign-in for all users too).

However, there’s a workaround for this: If you want to close the registration option but keep the Auth service alive (so other users will still be able to login), you can implement a custom Cloud Function that hooks into the user creation process using the Authentication Service Triggers.

Firestore DB

Firestore itself is a NoSQL database. Meaning that we don’t use columns, table and rows.
Instead, when we create a Firestore database, we split our data up into collections.
And inside each of those collections we store documents (which you can think of “records”).

For example: We have this collection called “books” and all those different documents stored inside it. Each one of those documents are given a unique identifier (usually generated by google)

Web Application / Mobile Applications’ front-end code uses Firestore client SDK to communicate directly with the Firestore DB in real-time and perform actions such as Update, Delete, Get, Create, and more.

We don’t want malicious users to perform queries on other legitimate user’s data in the Firestore DB. In order to enforce authentication and authorization between clients, every incoming request is filtered using the Firebase Security Rules, which we’ll review next.

Firebase Security Rules

Firebase Security Rules is an additional layer of access control and data validation which allows you to filter requests/Firestore DB queries before they are processed further in Google’s Cloud Environment. In order to create a user-based and role-based access system that keeps your users’ data safe, you should use the Firebase Auth service (which we reviewed before) with proper Firebase Security Rules set.

To identify the client that sends a request/query, Firebase Security Rules provides us a special object called request.auth:

  • If the client is not authenticated, this object will be null
  • If the client is authenticated / has a live session: This object contains the properties from the user’s JWT.

Note: Google’s server-side is responsible for validating the signature of the client’s JWT. Hence, when writing the security rules, we can assume that the data in request.auth object is coming from a trusted source.

How do I write Rules to enforce Authentication?

To enforce authentication, you just need to make sure that the request.auth object is not null when writing the Security Rules.

For example:

service cloud.firestore {
  match /databases/{database}/documents {
    match /usersBooks/{userId} {
      allow read, write: if request.auth != null;
    }
  }
}

How do I write Rules to enforce Authorization?

The snippet below shows how to use the request.auth object in order to identify who’s sending the request, and more importantly, to determine whether this client is authorized or not to perform a query:

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure the uid of the requesting user matches name of the usersBooks
    // document. The wildcard expression {userId} makes the userId variable
    // available in rules.
    match /usersBooks/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

Pentesting Firebase Apps

The most popular attack vector for Firebase Applications is checking if the Firestore DB can be accessed without authentication. There are a lot of tools/automated scripts on github that is doing this security check.

However, this security check is very simple and it covers just the tip of the iceberg. Besides authentication issues (which can be mitigated by adding a simple if request.auth != null rule), there’s a whole world of Authorization issues that was left undocumented.

Since this subject didn’t get enough attention, I decided to write firepwn: A tool for testing Firebase Authentication and Authorization issues.

The tool can be found here: https://github.com/0xbigshaq/firepwn-tool

Firepwn uses Google’s Client SDK to perform any query of your choice (get, set, delete and update), example:

This will allow you to “extend” the abilities of your target application and perform custom queries of your own.

Testing for Authorization Issues

If you want to check for Authorization issues, make sure you are authenticated to the target application. This can be done using the Auth Service (on the top right of the UI):

If you don’t have an account in your target application, you can register a new one by using the “Register” feature (above) and then login with those credentials to obtain a fresh JWT.

Testing Authentication Issues

To perform authentication checks: Simply perform queries and make sure that the “Auth Service” (on the right) says “not logged in”.

Understanding the Android clearTextTrafficPermitted Flag

Introduction

The cleartextTrafficPermitted flag is one of the options in Android’s Network Security Configuration file. The online documentation (https://developer.android.com/training/articles/security-config) explains that from Android 9 (API level 28) and higher, it will be set by default to false and it is intended to prevent insecure communication attempts using clear-text HTTP originating from Android applications.

OK, so what does this actually mean? Will all apps that run on the Android 9 platform with a default configuration be protected? Do these flags instruct the system to prohibit any kind of non-secure TCP communications or does it only apply to HTTP? What about 3rd party libraries such as OkHttp, async-http-client, dubbo? What about WebView controllers? What about native libraries issuing network requests? To answer all these questions, we need to understand at which level this configuration is being enforced. Otherwise, we may fall into a false sense of security.

But how does it actually work? What component and at which level enforces these restrictions? Is it a kernel module such as SELinux or AppArmor? It is Linux containers? Is it the Android Runtime environment? Is it a compiler flag that acts at the time of Ahead-Of-Time or Just-in-time compilation to strip the insecure code parts?

In this article, we will try to understand how this flag works by example and how it can help us to make a more secure application. We will also learn by example which use-cases aren’t being protected by the flag.

The importance of TLS

Just before we begin, a short recap on why we need to protect all application communication with TLS.

Potential threat agents include:

  • Wi-fi layer attackers – Numerous router level vulnerabilities, wi-fi hacking, and results of our private research show that tens of millions of routers could be compromised. As such, even home wi-fi cannot be considered as a secure by default. The app must maintain security at the network layer to provide secure transport for app-server communications.
  • WAN layer attackers – ISPs logging network traffic or APT and Government level actors monitoring communications at central network hops.

Compromised communications can lead to a number of possible risks including:

  • Hijacking secrets from client-side libraries via server-initiated redirects (We will talk about this more in the next blog post)
  • HTTPS connections can be downgraded to HTTP even if a server does not support HTTP
  • Open redirects to HTTP, both client-side initiated and server-side triggered (for example, the HTTP Desync Attack)
  • HTML injected resources (img) with http:// link that trigger HTTP communication
  • In-app user-provided clear-text HTTP link to app’s domain
  • Cache poisoning resulting in clear-text HTTP redirects

Creating a test-bed

To check how applications actually behave, we created an Android testing application and a python server for the application to communicate with. The test included trying different target APIs for the application and checking for each target API if the app could issue non-secure outgoing HTTP requests using different libraries as well as the default Android WebView controller.

For each library, three requests were sent: standard HTTPS, standard HTTP, and an HTTPS request to a URL which redirects the user agent to HTTP to simulate server-side-issued redirect attacks.

Testing notes

  1. Test results for HTTPS to HTTP redirects were the same as for HTTP. If in normal conditions the library prohibits HTTP connections it will also prohibit redirects to HTTP.
  2. Differences between minimal, target, and compile SDK versions from the manifest file:
    1. Minimal API – minimal API support required for the app to work;
    2. Compile API – IDE compiler used to compile the app
    3. Target API – the API level on which the application should run correctly as it was fully tested by the developers.
  3. Minimum SDK has no impact on how the cleartextTrafficPermitted flag is being enforced; only the API target level affects it.
  4. Most of the security issues arise on outdated Android OS versions. However, these versions are running on a large number of devices according to Google:

The results

This section will go through the results for the different libraries tested.

OkHttp

OkHttp is the most popular Android HTTP library and is maintained by Square: https://square.github.io/okhttp/. For the test the latest 3.* and 4.* versions were used. The results for both versions were the same.

The following table shows the results of using our testing app with different target SDK configurations and without the clearTextTrafficPermited flag set (i.e. the default configuration) on multiple Android devices of different OS versions.

 Android 5 API 21Android 6 API 23Android 7.1 API 25Android 8 API 26Android 9 API 28Android 10 API 29
target API 21-27 HTTP works HTTP works HTTP works HTTP works HTTP works HTTP works
target API 28+ HTTP works HTTP works HTTP works HTTP worksHTTP doesn’t workHTTP doesn’t work

The following table shows the results of using our testing app with different target SDK configurations and with the clearTextTrafficPermited flag set to “false” on multiple Android devices of different OS versions.

<base-config cleartextTrafficPermitted=“false”>


 Android 5 API 21Android 6 API 23Android 7.1 API 25Android 8 API 26Android 9 API 28Android 10 API 29
target API 21-27 HTTP works HTTP worksHTTP doesn’t workHTTP doesn’t workHTTP doesn’t workHTTP doesn’t work
target API 28+ HTTP works HTTP worksHTTP doesn’t workHTTP doesn’t workHTTP doesn’t workHTTP doesn’t work

The following table shows the results of using our testing app with different target SDK configurations and with the clearTextTrafficPermited flag set to “true” on multiple Android devices of different OS versions.

<base-config cleartextTrafficPermitted=“true”>


 Android 5 API 21Android 6 API 23Android 7.1 API 25Android 8 API 26Android 9 API 28Android 10 API 29
target API 21-27 HTTP works HTTP works HTTP works HTTP works HTTP works HTTP works
target API 28+ HTTP works HTTP works HTTP works HTTP works HTTP works HTTP works

When the network security policy is being correctly enforced, the OkHttp library won’t issue the request and will throw the following exception:

We can find multiple instances of checking the flag in OkHttp’s source code (https://github.com/square/okhttp) and can manually verify at which cases the check is being done. However, understanding the libraries internal logic may be required to identify the edge cases in which the flag could be ignored. The screenshot shows the source code for the relevant part of the connection chain:

Other Popular Network Libraries for Android

This section shows results for async-http-client library but the results were the same for multiple other libraries that were tested. For the most part, libraries which use okhttp “behind the scenes” support the flag but other popular Android libraries which don’t use okhttp (like async-http-client and many others) don’t support the flag at all.

The following table shows the results of using our testing app with different target SDK configurations with the clearTextTrafficPermited flag set to “false” on multiple Android devices of different OS versions. Since the flag is unsupported, the behavior is in fact the same as if the flag wasn’t set or if the flag was set to “true”.

<base-config cleartextTrafficPermitted=“false”>


 
Android 5 API 21

Android 6 API 23


Android 7.1 API 25

Android 8 API 26

Android 9 API 28

Android 10 API 29
target API 21-27 HTTP works HTTP works HTTP works HTTP works HTTP works HTTP works
target API 28+ HTTP works HTTP works HTTP works HTTP works HTTP works HTTP works

Android WebView

In this section we tested the default Android WebView . This test showed that in fact the WebView controller behaves differently to the OkHttp library in the context of enforcing the configuration flag. In particular:

  1. When the application’s target API is 25 or less, the “cleartextTrafficPermitted=”false”” is being ignored across all tested Android OS versions.
  2. When HTTP redirect occurs, the WebView behaves differently – it forwards the request to the device’s default browser (external) instead of rendering it on its own. This is secure behavior since the browser doesn’t send any sensitive cookies because it runs in a different context to the application’s WebView.

The following table shows the results of using WebView in our testing app with different target SDK configurations and without a value for the clearTextTrafficPermited flag set (default configuration) on multiple Android devices of different OS versions.

 Android 5 API 21Android 6 API 23Android 7.1 API 258 API 269 API 2810 API 29
target API 23-27HTTP worksHTTP worksHTTP worksHTTP worksHTTP worksHTTP works
target API 28+HTTP worksHTTP worksHTTP worksHTTP worksHTTP doesn’t workHTTP doesn’t work

The following table shows the results of using WebView in our testing app with different target SDK configurations and with the clearTextTrafficPermited flag set to “false” on multiple Android devices of different OS versions.

 Android 5 API 21Android 6 API 23Android 7.1 API 258 API 269 API 2810 API 29
target API 23-25HTTP worksHTTP worksHTTP worksHTTP worksHTTP worksHTTP works
target API 26-27HTTP worksHTTP worksHTTP doesn’t workHTTP doesn’t workHTTP doesn’t workHTTP doesn’t work
target API 28+HTTP worksHTTP worksHTTP doesn’t workHTTP doesn’t workHTTP doesn’t workHTTP doesn’t work

When the network policy is being correctly enforced, the WebView will show the following image

Configuration recipes

In this post, we won’t cover all configuration options, but only the main configuration options that should be understood. Full information can be found at https://developer.android.com/training/articles/security-config

To configure the clearTextTrafficPermited flag, we need to set the network security config at the manifest file level:

<application

    android:networkSecurityConfig=“@xml/network_security_config”>

We then need to populate the network_security_config.xml file with the relevant settings.

We have included below some examples of the network config file (network_security_config.xml) and the actual meaning of the settings:

  1. Disables all insecure outgoing connections – should be a default choice

<?xml version=“1.0” encoding=“utf-8”?>

 

<network-security-config>

    <base-config cleartextTrafficPermitted=“true”>

    </base-config>

</network-security-config>


  1. Allows HTTP traffic only to appdomain.com domain. All other domains will only be allowed via HTTPS.

<?xml version=“1.0” encoding=“utf-8”?>

 

<network-security-config>

    <base-config cleartextTrafficPermitted=“false”>

    </base-config>

    <domain-config cleartextTrafficPermitted=“true”>

            <domain includeSubdomains=“true”>appdomain.com</domain>

    </domain-config>

</network-security-config>


  1. Forbids any insecure traffic to appdomain.com but allows insecure connection for all other domains (base-config will be default):

<?xml version=“1.0” encoding=“utf-8” ?>

<network-security-config>

        <domain-config cleartextTrafficPermitted=“false”>

               <domain includeSubdomains=“true”>appdomain.com</domain>

        </domain-config>

</network-security-config>


  1. Forbids any insecure traffic to appdomain.com; allow explicitly all insecure connection for all other domains:

<?xml version=“1.0” encoding=“utf-8”?>

 

<network-security-config>

    <base-config cleartextTrafficPermitted=“true”>

    </base-config>

    <domain-config cleartextTrafficPermitted=“false”>

            <domain includeSubdomains=“true”>appdomain.com</domain>

    </domain-config>

</network-security-config>


Results Quick Q&A

In this section we will summarize some of the key questions which we have answered so far in our research:

  • What happens when the app communicates using HTTP and makes use of a framework/library which does not support this manifest flag
    • No restrictions will be in place
  • At which level does the manifest restrict clear-text communications?
    • Each library reads the flag from configuration flags and enforces the restrictions but only if it supports the flag.
  • What happens when native code issues a clear-text network request? For example, it sends an HTTP request or sends binary data (such as protobuf)? Will it be protected by this manifest configuration?
    • Only if the library supports the flag. In most cases these libraries won’t support the flag. The native code libraries and non-HTML libraries mostly have no flag awareness and support. You should rely only on how you configured the library. These configs either can be done in a “config” file or at the actual network request object properties and methods.
  • What happens when these libraries issue requests using HTTP and nonstandard port?
    • Generally, if the library supports the flag it will block all outgoing cleartext HTTP communication regardless of the port.

Result analysis and wrap-up

Multiple tests show that manifest flag clearTextTrafficPermited is not being universally enforced by either the Android OS or at the SDK level. IN practice, each client-side library must implement support for the flag on its own and the actual implementation may heavily differ from one library to another.

Certain libraries such as OkHttp (or Volley which uses OkHttp behind the scenes) have full support for this flag according to their documentation. However, many other HTTP libraries which do not use OkHttp do not support it at all and the presence of the flag has no impact on the app’s behavior.

The Android WebView component has support for the flag but actual results show inconsistencies between the WebView and OkHttp implementations. In fact, Android WebView puts more clients at risk compared to OkHttp (refer to the WebView result table) as it does not enforce the flag unless target API is 26+.

Regarding other clients, such as binary communication libraries, etc, do not assume that these libraries will respect the flag. Instead, the source code of each such library should be checked for the code part that enforces the restrictions on outgoing insecure communications. The default assumption for all client-side network libraries should be that they don’t support the flag unless the library explicitly states that it does.

Despite the lack of actual system-wide level protection, the flag still offers a greater level of security in situations where it is supported by the client library and either the application’s target SDK is set to 28+ or the application is running on Android 7.1 or higher.

Recommendations:
  1. Activate the clearTextTrafficPermited protection by setting it to “false” for all outgoing connections.
  2. Set the target API level to 28+.
  3. Check actual support for the flag across different libraries in use in your application.
  4. Be aware that the flag enforcement has its restrictions.
  5. Enforce other supplementary transport security measures, such as:
    • Using secure response headers such as HSTS.
    • Using explicit request object configurations to prohibit HTTP.
    • Implementing custom interception layers for libraries being used which will block clear-text URI schemes.
    • Removing any HTTP:// links from the code and server’s configuration.

Angular Template Injection without Quote Characters

Introduction

When you’re trying to detect XSS in an Angular application (AngularJS or Angular >2, the version doesn’t matter for this post), you’ll probably try the following possibilities:

  • You enter a basic XSS payload such as <script>alert(0);</script>
  • You find out that the developer encoded the output properly and the < > characters turn into &lt; and &gt; 
  • Your next attempt will be a client-side template injection: {{1338-1}}
  • The application displays 1337
  • From here on, it will be straight-forward: you’ll find a payload that matches the Angular version of the app and pop that alert to get a Proof of Concept.

However, in some cases, the output sanitation applies not only to characters like > and < but also encodes single and double quote characters ( ‘ will turn into &apos;, etc.)

This means that if you try to insert a payload that has quotes in it, for example:

{{constructor.constructor('alert(0)')()}}

It will turn into

{{constructor.constructor(&apos;alert(0)&apos;)()}}

At this point, your expression will trigger a syntax error and the payload will fail. 

So, what can we do about it?

When trying to avoid quotes in a payload (because it contains strings) the easiest way is to use functions that generates strings. In JavaScript, we will need to use the String object, specifically, the String.fromCharCode() method

The issue in this case (Angular applications) is that we cannot just call String.fromCharCode()in an Angular expression:

{{constructor.constructor(String.formCharCode(...))()}}

It will not work because Angular limits us to only using the properties and variables that are accessible under Angular’s scope. (More about the scope here: https://github.com/angular/angular.js/wiki/Understanding-Scopes). If Angular wasn’t stopping us from accessing objects outside the scope, we wouldn’t need to access the Stringobject. As attacker, we could just inject {{window.alert(0)}}

So what do we do when we don’t have access to the Stringobject but we still need to call fromCharCode() to generate our “alert(0)” string?

We can use this payload:

{{constructor.constructor(valueOf.name.constructor.fromCharCode(...))()}}

Let’s break it down

  • We used valueOf.name because it returns a string value. Right now, it doesn’t matter what string is returned. As long as it’s a string variable – we’re good to go. In the same way, we could use any of the following possibilities as they all generate strings:
  • Now that we have a string variable in our payload, let’s look at its constructor:
  • The constructor itself is a String object! Now, all we have to do is to add a dot character and access all of the available functions in the String object including fromCharCode

By getting to the String object in this indirect way, even though the object itself isn’t in Angular’s scope, we can create an Angular, Client-Side Template Injection payload without needing single or double quotes

And here’s our alert 🙂 without quotes.

Acknowledgements

Big shout-out goes to the folks from PortSwigger and Tomer Haddad


A Taxonomy on Brute Force Attacks

Brute force attack is a well-known technique of trial and error attempts used by attackers to gain access to unauthorized data. It can be leveraged against servers as an online attack and also against files as a local attack.

The common denominator of all these types is that the same pattern is almost always the same:

In most cases the attacker would have to know two major keys from the diagram – let’s take for example a common scenario where the attacker tries a brute-force attack on the login interface of an applicaiton in order to find the correct credentials of a certain account. The known keys would be:

  1. Something I know – In this case, the username of the account that the attacker wants to hijack.
  2. The result – On each attempt, the attacker receives an indicator of failed/successful result.

The well-known brute-force methods are:

Read more

Secure Development Lifecycle for Open Source Usage

Preface How do we adjust the SDL (Security Development Lifecycle) process for the growing use of open source in internal/external systems we develop and maintain? This is a question I hear a lot lately from our customers in some recent SDL projects we (AppSec Labs) carried out for our customers. After we did some research, […]

Hacking Android Apps Through Exposed Components

In almost every Android application, developers expose activities without sufficient protections. Exposing activities can lead to various attacks. For example, an attacker or a malicious app installed on the same device, can call those exposed activities to invoke internal pages of the application. Calling internal pages puts the application at risk of phishing by manipulating […]