diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/BlindSendFileAssignment.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/BlindSendFileAssignment.java index 1896cad1f..3214757c6 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/BlindSendFileAssignment.java +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/BlindSendFileAssignment.java @@ -6,6 +6,8 @@ import org.apache.commons.lang.exception.ExceptionUtils; import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentPath; import org.owasp.webgoat.assignments.AttackResult; +import org.owasp.webgoat.session.WebSession; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.http.MediaType; @@ -51,11 +53,15 @@ import java.util.List; * @version $Id: $Id * @since November 18, 2016 */ -@AssignmentPath("XXE/blind") +@AssignmentPath("xxe/blind") public class BlindSendFileAssignment extends AssignmentEndpoint { @Value("${webgoat.user.directory}") private String webGoatHomeDirectory; + @Autowired + private WebSession webSession; + @Autowired + private Comments comments; @PostConstruct @SneakyThrows @@ -70,10 +76,11 @@ public class BlindSendFileAssignment extends AssignmentEndpoint { @RequestMapping(method = RequestMethod.POST, consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody - public AttackResult createNewUser(@RequestBody String userInfo) throws Exception { + public AttackResult createNewUser(@RequestBody String commentStr) throws Exception { String error = "Parsing successful contents not send to server"; try { - //parseXml(userInfo); + Comment comment = comments.parseXml(commentStr); + comments.addComment(comment, false); } catch (Exception e) { error = ExceptionUtils.getFullStackTrace(e); } @@ -83,9 +90,9 @@ public class BlindSendFileAssignment extends AssignmentEndpoint { boolean solved = lines.stream().filter(l -> l.contains("WebGoat 8 rocks...")).findFirst().isPresent(); logFile.delete(); if (solved) { - return 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 { - return failed().output(error).build(); + return trackProgress(failed().output(error).build()); } } diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/CommentsEndpoint.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/CommentsEndpoint.java new file mode 100644 index 000000000..a46326f44 --- /dev/null +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/CommentsEndpoint.java @@ -0,0 +1,30 @@ +package org.owasp.webgoat.plugin; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collection; + +import static org.springframework.web.bind.annotation.RequestMethod.GET; + +/** + * @author nbaars + * @since 5/4/17. + */ +@RestController +@RequestMapping("xxe/comments") +public class CommentsEndpoint { + + @Autowired + private Comments comments; + + @RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public Collection retrieveComments() { + return comments.getComments(); + } + +} diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/ContentTypeAssignment.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/ContentTypeAssignment.java index be850944e..96d470c87 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/ContentTypeAssignment.java +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/ContentTypeAssignment.java @@ -1,14 +1,17 @@ package org.owasp.webgoat.plugin; -import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.exec.OS; import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentHints; import org.owasp.webgoat.assignments.AssignmentPath; import org.owasp.webgoat.assignments.AttackResult; +import org.owasp.webgoat.session.WebSession; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; -import java.io.IOException; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; /** * ************************************************************************************************ @@ -39,37 +42,56 @@ import java.io.IOException; * @version $Id: $Id * @since November 17, 2016 */ -@AssignmentPath("XXE/content-type") +@AssignmentPath("xxe/content-type") @AssignmentHints({"xxe.hints.content.type.xxe.1", "xxe.hints.content.type.xxe.2"}) public class ContentTypeAssignment extends AssignmentEndpoint { + private final static String[] DEFAULT_LINUX_DIRECTORIES = {"usr", "opt", "var"}; + private final static String[] DEFAULT_WINDOWS_DIRECTORIES = {"Windows", "Program Files (x86)", "Program Files"}; + + + @Value("${webgoat.server.directory}") + private String webGoatHomeDirectory; + @Autowired + private WebSession webSession; + @Autowired + private Comments comments; + @RequestMapping(method = RequestMethod.POST, consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody - public AttackResult createNewUser(@RequestBody String userInfo, @RequestHeader("Content-Type") String contentType) throws Exception { - User user = new User(); + public AttackResult createNewUser(@RequestBody String commentStr, @RequestHeader("Content-Type") String contentType) throws Exception { AttackResult attackResult = failed().build(); - if (MediaType.APPLICATION_JSON_VALUE.equals(contentType)) { - user = parseJson(userInfo); + Comment comment = null; + if (APPLICATION_JSON_VALUE.equals(contentType)) { + comment = comments.parseJson(commentStr); + comments.addComment(comment, true); attackResult = failed().feedback("xxe.content.type.feedback.json").build(); } + if (MediaType.APPLICATION_XML_VALUE.equals(contentType)) { - // user = parseXml(userInfo); - attackResult = failed().feedback("xxe.content.type.feedback.xml").build(); + String error = ""; + try { + comment = comments.parseXml(commentStr); + comments.addComment(comment, false); + } catch (Exception e) { + error = org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(e); + } + attackResult = failed().feedback("xxe.content.type.feedback.xml").output(error).build(); } -// if (checkSolution(user)) { -// attackResult = success().output("xxe.content.output").outputArgs(user.getUsername()).build(); -// } - return attackResult; + if (checkSolution(comment)) { + attackResult = success().build(); + } + return trackProgress(attackResult); } - private User parseJson(String userInfo) { - ObjectMapper mapper = new ObjectMapper(); - try { - return mapper.readValue(userInfo, User.class); - } catch (IOException e) { - return new User(); + private boolean checkSolution(Comment comment) { + String[] directoriesToCheck = OS.isFamilyUnix() ? DEFAULT_LINUX_DIRECTORIES : DEFAULT_WINDOWS_DIRECTORIES; + boolean success = true; + for (String directory : directoriesToCheck) { + success &= comment.getText().contains(directory); } + return success; } } diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/Ping.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/Ping.java index 6ef28f863..ec37961f7 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/Ping.java +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/Ping.java @@ -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.txt"); + File logFile = new File(webGoatHomeDirectory, "/XXE/log.tdxt"); try { try (PrintWriter pw = new PrintWriter(logFile)) { pw.println(logLine); diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/SimpleXXE.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/SimpleXXE.java index d5c6b6a82..f1f5bdfc3 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/SimpleXXE.java +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/SimpleXXE.java @@ -1,24 +1,20 @@ package org.owasp.webgoat.plugin; import org.apache.commons.exec.OS; +import org.apache.commons.lang.exception.ExceptionUtils; import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentHints; import org.owasp.webgoat.assignments.AssignmentPath; import org.owasp.webgoat.assignments.AttackResult; -import org.owasp.webgoat.session.WebSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; -import java.util.Collection; - import static org.springframework.http.MediaType.ALL_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; -import static org.springframework.web.bind.annotation.RequestMethod.GET; import static org.springframework.web.bind.annotation.RequestMethod.POST; /** @@ -65,35 +61,22 @@ public class SimpleXXE extends AssignmentEndpoint { @Value("${webgoat.server.directory}") private String webGoatHomeDirectory; @Autowired - private WebSession webSession; - @Autowired private Comments comments; - @RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE) - @ResponseBody - public Collection retrieveComments() { - return comments.getComments(); - } - @RequestMapping(method = POST, consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) @ResponseBody public AttackResult createNewComment(@RequestBody String commentStr, @RequestHeader("Content-Type") String contentType) throws Exception { - Comment comment = null; - if (APPLICATION_JSON_VALUE.equals(contentType)) { - comment = comments.parseJson(commentStr); - comments.addComment(comment, true); - } - if (MediaType.APPLICATION_XML_VALUE.equals(contentType)) { - //Do not show these comments to all users - comment = comments.parseXml(commentStr); + String error = ""; + try { + Comment comment = comments.parseXml(commentStr); comments.addComment(comment, false); + if (checkSolution(comment)) { + return trackProgress(success().build()); + } + } catch (Exception e) { + error = ExceptionUtils.getFullStackTrace(e); } - if (checkSolution(comment)) { - return trackProgress(success() - .output("xxe.simple.output") - .outputArgs(webSession.getUserName()).build()); - } - return trackProgress(failed().build()); + return trackProgress(failed().output(error).build()); } private boolean checkSolution(Comment comment) { diff --git a/webgoat-lessons/xxe/src/main/resources/html/XXE.html b/webgoat-lessons/xxe/src/main/resources/html/XXE.html index 7d79b58d3..2ad34cf5d 100644 --- a/webgoat-lessons/xxe/src/main/resources/html/XXE.html +++ b/webgoat-lessons/xxe/src/main/resources/html/XXE.html @@ -48,13 +48,13 @@ @@ -68,111 +68,112 @@
- -
- - - -
-
- Registration form - - - - - - - - - - - - - - - - - - -
Username
E-mail
Password
-
-
- -
-
+
+
+
+
+ user profile image +
+
+
+ John Doe + uploaded a photo. +
+
24 days ago
+
+
+ +
+ image post +
+ +
+ +
+
- +
+ +
+
+
- -
- -
- -
- - - -
-
- Registration form - - - - - - - - - - - - - - - - - - -
Username
E-mail
Password
-
-
- -
-
+
+
+
+
+ user profile image +
+
+
+ John Doe + uploaded a photo. +
+
24 days ago
+
+
+ +
+ image post +
+ +
+ +
+
- +
+ +
+
+
- -
diff --git a/webgoat-lessons/xxe/src/main/resources/images/avatar1.png b/webgoat-lessons/xxe/src/main/resources/images/avatar1.png new file mode 100644 index 000000000..4ea864f90 Binary files /dev/null and b/webgoat-lessons/xxe/src/main/resources/images/avatar1.png differ diff --git a/webgoat-lessons/xxe/src/main/resources/js/xxe.js b/webgoat-lessons/xxe/src/main/resources/js/xxe.js index 9002de2e4..263413f3e 100644 --- a/webgoat-lessons/xxe/src/main/resources/js/xxe.js +++ b/webgoat-lessons/xxe/src/main/resources/js/xxe.js @@ -1,22 +1,73 @@ $(document).ready(function () { - $("#postComment").unbind(); - $("#postComment").on("click", function () { - var commentInput = $("#commentInput").val(); + $("#postCommentSimple").unbind(); + $("#postCommentSimple").on("click", function () { + var commentInput = $("#commentInputSimple").val(); + var xml = '' + + '' + + ' ' + commentInput + '' + + ''; $.ajax({ type: 'POST', url: 'xxe/simple', - data: JSON.stringify({text: commentInput}), - contentType: "application/json", - dataType: 'json' + data: xml, + contentType: "application/xml", + dataType: 'xml', + complete: function (data) { + $("#commentInputSimple").val(''); + getComments('#commentsListSimple') + } + }) + }); + getComments('#commentsListSimple'); +}); + +$(document).ready(function () { + $("#postCommentBlind").unbind(); + $("#postCommentBlind").on("click", function () { + var commentInput = $("#commentInput").val(); + var xml = '' + + '' + + ' ' + commentInput + '' + + ''; + $.ajax({ + type: 'POST', + url: 'xxe/blind', + data: xml, + contentType: "application/xml", + dataType: 'xml' }).then( function () { - getComments(); + getComments('#commentsListBlind'); $("#commentInput").val(''); } ) - }) + }); + getComments('#commentsListBlind'); +}); + +$(document).ready(function () { + $("#postCommentContentType").unbind(); + $("#postCommentContentType").on("click", function () { + var commentInput = $("#commentInputContentType").val(); + $.ajax({ + type: 'POST', + url: 'xxe/content-type', + data: JSON.stringify({text: commentInput}), + contentType: "application/json", + dataType: 'xml' + }).then( + function () { + getComments('#commentsListContentType'); + $("#commentInputContentType").val(''); + } + ) + }); + getComments('#commentsListContentType'); +}); + +$(document).ready(function () { getComments(); -}) +}); var html = '
  • ' + '
    ' + @@ -31,15 +82,15 @@ var html = '
  • ' + '
  • ' + ''; -function getComments() { - $.get("xxe/simple", function (result, status) { - $("#comments_list").empty(); +function getComments(field) { + $.get("xxe/comments", function (result, status) { + $(field).empty(); for (var i = 0; i < result.length; i++) { var comment = html.replace('USER', result[i].user); comment = comment.replace('DATETIME', result[i].dateTime); comment = comment.replace('COMMENT', result[i].text); - $("#comments_list").append(comment); + $(field).append(comment); } }); -} \ No newline at end of file +} diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind.adoc b/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind.adoc index cd615ee26..0d7e110e4 100644 --- a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind.adoc +++ b/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind.adoc @@ -8,11 +8,11 @@ Our WebGoat server by default has an /xxe/ping endpoint which we can use. *This [source] ---- -curl -i http://localhost:8080/WebGoat/XXE/ping +curl -i http://localhost:8080/WebGoat/XXE/ping?text=HelloWorld will result in: -GET curl/7.45.0 +GET curl/7.45.0 HelloWorld ---- at the server side. @@ -33,12 +33,12 @@ Now submit the form and change the xml to: ---- + %remote; ]> - - test&ping; - + + test&ping; + ---- Now if we check our server log we will see: @@ -48,7 +48,8 @@ Now if we check our server log we will see: GET Java/1.8.0_101 HelloWorld ---- -So with the XXE we are able to ping our own server which means XXE injection is possible. +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. [NOTE] In this case we use http://localhost:8080/WebGoat/plugin_lessons/XXE/test.dtd to fetch the dtd but in reality this will