Changed XXE lessons to use photo comment example

This commit is contained in:
Nanne Baars 2017-05-04 06:25:11 +02:00
parent 05f6fb226f
commit 6f0f71b131
9 changed files with 250 additions and 155 deletions

View File

@ -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());
}
}

View File

@ -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<Comment> retrieveComments() {
return comments.getComments();
}
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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<Comment> 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) {

View File

@ -48,13 +48,13 @@
</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="commentInputSimple" placeholder="Add a comment" type="text"/>
<span class="input-group-addon">
<i id="postComment" class="fa fa-edit" style="font-size: 20px"></i>
<i id="postCommentSimple" class="fa fa-edit" style="font-size: 20px"></i>
</span>
</div>
<ul class="comments-list">
<div id="comments_list">
<div id="commentsListSimple">
</div>
</ul>
</div>
@ -68,111 +68,112 @@
</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_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>
<!-- using attack-form class on your form will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework -->
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
<form class="attack-form" accept-charset="UNKNOWN" prepareData="registerJson" method="POST" name="form"
action="/WebGoat/XXE/content-type" contentType="application/json">
<div id="lessonContent">
<strong>Registration form</strong>
<form prepareData="registerJson" accept-charset="UNKNOWN" method="POST" name="form" action="#attack/307/100">
<table>
<tr>
<td>Username</td>
<td><input name="username" value="" type="TEXT"/></td>
</tr>
<tr>
<td>E-mail</td>
<td><input name="email" value="" type="TEXT"/></td>
</tr>
<tr>
<td>Password</td>
<td><input name="email" value="" type="TEXT"/></td>
</tr>
<tr>
<td></td>
<td align="right"><input type="submit" value="Sign up"/></td>
</tr>
</table>
<br/>
<br/>
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
<div class="container-fluid">
<div class="panel post">
<div class="post-heading">
<div class="pull-left image">
<img th:src="@{/images/avatar1.png}"
class="img-circle avatar" alt="user profile image"/>
</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>
<div class="post-image">
<img th:src="@{images/cat.jpg}" class="image" alt="image post"/>
</div>
<div class="post-description">
</div>
<div class="post-footer">
<div class="input-group">
<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>
</div>
<ul class="comments-list">
<div id="commentsListContentType">
</div>
</ul>
</div>
</div>
</form>
</div>
<br/>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</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_overflow.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_blind.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_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>
<!-- using attack-form class on your form will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework -->
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
<form class="attack-form" accept-charset="UNKNOWN" prepareData="register" method="POST" name="form"
action="/WebGoat/XXE/blind" contentType="application/xml">
<div id="lessonContent">
<strong>Registration form</strong>
<form prepareData="register" accept-charset="UNKNOWN" method="POST" name="form" action="#attack/307/100">
<table>
<tr>
<td>Username</td>
<td><input name="username" value="" type="TEXT"/></td>
</tr>
<tr>
<td>E-mail</td>
<td><input name="email" value="" type="TEXT"/></td>
</tr>
<tr>
<td>Password</td>
<td><input name="email" value="" type="TEXT"/></td>
</tr>
<tr>
<td></td>
<td align="right"><input type="submit" value="Sign up"/></td>
</tr>
</table>
<br/>
<br/>
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
<div class="container-fluid">
<div class="panel post">
<div class="post-heading">
<div class="pull-left image">
<img th:src="@{/images/avatar1.png}"
class="img-circle avatar" alt="user profile image"/>
</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>
<div class="post-image">
<img th:src="@{images/cat.jpg}" class="image" alt="image post"/>
</div>
<div class="post-description">
</div>
<div class="post-footer">
<div class="input-group">
<input class="form-control" id="commentInput" placeholder="Add a comment" type="text"/>
<span class="input-group-addon">
<i id="postCommentBlind" class="fa fa-edit" style="font-size: 20px"></i>
</span>
</div>
<ul class="comments-list">
<div id="commentsListBlind">
</div>
</ul>
</div>
</div>
</form>
</div>
<br/>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</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_mitigation.adoc"></div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -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 = '<?xml version="1.0"?>' +
'<comment>' +
' <text>' + commentInput + '</text>' +
'</comment>';
$.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 = '<?xml version="1.0"?>' +
'<comment>' +
' <text>' + commentInput + '</text>' +
'</comment>';
$.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 = '<li class="comment">' +
'<div class="pull-left">' +
@ -31,15 +82,15 @@ var html = '<li class="comment">' +
'</div>' +
'</li>';
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);
}
});
}
}

View File

@ -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:
----
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://localhost:8080/WebGoat/plugin_lessons/XXE/attack.dtd">
<!ENTITY % remote SYSTEM "http://localhost:8080/WebGoat/XXE/attack.dtd">
%remote;
]>
<user>
<username>test&ping;</username>
</user>
<comment>
<text>test&ping;</text>
</comment>
----
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