SSRF and RCE in Apache Batlik due to Improper Parsing of `jar:` URI

We discussed this vulnerability during Episode 165 on 07 November 2022

A somewhat simple security control bypass in Apache Batik’s DefaultScriptSecurity and DefaultExternalResourceSecurity controls. Where Batik has to be able to load SVG files (and associated resources) from either a local or remote source, it makes for an interesting attack for SSRF and/or RCE. Of course, it has the earlier mentioned security controls in place to prevent an attacker from just loading a resource remotely from their own server. If you load a local SVG file, it can load local scripts and resources but not remote ones. If you load a remote SVG file, you can load scripts over HTTP or any other supported protocol, but only from the same host. The problem is, DefaultScriptSecurity and DefaultExternalResourceSecurity relied on Java’s getHost() method to check if the document and script URLs matched.

One of the supported protocols is Java Archives (JARs). Unfortunately, getHost() will always return null for JAR URLs. In order to resolve the host properly, you have to call getFile() and call getHost() on the returned object from getFile(). This essentially gives an attacker the ability to load JAR files from an arbitrary remote host. This can be used in the form of an SSRF to send requests or NTLM relays to an attacker server, or RCE.

RCE was a bit interesting as the straight-forward route of just providing a malicious Java class was blocked due to a bug in Batik’s script type allow list. But regardless, ECMAScript was supported via Mozilla’s Rhino, which has a known code execution vector by abusing string concetenation to an eval() and using a string() constructor that uses java.lang.Runtime.getRuntime().exec() to run an arbitrary shell command.