Introducing AIR GO

AIR GO is an app vulnerability scanning service for finding vulnerabilities in Android or iOS app package files (APK or IPA). AIR GO is somewhat similar to SandDroid, an open source project. Recently, LINE has been using AIR GO to check LINE apps for vulnerabilities before they are released, to provide secure services to LINE users. Anyone can access AIR GO simply by registering their email address. Actually, AIR GO had been introduced on the LINE website previously; today, I’d like to discuss about it in more of a developer’s point of view.

How AIR GO works

AIR GO takes in a package file (APK or IPA), decompresses the file and retrieves required information. Using the information, AIR GO performs the following tasks:

  • Analyzing security vulnerabilities
  • Checking for code obfuscation, open source licenses used, and malware
  • Producing a report for the result

If a URL was given, AIR GO checks for web-based malware. The order in which an APK or IPA file is diagnosed is this — decompiling code, parsing the decompiled code, comparing the parsed data with vulnerability patterns and finally, producing a report of the analysis. Let me take you through the process in detail; let’s suppose we have submitted an APK file.

Disassembly

Decompiling code and parsing is a process of decompiling a binary to turn it into a format that is easy to read, and to extract data required for the analysis. When you decompress an APK (Android package) file, you end up with files in various formats. AIR GO extracts information from DEX, SO, DLL and XML files. The classes.dex file contains intermediate bytecode, and runs on Dalvik VM; to convert the file into something us humans can read, we use smali. This conversion is called disassembling — converting byte code into an instruction list — and AIR GO uses smali code to analyze vulnerabilities.

Let’s see an example. The following code is a simple code that prints out “Toast Hello”, using the Toast class of Android.

String message = "Toast Hello";
Toast toast = Toast.makeText(this, message, Toast.LENGTH_LONG);
toast.show();

Here is the result of disassembling the code above into smali code.

const-string v2, "Toast Hello"
const/4 v3, 0x1
invoke-static {p0, v2, v3}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v1
invoke-virtual {v1}, Landroid/widget/Toast;->show()V

The original code in Java contains a call to the show() method. Let’s look at the corresponding in the smali code. Look at the bottom line of the smali code:

airgo_smali

The invoke-virtual instruction (see opcodes) is calling the show() virtual method on an instance of the Toast class (created by the makeText() method) stored in the v1 register. We can tell that the show() method is implemented in the Landroid/widget/Toast class, that it does not take any parameters and returns void (V).

To call a method with smali, you need to first write the fully-qualified name to the class, the method name, its parameters and the return type, in this order. AIR GO stores patterns of potentially vulnerable method invocations, and compares the analyzed code with its vulnerability patterns. When an APK is submitted, AIR GO compares the APK with the stored pattern. AIR GO checks for new mobile app vulnerability patterns registered on sites such as Google’s App security improvement program and , CVE, and if there are, AIR GO adds those patterns to itself.

Analysis – Insecure hostname verification

Now, let me share how we make an analysis, with an insecure hostname verification example.

Only a small portion of apps are standalone these days. Most apps communicate with servers, transfer and receive data to and from, and update information. Which means that there can be vulnerabilities in server-client communication. Insecure hostname verification is a vulnerability which allows an attacker to perform a man-in-the-middle (MiTM), tricking the client into connecting to an untrusted host. Apps are likely to access hostnames that have been set by developers. In order to prevent this vulnerability, we need to prohibit a client from accessing hosts that have not been set by the developer. In other words, we must make sure that clients always verify a server’s hostname and its certificate before they establish a secure connection.

To verify a hostname, we pass an instance of the HostnameVerifier class to the HttpsURLConnection.setHostnameVerifier() method. To implement the HostnameVerifier, we need to override the virtual method, verify, by which you can verify if the hostname is the one intended to access. If the hostname is valid, return true, otherwise false. But, returning true without a thorough verification still allows access to invalid hosts, opening the door to MiTM attacks.

