Lucene search
+L

Apache Zeppelin CVE-2024-31866 detection

🗓️ 21 Jul 2026 12:37:43Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com

Interpreter classpath env injection in Apache Zeppelin.

Related
Refs
Code
ReporterTitlePublishedViews
Family
cnnvd
CNNVD
Apache Zeppelin 安全漏洞
9 Apr 202400:00
–cnnvd
cnvd
CNVD
Apache Zeppelin Code Execution Vulnerability
11 Apr 202400:00
–cnvd
cve
CVE
CVE-2024-31866
9 Apr 202416:09
–cve
cvelist
Cvelist
CVE-2024-31866 Apache Zeppelin: Interpreter download command does not escape malicious code injection
9 Apr 202416:09
–cvelist
euvd
EUVD
EUVD-2024-1153
3 Oct 202520:07
–euvd
github
Github Security Blog
Improper escaping in Apache Zeppelin
9 Apr 202418:30
–github
mend
Mend
CVE-2024-31866
9 Apr 202416:09
–mend
nvd
NVD
CVE-2024-31866
9 Apr 202416:15
–nvd
osv
OSV
GHSA-86JX-WR74-XR74 Improper escaping in Apache Zeppelin
9 Apr 202418:30
–osv
ptsecurity
Positive Technologies
PT-2024-24254 · Apache · Apache Zeppelin
9 Apr 202400:00
–ptsecurity
Rows per page
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from typing import Tuple

from kittysploit import *
from lib.protocols.http.http_client import Http_client
from lib.scanner.http.response_validation import parse_json_response

_AFFECTED_MIN = "0.8.2"
_FIXED_VERSION = "0.11.1"


class Module(Scanner, Http_client):
    __info__ = {
        "name": "Apache Zeppelin CVE-2024-31866 detection",
        "description": (
            "Fingerprints Apache Zeppelin via /api/version and flags versions "
            f">= {_AFFECTED_MIN} and < {_FIXED_VERSION} potentially affected by "
            "CVE-2024-31866 (interpreter classpath env injection). "
            "Version check only — no exploitation."
        ),
        "author": ["KittySploit Team"],
        "severity": "critical",
        "cve": "CVE-2024-31866",
        "references": [
            "https://nvd.nist.gov/vuln/detail/CVE-2024-31866",
            "https://lists.apache.org/thread/jpkbq3oktopt34x2n5wnhzc2r1410ddd",
            "https://github.com/apache/zeppelin/pull/4715",
        ],
        "modules": [],
        "tags": [
            "web",
            "scanner",
            "zeppelin",
            "apache",
            "version",
            "cve-2024-31866",
        ],
        "agent": {
            "risk": "active",
            "effects": ["network_probe"],
            "expected_requests": 1,
            "reversible": True,
            "approval_required": False,
            "produces": ["tech_hints", "risk_signals", "endpoints"],
            "cost": 1.0,
            "noise": 0.2,
            "value": 1.0,
            "requires": {
                "min_endpoints": 0,
                "min_params": 0,
                "tech_hints_any": [],
                "tech_hints_all": [],
                "specializations_any": [],
                "risk_signals_any": [],
                "auth_session": False,
                "capabilities_any": [],
                "capabilities_all": [],
                "confidence_min": {},
                "confidence_min_any": {},
                "endpoint_pattern_any": ["/api/version"],
                "param_any": [],
                "api_surface_ready": False,
            },
            "chain": {
                "produces_capabilities": [],
                "consumes_capabilities": [],
                "option_bindings": {},
                "suggested_followups": [],
            },
        },
    }

    port = OptPort(8080, "Zeppelin HTTP port", True)
    ssl = OptBool(False, "Use HTTPS", True, advanced=True)
    base_path = OptString("/", "URL path prefix if Zeppelin is not at site root", required=False)

    @staticmethod
    def _version_tuple(value: str) -> Tuple[int, ...]:
        parts = []
        for token in str(value or "").split("."):
            digits = "".join(ch for ch in token if ch.isdigit())
            parts.append(int(digits) if digits else 0)
        while len(parts) < 3:
            parts.append(0)
        return tuple(parts[:3])

    def _normalize_base(self) -> str:
        path = str(self.base_path or "/").strip() or "/"
        if not path.startswith("/"):
            path = "/" + path
        return path.rstrip("/")

    def _path(self, suffix: str) -> str:
        base = self._normalize_base()
        if not suffix.startswith("/"):
            suffix = "/" + suffix
        return f"{base}{suffix}" if base else suffix

    def _is_vulnerable(self, version: str) -> bool:
        current = self._version_tuple(version)
        return (
            current >= self._version_tuple(_AFFECTED_MIN)
            and current < self._version_tuple(_FIXED_VERSION)
        )

    def _extract_version(self, data: dict) -> str:
        body = data.get("body")
        if isinstance(body, dict):
            version = str(body.get("version") or "").strip()
            if version:
                return version
        for key in ("version", "zeppelinVersion"):
            version = str(data.get(key) or "").strip()
            if version:
                return version
        return ""

    def run(self):
        try:
            timeout = max(int(self.timeout or 10), 10)
            response = self.http_request(
                method="GET",
                path=self._path("/api/version"),
                allow_redirects=True,
                timeout=timeout,
            )
            data, err = parse_json_response(response)
            if err or not data:
                return False

            version = self._extract_version(data)
            if not version:
                status = str(data.get("status") or "").upper()
                if status and status != "OK":
                    return False
                self.set_info(
                    severity="info",
                    cve="CVE-2024-31866",
                    reason="Apache Zeppelin API detected but version could not be extracted",
                    path=self._path("/api/version"),
                )
                return True

            print_success(f"Apache Zeppelin {version} detected")
            if self._is_vulnerable(version):
                self.set_info(
                    severity="critical",
                    cve="CVE-2024-31866",
                    reason=(
                        f"Apache Zeppelin {version} is in the CVE-2024-31866 range "
                        f"(>= {_AFFECTED_MIN}, < {_FIXED_VERSION})"
                    ),
                    version=version,
                    confidence="high",
                    path=self._path("/api/version"),
                )
                return True

            self.set_info(
                severity="info",
                reason=(
                    f"Apache Zeppelin {version} detected; outside CVE-2024-31866 "
                    f"affected range (fixed in {_FIXED_VERSION}+)"
                ),
                version=version,
                path=self._path("/api/version"),
            )
            return False
        except Exception as exc:
            print_error(f"Scanner failed: {exc}")
            return False

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

21 Jul 2026 12:37Current
8.4High risk
Vulners AI Score8.4
CVSS 3.19.8
EPSS0.01427
SSVC
0