Rise of the Machines: AI and Security – Free Webinar with Checkmarx’ CTO

Get Freebies by Abusing the Android InApp Billing API

Security researchers started talking about vulnerabilities in the Android InApp Billing API years ago, but we found it worthwhile to take another look to see how it has improved (or not) and verify the best way to build security into the application. The Android InApp Billing API is a powerful part of the Android framework that allows developers to easily monetize applications with in-app purchases and subscriptions. With this API, developers are able to sell content in their app, including:

*  Subscriptions to magazines
*  Premium features
*  Extra content in games

Google handles the payment process. In order to use the Android InApp Billing API, you must have Google Play services. This method means that the credit card data isn’t exposed to the developers.

The Android InApp Billing API provides two different product types:
*  Managed Products
>  Consumable: Can be purchased multiple times (e.g. coins, credit, etc.).
>  Non-Consumable: Can be purchased only one time (e.g. obtaining the pro version of an app).
*  Subscriptions
*  Products requiring recurring billing (monthly or annual).

For managed products or subscriptions, the developer needs to define the products in Google Play Console. Google tracks products and transactions using purchase tokens and order IDs.

InApp Billing Workflow

In a high level overview, the following steps occur to perform a payment using the InApp Billing API:
1.  The user starts a purchase inside the mobile application (i.e. buying credits or items).
2.  The mobile application uses the Google Play API to perform a purchase request.
3.  Google Play application asks the user for payment details (i.e. credit cards, PayPal, or a code).
4.  When the payment is correctly performed, Google sends a purchase result with the item purchased by the user and the signature of the payment.
5.  After some verification by the mobile application, the content is delivered to the user.

InApp Billing Workflow

Purchase Verification

As explained in step four (Purchase Result), Google Play returns the following elements to the app to verify the content purchased:
*  JSON object
>  OrderId: Identifier used to track the transaction
>  productId: Identifier of the product purchased
>  purchaseState: Notify if the purchase was successful or canceled
>  purchaseToken: String generated by Google Play to uniquely identify the transaction
*  Signature of the purchase
*  Signed with the private developer key
With those elements, it is necessary to perform a signature validation to ensure that the payment was performed correctly. To do that, the developer needs to validate the signature of the purchase using his public key.

For the purchase verification process, Google recommends that you validate the purchase details on a server controlled by the developer. Google stated that performing validation on the device is not truly secure.

Here is an extract of the best practices provided by Google:

Google Best Practices: Validating Purchase DetailsGoogle Best Practices: Validate On A Device

Implementation in Real Life

In order to familiarize developers with the InApp Billing API, Google provides a sample app to show how the API works.

Sample App ScreenshotSample App Screenshot 2

We can see that the signature verification is implemented inside the application. However, Google recommends that you implement this code on a server that communicates with the application.

Then, it’s also possible to use some third-party libraries to handle the InApp Billing API. We found two popular libraries used by Android applications:
*  Prime31
*  Unity

When looking at the Prime 31 documentation, we can see that the library is only able to perform client side validation:

Purchase Validation

In this library, the functions “verify” and “verifySignature” in charge of validating the signature of the payment are located in: com.prime31.util\Security.java

In a same manner, the Unity library is not able to handle server side verification:

Point Of Validation

As you can see, these two libraries don’t support server side validation.

Android InApp Billing API Vulnerability

InApp Billing Library Hacked

In 2013, Dominik Schürmann found two vulnerabilities in the Google InApp Billing library allowing to bypass the payment process and to be able to buy unlimited items. Dominik detailed the vulnerabilities on his blog.

The first vulnerability Dominik found allows a malicious application installed on the device to impersonate the Google Play billing service (com.android.vending). In fact, it was possible for a malicious app to define an intent filter with a high priority in order to receive the requests sent by the targeted app.

The second vulnerability Dominik found related to the signature verification. In short, if the signature is an empty string (“”), the verifyPurchase function returns true. Here is a sample of the verifyPurchase function used at this time:

The problem of this function is that, by default, it returns true. So, if the signature is empty, the verify function is never called and the function returns true.

