initial cut on XSS, need to add some tests still
This commit is contained in:
parent
05f6fb226f
commit
feead6b740
@ -54,21 +54,24 @@ define(['jquery',
|
||||
webgoat.customjs.jquery = $; //passing jquery into custom js scope ... still klunky, but works for now
|
||||
webgoat.customjs.jqueryVuln = $vuln;
|
||||
|
||||
// temporary shim to support dom-xss assignment
|
||||
// shim to support xss lesson
|
||||
webgoat.customjs.phoneHome = function (e) {
|
||||
console.log('phoneHome invoked');
|
||||
console.log(arguments.callee);
|
||||
//
|
||||
webgoat.customjs.jquery.ajax({
|
||||
method: "POST",
|
||||
url: "/WebGoat/CrossSiteScripting/dom-xss",
|
||||
url: "/WebGoat/CrossSiteScripting/phone-home-xss",
|
||||
data: {param1: 42, param2: 24},
|
||||
headers: {
|
||||
"webgoat-requested-by": "dom-xss-vuln"
|
||||
},
|
||||
contentType: 'application/x-www-form-urlencoded; charset=UTF-8'
|
||||
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
success: function (data) {
|
||||
//devs leave stuff like this in all the time
|
||||
console.log('phone home said ' + data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
|
@ -0,0 +1,24 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 4/8/17.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@XmlRootElement
|
||||
public class Comment {
|
||||
private String user;
|
||||
private String dateTime;
|
||||
private String text;
|
||||
}
|
||||
|
@ -58,8 +58,7 @@ public class CrossSiteScriptingLesson5a extends AssignmentEndpoint {
|
||||
@RequestParam Integer QTY4, @RequestParam String field1,
|
||||
@RequestParam Integer field2, HttpServletRequest request)
|
||||
throws IOException {
|
||||
// System.out.println("foo");
|
||||
// Should add some QTY validation here. Someone could have fun and enter a negative quantity and get merchanidise and a refund :)
|
||||
|
||||
double totalSale = QTY1.intValue() * 69.99 + QTY2.intValue() * 27.99 + QTY3.intValue() * 1599.99 + QTY4.intValue() * 299.99;
|
||||
|
||||
userSessionData.setValue("xss-reflected1-complete",(Object)"false");
|
||||
|
@ -3,6 +3,7 @@ package org.owasp.webgoat.plugin;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.owasp.webgoat.session.UserSessionData;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
@ -10,27 +11,27 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* Created by jason on 11/23/16.
|
||||
*/
|
||||
@AssignmentPath("/CrossSiteScripting/dom-xss")
|
||||
|
||||
@AssignmentPath("/CrossSiteScripting/phone-home-xss")
|
||||
public class DOMCrossSiteScripting extends AssignmentEndpoint {
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
public @ResponseBody
|
||||
AttackResult completed(@RequestParam Integer param1,
|
||||
@RequestParam Integer param2, HttpServletRequest request) throws IOException {
|
||||
|
||||
UserSessionData userSessionData = getUserSessionData();
|
||||
SecureRandom number = new SecureRandom();
|
||||
userSessionData.setValue("randValue",number.nextInt());
|
||||
|
||||
if (param1 == 42 && param2 == 24 && request.getHeader("webgoat-requested-by").equals("dom-xss-vuln")) {
|
||||
System.out.println("DOM-XSS successful, param1 is 42");
|
||||
return trackProgress(success().build());
|
||||
System.out.println(userSessionData.getValue("randValue") + " << randValue");
|
||||
return trackProgress(success().output("phoneHome Response is " + userSessionData.getValue("randValue").toString()).build());
|
||||
} else {
|
||||
return trackProgress(failed().build());
|
||||
}
|
||||
}
|
||||
}
|
||||
// something like ... http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere%3Cscript%3Ewebgoat.customjs.phoneHome();%3C%2Fscript%3E
|
||||
// something like ... http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere%3Cscript%3Ewebgoat.customjs.phoneHome();%3C%2Fscript%3E--andMoreGarbageHere
|
||||
// or http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere<script>webgoat.customjs.phoneHome();<%2Fscript>
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -3,6 +3,7 @@ package org.owasp.webgoat.plugin;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.owasp.webgoat.session.UserSessionData;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
@ -15,21 +16,19 @@ import java.io.IOException;
|
||||
* Created by jason on 11/23/16.
|
||||
*/
|
||||
@AssignmentPath("/CrossSiteScripting/dom-follow-up")
|
||||
public class DOMCrossSiteScriptingFollowUp extends AssignmentEndpoint {
|
||||
public class DOMCrossSiteScriptingVerifier extends AssignmentEndpoint {
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
public @ResponseBody
|
||||
AttackResult completed(@RequestParam String successMessage) throws IOException {
|
||||
if (successMessage.equals("DOM-XSS successful, param1 is 42")) {
|
||||
|
||||
UserSessionData userSessionData = getUserSessionData();
|
||||
|
||||
if (successMessage.equals(userSessionData.getValue("randValue").toString())) {
|
||||
return trackProgress(success().feedback("xss-dom-message-success").build());
|
||||
} else {
|
||||
return trackProgress(failed().feedback("xss-dom-message-success").build());
|
||||
return trackProgress(failed().feedback("xss-dom-message-failure").build());
|
||||
}
|
||||
}
|
||||
}
|
||||
// something like ... http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere%3Cscript%3Ewebgoat.customjs.phoneHome();%3C%2Fscript%3E
|
||||
// or http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere<script>webgoat.customjs.phoneHome();<%2Fscript>
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,33 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.owasp.webgoat.session.UserSessionData;
|
||||
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.IOException;
|
||||
|
||||
/**
|
||||
* Created by jason on 11/23/16.
|
||||
*/
|
||||
@AssignmentPath("/CrossSiteScripting/stored-xss-follow-up")
|
||||
public class StoredCrossSiteScriptingVerifier extends AssignmentEndpoint {
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
public @ResponseBody
|
||||
AttackResult completed(@RequestParam String successMessage) throws IOException {
|
||||
|
||||
UserSessionData userSessionData = getUserSessionData();
|
||||
|
||||
if (successMessage.equals(userSessionData.getValue("randValue").toString())) {
|
||||
return trackProgress(success().feedback("xss-stored-callback-success").build());
|
||||
} else {
|
||||
return trackProgress(failed().feedback("xss-stored-callback-failure").build());
|
||||
}
|
||||
}
|
||||
}
|
||||
// something like ... http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere%3Cscript%3Ewebgoat.customjs.phoneHome();%3C%2Fscript%3E
|
||||
// or http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere<script>webgoat.customjs.phoneHome();<%2Fscript>
|
@ -0,0 +1,94 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import com.beust.jcommander.internal.Lists;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.collect.EvictingQueue;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.owasp.webgoat.session.UserSessionData;
|
||||
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 static org.springframework.http.MediaType.ALL_VALUE;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||
|
||||
/**
|
||||
* Created by jason on 11/23/16.
|
||||
*/
|
||||
|
||||
@AssignmentPath("/CrossSiteScripting/stored-xss")
|
||||
public class StoredXssComments extends AssignmentEndpoint {
|
||||
|
||||
@Autowired
|
||||
private WebSession webSession;
|
||||
private static DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd, HH:mm:ss");
|
||||
|
||||
private static final Map<String, EvictingQueue<Comment>> userComments = Maps.newHashMap();
|
||||
private static final EvictingQueue<Comment> comments = EvictingQueue.create(100);
|
||||
private static final String phoneHomeString = "<script>webgoat.customjs.phoneHome()</script>";
|
||||
|
||||
static {
|
||||
comments.add(new Comment("webgoat", DateTime.now().toString(fmt), "This comment is safe"));
|
||||
comments.add(new Comment("guest", DateTime.now().toString(fmt), "This one is safe too."));
|
||||
comments.add(new Comment("guest", DateTime.now().toString(fmt), "Can you post a comment, calling webgoat.customjs.phoneHome() ?"));
|
||||
}
|
||||
|
||||
@RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE,consumes = ALL_VALUE)
|
||||
@ResponseBody
|
||||
public Collection<Comment> retrieveComments() {
|
||||
Collection<Comment> allComments = Lists.newArrayList();
|
||||
Collection<Comment> xmlComments = userComments.get(webSession.getUserName());
|
||||
if (xmlComments != null) {
|
||||
allComments.addAll(xmlComments);
|
||||
}
|
||||
allComments.addAll(comments);
|
||||
return allComments;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public AttackResult createNewComment (@RequestBody String commentStr) throws IOException {
|
||||
|
||||
Comment comment = parseJson(commentStr);
|
||||
|
||||
EvictingQueue<Comment> comments = userComments.getOrDefault(webSession.getUserName(), EvictingQueue.create(100));
|
||||
comments.add(comment);
|
||||
comment.setDateTime(DateTime.now().toString(fmt));
|
||||
comment.setUser(webSession.getUserName());
|
||||
|
||||
userComments.put(webSession.getUserName(), comments);
|
||||
|
||||
if (comment.getText().contains(phoneHomeString)) {
|
||||
return (success().feedback("xss-stored-comment-success").build());
|
||||
} else {
|
||||
return (failed().feedback("xss-stored-comment-failure").build());
|
||||
}
|
||||
}
|
||||
|
||||
private Comment parseJson(String comment) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
try {
|
||||
return mapper.readValue(comment, Comment.class);
|
||||
} catch (IOException e) {
|
||||
return new Comment();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,75 @@
|
||||
/* Component: Posts */
|
||||
.post .post-heading {
|
||||
height: 95px;
|
||||
padding: 20px 15px;
|
||||
}
|
||||
.post .post-heading .avatar {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
display: block;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.post .post-heading .meta .title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.post .post-heading .meta .title a {
|
||||
color: black;
|
||||
}
|
||||
.post .post-heading .meta .title a:hover {
|
||||
color: #aaaaaa;
|
||||
}
|
||||
.post .post-heading .meta .time {
|
||||
margin-top: 8px;
|
||||
color: #999;
|
||||
}
|
||||
.post .post-image .image {
|
||||
width:20%;
|
||||
height: 40%;
|
||||
}
|
||||
.post .post-description {
|
||||
padding: 5px;
|
||||
}
|
||||
.post .post-footer {
|
||||
border-top: 1px solid #ddd;
|
||||
padding: 15px;
|
||||
}
|
||||
.post .post-footer .input-group-addon a {
|
||||
color: #454545;
|
||||
}
|
||||
.post .post-footer .comments-list {
|
||||
padding: 0;
|
||||
margin-top: 20px;
|
||||
list-style-type: none;
|
||||
}
|
||||
.post .post-footer .comments-list .comment {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.post .post-footer .comments-list .comment .avatar {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
.post .post-footer .comments-list .comment .comment-heading {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
.post .post-footer .comments-list .comment .comment-heading .user {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
display: inline;
|
||||
margin-top: 0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.post .post-footer .comments-list .comment .comment-heading .time {
|
||||
font-size: 12px;
|
||||
color: #aaa;
|
||||
margin-top: 0;
|
||||
display: inline;
|
||||
}
|
||||
.post .post-footer .comments-list .comment .comment-body {
|
||||
margin-left: 50px;
|
||||
}
|
||||
.post .post-footer .comments-list .comment > .comments-list {
|
||||
margin-left: 50px;
|
||||
}
|
@ -166,19 +166,7 @@
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content5b.adoc"></div>
|
||||
<div class="attack-container">
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="xss-5b"
|
||||
action="/WebGoat/CrossSiteScripting/attack5b"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
<input size="4" value="" name="isReflectedXSS" type="text" />
|
||||
<input type="submit" name="reflectedXssSubmit" value="Submit"/>
|
||||
</form>
|
||||
<!-- do not remove the two following div's, this is where your feedback/output will land -->
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
<!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
@ -235,135 +223,86 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content7.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:CrossSiteScripting_content7.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:CrossSiteScripting_content8.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:CrossSiteScripting_content9.adoc"></div>-->
|
||||
<!--<img align="middle" th:src="@{/plugin_lessons/plugin/CrossSiteScripting/images/Reflected-XSS.png}" />-->
|
||||
<!--</div>-->
|
||||
<div class="lesson-page-wrapper">
|
||||
|
||||
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content7b.adoc"></div>
|
||||
|
||||
<!--<div class="lesson-page-wrapper">-->
|
||||
<!--<!– reuse this lesson-page-wrapper block for each 'page' of content in your lesson –>-->
|
||||
<!--<!– include content here. 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:CrossSiteScripting_content9a.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 –>-->
|
||||
<!-- comment area -->
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/stored-xss.css}"/>
|
||||
<script th:src="@{/lesson_js/stored-xss.js}" language="JavaScript"></script>
|
||||
|
||||
<!--<!– 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" method="POST"-->
|
||||
<!--name="form" action="/WebGoat/CrossSiteScripting/attack9a"-->
|
||||
<!--enctype="application/json;charset=UTF-8">-->
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
|
||||
<!--<table cellspacing="0" cellpadding="0" border="0">-->
|
||||
<!--<tbody>-->
|
||||
<!--<tr>-->
|
||||
<!--<td>Title:</td>-->
|
||||
<!--<td><input name="title" value="" type="TEXT" /></td>-->
|
||||
<!--</tr>-->
|
||||
<!--<tr>-->
|
||||
<!--<td valign="TOP">Message:</td>-->
|
||||
<!--<td><textarea cols="60" name="message" rows="5"></textarea></td>-->
|
||||
<!--</tr>-->
|
||||
<!--</tbody>-->
|
||||
<!--</table>-->
|
||||
<!--<p>-->
|
||||
<!--<input name="SUBMIT" value="Submit" type="SUBMIT" />-->
|
||||
<!--</p>-->
|
||||
<!--<hr />-->
|
||||
<!--<hr />-->
|
||||
<!--<h1>Message List</h1>-->
|
||||
<!--<table cellspacing="0" cellpadding="0" border="0">-->
|
||||
<!--<tbody>-->
|
||||
<!--<tr>-->
|
||||
<!--<td><a href="#" style="cursor: hand" link="attack?Num=1"><u></u></a></td>-->
|
||||
<!--</tr>-->
|
||||
<!--</tbody>-->
|
||||
<!--</table>-->
|
||||
<!--</form>-->
|
||||
<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>
|
||||
|
||||
<!--<!– do not remove the two following div's, this is where your feedback/output will land –>-->
|
||||
<!--<div class="attack-feedback"></div>-->
|
||||
<!--<div class="attack-output"></div>-->
|
||||
<!--<!– ... of course, you can move them if you want to, but that will not look consistent to other lessons –>-->
|
||||
<!--</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="postComment" class="fa fa-edit" style="font-size: 20px"></i>
|
||||
</span>
|
||||
</div>
|
||||
<ul class="comments-list">
|
||||
<div id="list">
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end comments -->
|
||||
|
||||
<!--<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:CrossSiteScripting_content10.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:CrossSiteScripting_content11.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:CrossSiteScripting_content12.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:CrossSiteScripting_content13.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:CrossSiteScripting_content13a.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:CrossSiteScripting_content14.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:CrossSiteScripting_content15.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:CrossSiteScripting_content15a.adoc"></div>-->
|
||||
<!--</div>-->
|
||||
<div class="attack-container">
|
||||
<!-- this will be where they can store the additional comment -->
|
||||
<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="DOMFollowUp"
|
||||
action="/WebGoat/CrossSiteScripting/stored-xss-follow-up"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
<input name="successMessage" value="" type="TEXT" />
|
||||
<input name="submitMessage" value="Submit" type="SUBMIT"/>
|
||||
</form>
|
||||
<!-- do not remove the two following div's, this is where your feedback/output will land -->
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
<!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<!-- overview of XSS defenses -->
|
||||
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content8.adoc"></div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<!-- links to OWASP XSS resources mainly -->
|
||||
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content9.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:CrossSiteScripting_content16.adoc"></div>-->
|
||||
<!--</div> -->
|
||||
|
||||
</html>
|
@ -8,5 +8,7 @@ xss-reflected-6a-failure=No, look at the example. Check the GoatRouter.js file.
|
||||
xss.lesson1.failure=Are you sure? Try using a tab from a different site.
|
||||
xss-dom-message-success=Correct, I hope you didn't cheat, using the console!
|
||||
xss-dom-message-failure=Incorrect, keep trying. It should be obvious in the log when you are successful.
|
||||
|
||||
#xss-reflected-5b-do5a-first=Do the reflected xss attack prior to this, then come back and answer this.
|
||||
xss-stored-comment-success=It appears your payload should invoke the function. To tell for sure, you need to capture the value and put it in the form below. Then we will really know.
|
||||
xss-stored-comment-failure=We can't see the payload in your submission, but XSS can be tricky. Look for the call back fired after the comments reload. If you see that and can put the correct value there and put it in, maybe you did succeed.
|
||||
xss-stored-callback-success=Yes, that is the correct value (note, it will be a different value each time the phoneHome endpoint is called).
|
||||
xss-stored-callback-failure=No, that is not the correct value (note, it will be a different value each time the phoneHome endpoint is called).
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
@ -0,0 +1,45 @@
|
||||
$(document).ready(function () {
|
||||
$("#postComment").on("click", function () {
|
||||
var commentInput = $("#commentInput").val();
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: 'CrossSiteScripting/stored-xss',
|
||||
data: JSON.stringify({text: commentInput}),
|
||||
contentType: "application/json",
|
||||
dataType: 'json'
|
||||
}).then(
|
||||
function () {
|
||||
getChallenges();
|
||||
$("#commentInput").val('');
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
var html = '<li class="comment">' +
|
||||
'<div class="pull-left">' +
|
||||
'<img class="avatar" src="images/avatar1.png" alt="avatar"/>' +
|
||||
'</div>' +
|
||||
'<div class="comment-body">' +
|
||||
'<div class="comment-heading">' +
|
||||
'<h4 class="user">USER</h4>' +
|
||||
'<h5 class="time">DATETIME</h5>' +
|
||||
'</div>' +
|
||||
'<p>COMMENT</p>' +
|
||||
'</div>' +
|
||||
'</li>';
|
||||
|
||||
getChallenges();
|
||||
|
||||
function getChallenges() {
|
||||
$("#list").empty();
|
||||
$.get('CrossSiteScripting/stored-xss', function (result, status) {
|
||||
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);
|
||||
$("#list").append(comment);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
})
|
@ -1,5 +1,10 @@
|
||||
== Was it Really Reflected XSS?
|
||||
== Self XSS or Reflected XSS?
|
||||
|
||||
You should have been able to execute script with the last example. Was it truly reflected XSS?
|
||||
You should have been able to execute script with the last example. At this point, it would be considered 'self XSS' though.
|
||||
|
||||
Why is that?
|
||||
|
||||
That is because there is no link that would tigger that XSS.
|
||||
You can try it yourself to see what happens ... go to (substitute localhost with your server's name or IP if you need to):
|
||||
|
||||
link: http://localhost:8080/WebGoat/CrossSiteScripting/attack5a?QTY1=1&QTY2=1&QTY3=1&QTY4=1&field1=<script>alert('my javascript here')</script>4128+3214+0002+1999&field2=111
|
||||
|
@ -1,6 +1,8 @@
|
||||
== DOM-Based XSS Scenario
|
||||
== Reflected and DOM-Based XSS
|
||||
|
||||
DOM-based XSS is another form of reflected XSS. Both are triggered by sending a link with inputs that are reflected to the browser.
|
||||
The difference between DOM and 'traditional' reflected XSS is that, with DOM, the payload will never go to the server. It will only ever be processed by the client.
|
||||
|
||||
DOM-based XSS is similar to reflected. The difference is that the payload will never go to the server, but will only ever be processed by the client.
|
||||
|
||||
* Attacker sends a malicious URL to victim
|
||||
* Victim clicks on the link
|
||||
|
@ -1,12 +1,15 @@
|
||||
== Ientify Potential for DOM-Based XSS
|
||||
|
||||
For this, you'll want to look for some 'test' code in the route handlers (javascript/backbone). Sometimes, test code gets left in.
|
||||
(Often times test code is very simple and lacks security or any quality controls!).
|
||||
DOM-Based XSS can usually be found by looking for the route configurations in the client-side code.
|
||||
Look for a route that takes inputs that you can ID being 'reflected' to the page.
|
||||
|
||||
For this example, you'll want to look for some 'test' code in the route handlers (WebGoat uses backbone as its primary javascript library).
|
||||
Sometimes, test code gets left in production (and often times test code is very simple and lacks security or any quality controls!).
|
||||
|
||||
Your objective is to find the route and exploit it. First though ... what is the base route? As an example, look at the URL for this lesson ...
|
||||
it should look something like /WebGoat/start.mvc#lesson/CrossSiteScripting.lesson/5 (although maybe slightly different). The 'base route' in this case is:
|
||||
*start.mvc#lesson/*
|
||||
|
||||
The *CrossSiteScripting.lesson/5* after that are parameters that are processed by javascript route handler.
|
||||
The *CrossSiteScripting.lesson/#* after that are parameters that are processed by javascript route handler.
|
||||
|
||||
So, what is test route for this test code?
|
||||
|
@ -1,9 +1,11 @@
|
||||
== Try It! DOM-Based XSS
|
||||
|
||||
Some attacks are 'blind'. Fortunately, you have the server running here so you will be able to tell if you are successful. Use the route you just found and see if
|
||||
you can use the fact that it reflects a parameter from the route without encoding to execute an internal function in WebGoat. The function you want to execute is ...
|
||||
Some attacks are 'blind'. Fortunately, you have the server running here so you will be able to tell if you are successful.
|
||||
Use the route you just found and see if you can use the fact that it reflects a parameter from the route without encoding to execute an internal function in WebGoat.
|
||||
The function you want to execute is ...
|
||||
|
||||
*webgoat.customjs.phoneHome()*
|
||||
|
||||
Sure, you could just use console/debug to trigger it, but you need to trigger it via a URL in a new tab. Once you complete it, paste the output message from the log below ...
|
||||
Sure, you could just use console/debug to trigger it, but you need to trigger it via a URL in a new tab.
|
||||
|
||||
Once you do trigger it, a subsequent response will come to the browser with a random number. Put that random number in below.
|
||||
|
@ -0,0 +1,18 @@
|
||||
== DOM-Based XSS Example
|
||||
|
||||
----
|
||||
<script language="javascript"> function resetVals(form)
|
||||
{
|
||||
var temp = document.URL;
|
||||
var idx = temp.indexOf('login?');
|
||||
var errorMsg = temp.substring(idx+1,temp.length).split('=');
|
||||
if (errorMsg\[1\].indexOf("/login?")!=-1)
|
||||
{
|
||||
var index = errorMsg\[1\].indexOf("/login?");
|
||||
var msg = errorMsg\[1\].substring(index,length-1);
|
||||
errorMsg\[1\] = msg;
|
||||
}
|
||||
document.write('<b>'+errorMsg\[1\]+'</b>');
|
||||
}
|
||||
</script>
|
||||
----
|
@ -1,18 +1,11 @@
|
||||
== DOM-Based XSS Example
|
||||
== Stored XSS
|
||||
Stored cross-site scripting is different in that the payload is persisted (stored) as opposed to passed/injected via a link.
|
||||
|
||||
----
|
||||
<script language="javascript"> function resetVals(form)
|
||||
{
|
||||
var temp = document.URL;
|
||||
var idx = temp.indexOf('login?');
|
||||
var errorMsg = temp.substring(idx+1,temp.length).split('=');
|
||||
if (errorMsg\[1\].indexOf("/login?")!=-1)
|
||||
{
|
||||
var index = errorMsg\[1\].indexOf("/login?");
|
||||
var msg = errorMsg\[1\].substring(index,length-1);
|
||||
errorMsg\[1\] = msg;
|
||||
}
|
||||
document.write('<b>'+errorMsg\[1\]+'</b>');
|
||||
}
|
||||
</script>
|
||||
----
|
||||
== Stored XSS Scenario
|
||||
* Attacker posts malicious script to a message board
|
||||
* Message is stored in a server database
|
||||
* Victim reads the message
|
||||
* The malicious script embedded in the message board post executes in the victim’s browser
|
||||
** The script steals sensitive information, like the session id, and releases it to the attacker
|
||||
|
||||
*Victim does not realize attack occurred*
|
||||
|
@ -0,0 +1,6 @@
|
||||
See the comments below.
|
||||
|
||||
Add a comment with a javascript payload. Again ... you want to call the _webgoat.customjs.phoneHome_ function.
|
||||
|
||||
As an attacker (offensive security), keep in mind that most apps are not going to have such a straight-forwardly named compromise.
|
||||
Also, you may have to find a way to load your own javascript dynamically to fully achieve goals of exfiltrating data.
|
@ -0,0 +1,20 @@
|
||||
== DOM-based XSS Defense
|
||||
|
||||
* Attacker creates url:
|
||||
+
|
||||
----
|
||||
http://mylogin.com/login?error=<script>alert(“xss”)</script>
|
||||
----
|
||||
|
||||
* JavaScript must enforce input validation
|
||||
+
|
||||
----
|
||||
if ( errorMsg\[1\].match(/^[ a-zA-Z0-9:-]$/))
|
||||
{
|
||||
document.write(‘some error’);
|
||||
}
|
||||
else
|
||||
{
|
||||
document.write('<b>'+errorMsg\[1\]+'</b>');
|
||||
}
|
||||
----
|
@ -1,20 +1,27 @@
|
||||
== DOM-based XSS Defense
|
||||
== XSS Defense
|
||||
|
||||
* Attacker creates url:
|
||||
+
|
||||
----
|
||||
http://mylogin.com/login?error=<script>alert(“xss”)</script>
|
||||
----
|
||||
|
||||
* JavaScript must enforce input validation
|
||||
+
|
||||
----
|
||||
if ( errorMsg\[1\].match(/^[ a-zA-Z0-9:-]$/))
|
||||
{
|
||||
document.write(‘some error’);
|
||||
}
|
||||
else
|
||||
{
|
||||
document.write('<b>'+errorMsg\[1\]+'</b>');
|
||||
}
|
||||
----
|
||||
=== Why?
|
||||
Hopefully we've covered that by now. Bottom line, you don't want someone else's code running in the context of your users and their logged-in seession
|
||||
|
||||
=== What to encode?
|
||||
The basic premise of defending against XSS is *output endoding* any untrusted input that goes to the screen.
|
||||
That may be changing with more sophisticated attacks, but is still the best defense we currently have. *AND* ... *context matters*
|
||||
|
||||
Another word on 'untrusted input'. If in doubt, treat everything (even data you populated in your DB as untrusted).
|
||||
Sometimes data is shared across multiple systems and what you think is your data, may not have been created by you/your team.
|
||||
|
||||
=== When/Where?
|
||||
Encode *as the data is sent to the browser* (not in your persisted data). In the case of *Single Page Apps (SPA's), you will need to encode
|
||||
in the client*. Consult your framework/library for me details, but some resources will be provided on the next page.
|
||||
|
||||
=== How?
|
||||
|
||||
* Encode as HTML Entities in HTML Body
|
||||
* Encode as HTML Entities in HTML Attribute
|
||||
* Encode for Javascript if outputting user input to javascript (but think about that ... you're outputting user input into javascript on your page!!)
|
||||
|
||||
*DO NOT* try to blacklist/negative filter on strings like '<script>' and so forth.
|
||||
|
||||
|
||||
...See the next page for some recommended resources and reading on Defending against XSS
|
@ -0,0 +1,8 @@
|
||||
== Stored XSS Scenario
|
||||
* Attacker posts malicious script to a message board
|
||||
* Message is stored in a server database
|
||||
* Victim reads the message
|
||||
* The malicious script embedded in the message board post executes in the victim’s browser
|
||||
** The script steals sensitive information, like the session id, and releases it to the attacker
|
||||
|
||||
*Victim does not realize attack occurred*
|
@ -1,8 +1,46 @@
|
||||
== Stored XSS Scenario
|
||||
* Attacker posts malicious script to a message board
|
||||
* Message is stored in a server database
|
||||
* Victim reads the message
|
||||
* The malicious script embedded in the message board post executes in the victim’s browser
|
||||
** The script steals sensitive information, like the session id, and releases it to the attacker
|
||||
== XSS Defense Resources
|
||||
|
||||
*Victim does not realize attack occurred*
|
||||
=== Java OWASP Encoder
|
||||
Do not be bothered by the incubator status on this project, use it if you are doing Java Web Apps and want to defend against XSS, use this
|
||||
link: https://www.owasp.org/index.php/OWASP_Java_Encoder_Project
|
||||
|
||||
=== General XSS prevention Cheat Sheet
|
||||
link: https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
|
||||
|
||||
=== DOM XSS Prevention Cheat Sheet
|
||||
link: https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet
|
||||
|
||||
=== XSS Filter Evasion
|
||||
Good to know your enemy ...
|
||||
link https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
|
||||
|
||||
=== Javascript Framework Specifics
|
||||
|
||||
Encoding in the client can be tricky. Here are somre resources to help with that. If you don't see your framework below (e.g. Ember, React, ???) and would like to contribute or suggest something ...
|
||||
stop by https://github.com/WebGoat/WebGoat and file an issue (preferably with some recommendations/links) or fork and submit a PR.
|
||||
|
||||
==== jQuery
|
||||
be aware if you are using something like ...
|
||||
|
||||
_$selector.html(userInputHere)_,
|
||||
|
||||
...you are in danger. If you want to use that, ensure you are doing someting more like:
|
||||
|
||||
_$selector.html(someEncodeHtmlMethod(userInputHere))_
|
||||
|
||||
OR
|
||||
|
||||
_$selector.*text*(someEncodeHtmlMethod(userInputHere))_
|
||||
|
||||
...if you only want the text of what is output by the user (http://stackoverflow.com/questions/9735045/is-jquery-text-method-xss-safe#9735118)
|
||||
|
||||
==== Backbone.js
|
||||
(One character can make such a difference)
|
||||
|
||||
http://underscorejs.org/#template
|
||||
|
||||
https://nvisium.com/blog/2015/05/21/dont-break-your-backbone-xss-mitigation/
|
||||
|
||||
==== Angular
|
||||
Angular has sought to escape by default, but the expression language has proven to have 'sandbox' escapes. Best to check
|
||||
details of the version you are using and consult starting here: https://docs.angularjs.org/guide/security
|
Loading…
x
Reference in New Issue
Block a user