The following Java code does not verify the given hostname, and is therefore vulnerable.

URL url = new URL("https://example.org/");
HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
// Set Hostname verification
urlConnection.setHostnameVerifier(new HostnameVerifier() {
    @Override
    public boolean verify(String hostname, SSLSession session) {
        // Ignore host name verification. It always returns true.
        return true;
    }
});

The code above is vulnerable, because as we covered, the verify method does not actually verify if the argument, hostname, is an intended one or not. AIR GO checks code in smali, so let’s have the code above converted into smali code, as follows.

# virtual methods
.method public verify(Ljava/lang/String;Ljavax/net/ssl/SSLSession;)Z
    .locals 1
    .param p1, "hostname"    # Ljava/lang/String;
    .param p2, "session"    # Ljavax/net/ssl/SSLSession;

    .prologue
    .line 62
    const/4 v0, 0x1

    return v0
.end method

We take two parameters, p1 and p2. p1 is for the hostname and p2 is for the session. There is no code verifying the value of p1. It only saves 0x1, which means true to the v0 register and the value in v0 is returned as it is. Such code will be detected as vulnerable by AIR GO.

Google recommends to have hostnames verified through thorough verification, as shown below. (Code source)

URL url = new URL("https://example.org/");
HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
// Set Hostname verification
urlConnection.setHostnameVerifier(new HostnameVerifier() {
    @Override
    public boolean verify(String hostname, SSLSession session) {
        HostnameVerifier hv =
            HttpsURLConnection.getDefaultHostnameVerifier();
        return hv.verify("example.com", session);
    }
});

Let’s see the verification code in smali. Contrary to the previous verification method, this time, the code actually verifies the given hostname. AIR GO checks whether the verify() method of the class instance passed to the setHostnameVerifier() simply returns true or performs an appropriate verification process.

# virtual methods
.method public verify(Ljava/lang/String;Ljavax/net/ssl/SSLSession;)Z
    .locals 2
    .param p1, "hostname"    # Ljava/lang/String;
    .param p2, "session"    # Ljavax/net/ssl/SSLSession;
    .prologue
    .line 21
    invoke-static {}, Ljavax/net/ssl/HttpsURLConnection;->getDefaultHostnameVerifier()Ljavax/net/ssl/HostnameVerifier;
    move-result-object v0

    .local v0, "hv":Ljavax/net/ssl/HostnameVerifier;
    const-string/jumbo v1, "example.com"

    invoke-interface {v0, v1, p2}, Ljavax/net/ssl/HostnameVerifier;->verify(Ljava/lang/String;Ljavax/net/ssl/SSLSession;)Z
    move-result v1

    return v1
.end method

Using AIR GO

To use AIR GO, create an account which only requires you to verify your email address. Once you register and sign in to AIR GO, you will see the main screen as shown below. You can start analyzing apps simply by uploading a package file (APK or IPA) or entering a URL. The maximum file size allowed is 512 MB. Once a file is uploaded, it will be sent to the AIR GO server, and will undergo a series of processes; analysis, parsing, scanning and reporting. The result will come out only in a few minutes and is saved for you to check again.

airgo_main

To show you what a result might look like, I scanned an APK. As you can see from the screenshot of a diagnostic report summary, there are the name of the package and the basic information of the app, such as the game engine used, code obfuscation status, vulnerabilities, open source license and malware. An app is assessed with one of the following security levels:

CRITICAL

The app has malware or one or more vulnerabilities that:

WARNING

The app has one or more vulnerabilities that:

  • Are classified as moderately severe by OpenSSL
  • Have scored 4.0–6.9, based on the CVSS v3.0 Ratings
  • Listed on Google’s security campaign list and have no remediation date

NORMAL

The app has one or more vulnerabilities that:

  • Are classified as less severe by OpenSSL
  • Have scored 0.0–3.9, based on the CVSS v3.0 Ratings.

SAFE

No vulnerabilities are found.