Dominik developed a malicious app allowing you to exploit this vulnerability. It’s easy to use, you just have to compile the project and install the APK. Then, you can use the app to exploit the vulnerability in any application using InApp Billing.

Google fixed these two vulnerabilities by applying the following changes:
1.  Every mobile application using the InApp Billing API should define which is the target package for the intent as shown below:

Intent serviceIntent = new 
Intent("com.android.vending.billing.InAppBillingService.BIND"); 
serviceIntent.setPackage("com.android.vending");

*  Google modified the function checking the signature to return true only if the signature is valid as shown below:
Verify Purchase Modified

 

InApp Billing Hacked Again

Although Dominik discovered that vulnerability in 2013, it’s still possible to bypass the InApp Billing process and to obtain unlimited credits in different applications.

In order to exploit this issue in your Android phone, you don’t need to have rooted the device or have any specific privileges. You simply need to perform the following steps:
1.  Install a malicious application able to impersonate the Google Play Billing service (com.android.vending). For instance, you can still use the code Dominik provided and make some adjustments.
2.  Decompile the targeted application with apktool.
3.  Modify the smali code to replace the package used for the InApp Billing with your malicious application.
4.  Modify the smali code to change the function performing the signature validation. The idea is to return true in all cases.
5.  Recompile the targeted application with apktool.
6.  Sign the newly created package with jarsigner.
7.  Install the modified application in your device.
8.  PROFIT!
Note: I provide examples and technical details in the Vulnerable Mobile Applications section.

During our investigation, we found some mobile applications claiming that they are able to buy items for free in many mobile games:
*  CreeHack
*  LeoPlaycard
*  Lucky Patcher

After analysis, it appears that CreeHack and LeoPlaycard are exploiting the issue Dominik discovered. However, all the applications available on the Google Play store are not vulnerable anymore. Lucky Patcher was the only application we found that modifies the targeted application in order to bind the Google Play Billing service to Lucky Patcher.

InApp Billing Testing Keywords

Google offers three ways for developers to test the InApp Billing implementation in their mobile application.
1.  Test Purchases: Developers can define test accounts in their Google Play Console to make purchases without any actual charges.
2.  Real Purchases: Developers can perform real transactions with their mobile application.
3.  Static Responses: Developers can use specific keywords to perform dummy transactions. Google provides four reserved product IDs:
*  test.purchased: When you make an Google Play Billing request with this product ID, Google Play responds as though you successfully purchased an item. The response includes a JSON string, which contains fake purchase information.
*  test.cancelled: When you make an Google Play Billing request with this product ID, Google Play responds as though the purchase was canceled.
*  test.item_unavailable: When you make an Google Play Billing request with this product ID, Google Play responds as though the item being purchased was not listed in your application’s product list.
*  test.refunded: When you make an Google Play Billing request with this product ID, Google Play responds as though the purchase was refunded. However, it seems that this static response was removed.
Those Static Responses should be used only when debugging an application or in a testing environment. However, we found some mobile applications still allow the use of static responses in their production code!

At BSides Lisbon 2017 Jérémy Matos made a good presentation on this subject.

The first example of a mobile application that allows these static responses is PandaPop. In the verification process, the mobile application checks if the Static Response uses “android.test.purchased.” In this case, the signature of the payment is not verified.

File: com.prime31\GoogleIABPlugin
Function: purchaseProduct

Google AB Plugin

File: com.prime31.util\IabHelperImpl
Function: handleActivityResult

Iab Helper Impl

Another example is a library called Android InApp Billing v3. It is an open source library similar to Prime31 and Unity, used to handle requests to the InApp Billing API.

When you look at the source code of the library, you can find the Security.java file containing the following lines in the “verifyPurchase” function:

public static boolean verifyPurchase(String productId, String base64PublicKey,
                                         String signedData, String signature)
    {
        if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey)              ||           TextUtils.isEmpty(signature))
        {

            if (
                    productId.equals("android.test.purchased") ||
                    productId.equals("android.test.canceled") ||
                    productId.equals("android.test.refunded") ||
                    productId.equals("android.test.item_unavailable")
                    )
            {
                return true;
            }

            Log.e(TAG, "Purchase verification failed: missing data.");
            return false;
}

