Lucene search
+L

Acronis Backup CVE-2026-87886 Detect

🗓️ 29 Sep 2026 10:07:34Reported by KittySploitType 
kittysploit
 kittysploit
🔗 github.com👁 18 Views

Insecure file permissions in Acronis Backup plugin enable local privilege escalation.

Related
Refs
Code
ReporterTitlePublishedViews
Family
acronis
Acronis
Local privilege escalation due to insecure file permissions
17 Sep 202613:30
–acronis
bdu_fstec
BDU FSTEC
Acronis Backup RCE
17 Sep 202600:00
–bdu_fstec
circl
Circl
CVE-2026-87886
16 Sep 202602:27
–circl
cisa_kev
CISA KEV Catalog
Acronis Backup Incorrect Default Permissions Vulnerability
16 Sep 202600:00
–cisa_kev
cisa
CISA
CISA Adds Two Known Exploited Vulnerabilities to Catalog
16 Sep 202612:00
–cisa
cve
CVE
CVE-2026-87886
17 Sep 202622:57
–cve
cvelist
Cvelist
CVE-2026-87886
17 Sep 202622:57
–cvelist
euvd
EUVD
EUVD-2026-82595
18 Sep 202600:31
–euvd
nvd
NVD
CVE-2026-87886
17 Sep 202623:18
–nvd
ptsecurity
Positive Technologies
PT-2026-93254
11 Sep 202600:00
–ptsecurity
Rows per page
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Detect Acronis Backup plugin CVE-2026-87886 exposure on hosting panels."""

import re

from kittysploit import *
from lib.protocols.http.http_client import Http_client


class Module(Scanner, Http_client):
    __info__ = {
        "name": "Acronis Backup CVE-2026-87886 Detect",
        "description": (
            "Detects Acronis Backup integrations on cPanel/WHM, Plesk, or "
            "DirectAdmin that may be affected by CVE-2026-87886 (insecure file "
            "permissions enabling local privilege escalation). Fingerprints the "
            "plugin surface and parses build numbers against fixed releases "
            "(cPanel < 1.9.3.1021, Plesk < 1.8.11.638, DirectAdmin < 1.2.3.238)."
        ),
        "author": ["KittySploit Team"],
        "severity": "high",
        "cve": ["CVE-2026-87886"],
        "references": [
            "https://nvd.nist.gov/vuln/detail/CVE-2026-87886",
            "https://security-advisory.acronis.com/advisories/SEC-10986",
        ],
        "tags": [
            "web",
            "scanner",
            "acronis",
            "cpanel",
            "whm",
            "plesk",
            "directadmin",
            "hosting",
            "lpe",
            "cve-2026-87886",
            "kev",
            "vuln",
        ],
        "agent": {
            "risk": "active",
            "effects": ["network_probe"],
            "expected_requests": 6,
            "reversible": True,
            "approval_required": False,
            "produces": ["tech_hints", "risk_signals"],
            "cost": 1.0,
            "noise": 0.15,
            "value": 1.0,
            "requires": {
                "tech_hints_any": ["cpanel", "whm", "plesk", "directadmin", "hosting"],
            },
            "chain": {
                "produces_capabilities": [
                    {"capability": "lpe_surface", "from_detail": "Acronis plugin build"},
                ],
                "suggested_followups": [
                    "scanner/http/whm_login_detect",
                    "scanner/http/plesk_onyx_detect",
                    "scanner/http/directadmin_detect",
                ],
            },
        },
    }

    _BUILD_RE = re.compile(
        r"(?:build|version|release)[^\d]{0,24}(\d+\.\d+\.\d+(?:\.\d+)?)",
        re.IGNORECASE,
    )
    _FIXED = {
        "cpanel": (1, 9, 3, 1021),
        "plesk": (1, 8, 11, 638),
        "directadmin": (1, 2, 3, 238),
    }
    _PROBE_PATHS = (
        ("/cgi/AcronisBackup/index.cgi", "cpanel"),
        ("/3rdparty/acronis/", "cpanel"),
        ("/cgi/acronisbackup/", "cpanel"),
        ("/modules/acronis-backup/", "plesk"),
        ("/smb/web/view/ext/acronis-backup/", "plesk"),
        ("/modules/acronis/", "plesk"),
        ("/CMD_PLUGIN?plugin=acronis", "directadmin"),
        ("/CMD_PLUGINS/acronis/", "directadmin"),
    )

    @staticmethod
    def _parse_build(text: str) -> str:
        match = Module._BUILD_RE.search(text or "")
        return match.group(1) if match else ""

    @staticmethod
    def _build_tuple(build: str) -> tuple[int, ...]:
        parts = re.findall(r"\d+", build or "")
        return tuple(int(p) for p in parts)

    @staticmethod
    def _build_vulnerable(build: str, platform: str) -> bool | None:
        fixed = Module._FIXED.get(platform)
        if not fixed or not build:
            return None
        parsed = Module._build_tuple(build)
        if len(parsed) < len(fixed):
            return None
        return parsed[: len(fixed)] < fixed

    def _panel_kind(self) -> str:
        for path, _ in (("/", "whm"), ("/login_up.php", "plesk"), ("/", "directadmin")):
            response = self.http_request(method="GET", path=path, allow_redirects=False)
            if not response:
                continue
            body = (response.text or "").lower()
            if "whm login" in body or "cpanel" in body:
                return "cpanel"
            if "plesk" in body:
                return "plesk"
            if "directadmin login" in body:
                return "directadmin"
        return ""

    def run(self):
        panel = self._panel_kind()
        acronis_hit = ""
        build = ""
        platform = ""

        for path, hint in self._PROBE_PATHS:
            if panel and hint != panel and panel != "cpanel" and hint == "cpanel":
                continue
            response = self.http_request(method="GET", path=path, allow_redirects=True)
            if not response or int(response.status_code or 0) not in (200, 301, 302, 403):
                continue
            body = response.text or ""
            blob = body.lower()
            if "acronis" not in blob and "acronis" not in (response.headers or {}).get(
                "Server", ""
            ).lower():
                continue
            acronis_hit = path
            platform = hint
            build = self._parse_build(body)
            break

        if not acronis_hit:
            for path in ("/", "/login_up.php"):
                response = self.http_request(method="GET", path=path, allow_redirects=True)
                if not response:
                    continue
                body = response.text or ""
                if "acronis" not in body.lower():
                    continue
                acronis_hit = path
                platform = panel or "unknown"
                build = self._parse_build(body)
                break

        if not acronis_hit:
            return False

        affected = self._build_vulnerable(build, platform) if platform in self._FIXED else None
        if affected is False:
            return False

        reason = (
            f"Acronis Backup plugin detected on {platform or 'hosting panel'}"
            + (f" (build {build} appears pre-patch)" if affected else "")
            + "; CVE-2026-87886 requires local low-privilege access to exploit"
        )
        self.set_info(
            severity="high" if affected else "medium",
            cve="CVE-2026-87886",
            reason=reason,
            platform=platform or "unknown",
            build=build or "unknown",
            path=acronis_hit,
        )
        return True

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

29 Sep 2026 10:07Current
7.8High risk
Vulners AI Score7.8
CVSS 37.8
EPSS0.00233
SSVC
18