airgo_report

The result of detection is categorized into the following tabs. The information presented on each tab is as follows:

  • Summary: Detection counts per security level.
  • Obfuscation: Files are categorized into ‘OBFUSCATED’ and ‘NOT OBFUSCATED’. Core files that need to be but are not obfuscated are marked red, files of less importance and without obfuscation in orange, along with recommendations to obfuscate. Obfuscated files are marked green.
  • Vulnerability: Vulnerabilities are presented by severity, with the information on vulnerability source, description and guides on how to fix it.
  • License: A list of licenses of external libraries the app is using, with license information including whether the license has a reciprocal obligation or not.
  • Malware: The location where malware was found and the information used for detection.
  • Certificate: The content of the digital signature of the app, with the certificate issuer and the hash value.
  • Structure: The directory structure and files of the given APK or IPA.

AIR GO use case

LINE posts notices on LINE’s website and JVN. (Japan Vulnerability Notes) for the vulnerabilities found in the LINE app or any of the family apps to inform users to update their app to the latest version. For example, in July 2018, we announced the vulnerability in the LINE MUSIC Android app, due to failing to verify SSL server certificates, on our website and JVN.

This particular vulnerability was easily detectable by AIR GO — to prevent any similar issue, we’ve guided the developer to check the app with AIR GO after building the app. The vulnerability lied in HTTP certificate verification due to ignoring errors returned for attempting an HTTP connection to a remote host and also to an incorrect SSL certificate verification, and left a possibility for man-in-the-middle attacks. Attackers could intercept or modify the transferred data.

When implementing an HTTPS connection in Java, you can develop a safe socket protocol using the SSLContext class. The second parameter of the SSLContext.init() method is an array of TrustManager objects. You can override the TrustManager.checkServerTrusted() method and implement certificate verification by yourself. If you don’t verify the SSL certificate or skip handling exceptions for an invalid certificate, AIR GO would diagnose your app as vulnerable. While developing or testing and app we might have to ignore certificate verification due to environment or time constraints, but when you actually release and publish the app, vulnerable code must be removed and we need to verify an SSL certificate properly.

The following screenshot shows the details of the ‘TrustManager Verification’ vulnerability of LINE MUSIC. The detection count is 2, which means that AIR GO has detected two places for the given vulnerability. You are seeing the information of one of the two detections. Based on the information presented, we can tell that the GoogleHttpClient.a()method is calling the SSLContext.init() method, and that the second parameter of the init() method has a vulnerability. We can also see that the second parameter is the GoogleHttpClient$a, an inner class of GoogleHttpClient, and the vulnerability detected is in the checkServerTrusted() method of the GoogleHttpClient$a class. When we tracked this vulnerability, we found that the checkServerTrusted() method actually did not implement certificate verification.

airgo_detection

Summary

AIR GO detects vulnerable libraries used, insecure HTTPS certificate verification, accessing unintended hosts, insecure encryption algorithm, and unnecessarily exported Android components (activities, broadcast receivers, and services).

So we’ve seen how AIR GO works and how to use it. Here is a summary of what we’ve discussed so far:

  • AIR GO accepts binaries. Source code leaks are critical and may result in financial damages for companies; sharing source code with third-parties can be risky. AIR GO does not require you to provide raw source code, but takes in a binary file instead.
  • AIR GO checks whether DEX, DLL, and SO files are obfuscated. Obfuscation is a process that converts code into a form that is difficult to read or analyze, thus complicating code analysis and protecting the code. Determining whether code is obfuscated or not is difficult. If code is not obfuscated, we guide our developers to apply AIR ARMOR in their code.
  • AIR GO informs if your app satisfies all the security requirements of Google Play which your app must satisfy to be accepted. AIR GO enables you to check if your app is ready to be submitted to Google Play.

Plus, there is much more to AIR GO, including license check, URL scanning and IPA checking. Why don’t you have a go with AIR GO for your app?