CVE-2026-79920 Overview
CVE-2026-79920 is a missing authorization vulnerability [CWE-862] in Ajenti, a modular Linux and BSD server admin panel. Versions prior to 2.2.16 allow any authenticated user to invoke the /api/core/tasks/start endpoint and enqueue the InstallPlugin, UnInstallPlugin, or UpgradeAll tasks defined in plugins/plugins/tasks.py. The task worker executes pip as root, so a low-privileged account can install, remove, or upgrade Python packages under the ajenti.plugin.* namespace. Attackers gain arbitrary root code execution and full host compromise. Ajenti fixed the issue in version 2.2.16 by adding permission checks and validating the package name and version fields.
Critical Impact
Any authenticated Ajenti user can trigger root-level pip operations, resulting in complete host takeover.
Affected Products
- Ajenti server admin panel versions prior to 2.2.16
- Deployments running the plugins core module
- Linux and BSD hosts where Ajenti runs as root
Discovery Timeline
- 2026-09-21 - CVE-2026-79920 published to NVD
- 2026-09-24 - Last updated in NVD database
Technical Details for CVE-2026-79920
Vulnerability Analysis
The InstallPlugin, UnInstallPlugin, and UpgradeAll classes in plugins/plugins/tasks.py are registered as background tasks that can be enqueued through the core task API. The /api/core/tasks/start endpoint requires authentication but does not enforce a plugin-management permission before scheduling these tasks. Any authenticated session, including low-privileged accounts, can therefore start plugin lifecycle operations reserved for administrators.
The task worker runs with root privileges because Ajenti manages system services. When InstallPlugin executes, it builds a pip specification string of the form ajenti.plugin.{name}=={version} from user-supplied fields and passes it to subprocess.check_output([sys.executable, '-m', 'pip', 'install', self.spec]). Because name and version are not validated, an attacker can select any package published under the ajenti.plugin.* namespace or manipulate version constraints. Successful installation triggers arbitrary code execution as root through package installation hooks.
Root Cause
Two defects combine to produce the impact. First, the task registration path omits an authorization check, so the endpoint trusts any authenticated caller. Second, the pip specification is constructed from unvalidated input, allowing malformed names or versions that would otherwise be rejected. The fix in version 2.2.16 introduces both a PermissionProvider for plugin management and strict regex validation of the name and version parameters.
Attack Vector
Exploitation requires network access to the Ajenti web interface and valid credentials for any user account. The attacker sends a crafted request to /api/core/tasks/start referencing the InstallPlugin task with a controlled name and version. The worker executes pip install as root, fetching and installing the attacker-selected package. Code execution follows immediately via the package's install-time hooks.
# Patch excerpt from plugins/plugins/tasks.py (v2.2.16)
import os
import re
import sys
import logging
import subprocess
from aj.auth import authorize
from aj.plugins.core.api.tasks import Task
_SAFE_NAME = re.compile(r'^[A-Za-z0-9]([A-Za-z0-9._-]*[A-Za-z0-9])?$')
_SAFE_VERSION = re.compile(r'^[A-Za-z0-9]([A-Za-z0-9.+_-]*[A-Za-z0-9])?$')
def _check_spec_parts(name, version=None):
if not (name and _SAFE_NAME.match(name)):
raise ValueError(f'Refusing malformed package name {name!r}')
if version is not None and not (version and _SAFE_VERSION.match(version)):
raise ValueError(f'Refusing malformed package version {version!r}')
class InstallPlugin(Task):
name = 'Installing plugin'
def __init__(self, context, name=None, version=None):
Task.__init__(self, context)
_check_spec_parts(name, version)
self.spec = f'ajenti.plugin.{name}=={version}'
def run(self):
if os.getuid() == 0:
subprocess.check_output([sys.executable, '-m', 'pip', 'install', self.spec])
Source: GitHub Commit Bug Fix
Detection Methods for CVE-2026-79920
Indicators of Compromise
- POST requests to /api/core/tasks/start referencing InstallPlugin, UnInstallPlugin, or UpgradeAll from non-administrator sessions.
- Unexpected pip install or pip uninstall invocations spawned by the Ajenti process running as root.
- New or unexpected packages under the ajenti.plugin.* namespace in the Python site-packages directory.
- Outbound network connections from the Ajenti host to pypi.org or attacker-controlled package indexes.
Detection Strategies
- Correlate Ajenti authentication logs with task queue events to identify low-privileged users triggering plugin lifecycle tasks.
- Alert on subprocess executions where the parent is the Ajenti worker and the command line contains pip install or pip uninstall.
- Baseline the installed Python package inventory and flag additions between scheduled maintenance windows.
Monitoring Recommendations
- Forward Ajenti application logs and Linux audit logs to a central analytics platform for correlation.
- Enable process auditing (auditd or equivalent) for executions of pip, python, and sys.executable originating from web service accounts.
- Monitor egress traffic from admin panel hosts to package repositories and restrict it to approved mirrors.
How to Mitigate CVE-2026-79920
Immediate Actions Required
- Upgrade Ajenti to version 2.2.16 or later on every affected host.
- Rotate credentials for all Ajenti user accounts and audit recent task queue activity.
- Review installed Python packages under ajenti.plugin.* and remove any unauthorized entries.
- Restrict network access to the Ajenti web interface to trusted management networks.
Patch Information
The fix is available in Ajenti v2.2.16. Two commits address the vulnerability: the permission enforcement commit introduces a PermissionProvider and calls authorize before executing plugin management tasks, and the input validation commit adds regex checks for the name and version fields. See the GitHub Security Advisory GHSA-j8xf-5fw2-f99q for full details.
Workarounds
- Disable or remove the plugins core module until upgrading is possible.
- Limit Ajenti account creation and remove non-administrative accounts that do not require access.
- Place the admin panel behind a reverse proxy that enforces IP allowlists and multi-factor authentication.
# Upgrade Ajenti to the patched release
pip install --upgrade 'ajenti-panel>=2.2.16'
systemctl restart ajenti
# Verify the installed version
ajenti-panel --version
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