If the Static Responses are in use, this library validates the purchase without any verification.

Vulnerable Mobile Applications

Fruit Ninja

FruitNinja has been downloaded more than 100 million times. It’s a simple game where the goal is to destroy the maximum number of fruits in a limited time.

When we looked at the source, we found that most of the InApp Billing workflow is handled on the com.halfbrick.bricknet.GooglePlayBillingService class.

We identified a function called PurchaseResult that is used to validate the purchase.

This function is called several times in the onActivityResult function as shown below:

the onActivityResult function

On the previous code sample, we saw that the PurchaseResult is called with different parameters:
*  The product ID string (str)
*  Two booleans
*  The In-app purchase data
*  The In-app signature of the purchase
When we looked at the code of the PurchaseResult function, we saw that another function called PurchaseResultNative is used:

PurchaseResultNative

And in fact, this function is a native function as shown below:

Native Function

The developers decided to create a shared library (here called libmortage.so), which handles the critical functions. So, in order to understand what the PurchaseResultNative does, you need to reverse engineer the shared library.

However, it seems the PurchaseResultNative function suffers a security issue on the signature validation. Indeed, during our tests, we found if the signature is empty, the signature is considered valid. Dominik found this same behavior in 2013.

So, to bypass the InApp Billing workflow, we only need to replace com.android.vending string with the name of our package, which is org.billinghack in the following code:

bypass Workflow

We performed the modifications on the smali code.

The Exploitation in the Game

We performed our tests in an emulator and on a physical device. First, we launched the store in the game. As you can see, due to the ability to impersonate the Google Play Billing service, there is no price for the different items:

there is no price for the different items

We can “buy” any item we want! Dominik’s application returns a pop-up asking if we want to obtain the item for free:

Billing Hack: Buy it for FREE

Finally, the purchase was a success; you can see the number of apples increased to from 1900 to 1950:

Purchase Success

We created this video as a proof of concept:

Juice Jam

JuiceJam is another popular application and has been installed more than 10 million times.

In the same manner, we modified the smali code to change the targeted package for the Google Billing service. In this application we modified the function startSetup in com.jesusla.google.IabHelper.smali file as shown below:

JuiceJam Start Setup Modified

Then, we just modified the verifyPurchase function in com.jesusla.google.Security.smali file to return true every time. Here’s the Java source code decompiled with Jadx:Java Source Code Decompiled

Here’s the smali code modified to return true by default:

smali Code Modified

 

We just added the following code before the return, which implies that the register v1 is set to true:
*  const/4 v1, 0x1
After applying these modifications, you only need to recompile the application and enjoy your ability to have free credits!

We found several other popular apps that were also vulnerable to the same technique.

Stopping Attackers

A good approach is to use an external server controlled by the developers that performs the signature verification and provides the content bought by the user. However, if the developers are not selling real content, but items similar to credits, an attacker will still be able to find a way to obtain credits by reversing the application.

The other approaches to securing applications using the Android InApp billing API are imperfect and only slow down an attacker. The developers can use Proguard to obfuscate the code and prevent people from understanding the verification process. Another solution is to use the Java Native Interface (JNI) to use native code embedded in the application. In this scenario, a developer can embed the sensitive functions in a shared library used by the mobile application. Again, trying to reverse low level languages is more time consuming. It’s difficult to implement a solution that can reliably and securely prevent attackers from modifying a mobile application to bypass the payment process using the Google Android InApp Billing workflow.

The JavaScript Guide: Web Application Secure Coding Practices - Whitepaper

Disclosure Timeline

Fruit Ninja

15-Jun-2018 – Contacted Fruit Ninja.
20-Jun-2018 – Fruit Ninja confirmed they received the findings, mentioning they will get back to us if they need to follow up.
27-Jun-2018 – Sent reminder to Fruit Ninja, mentioning our intent to publish.
Never heard from them again.

JuiceJam

15-Jun-2018 – Contacted Juice Jam.
15-Jun-2018 – Juice Jam confirmed receiving our message, mentioning they will get back to us.
Never heard from them again.

 

Jump to Category