Added more content for CSRF lesson
This commit is contained in:
parent
5eed385d5d
commit
43b82027f5
5
.gitignore
vendored
5
.gitignore
vendored
@ -14,7 +14,8 @@
|
||||
/.settings/org.eclipse.wst.validation.prefs
|
||||
/.externalToolBuilders/
|
||||
.project
|
||||
/target
|
||||
*/target/*
|
||||
mongo-data/*
|
||||
.classpath
|
||||
.idea/
|
||||
.settings/
|
||||
@ -29,6 +30,7 @@ src/main/webapp/plugin_lessons/*.jar
|
||||
src/main/webapp/users/*.props
|
||||
classes/*
|
||||
*.iml
|
||||
pom.xml.versionsBackup
|
||||
|
||||
/*.iml
|
||||
.extract/*
|
||||
@ -39,3 +41,4 @@ webgoat-lessons/**/target
|
||||
**/*.jar
|
||||
**/.DS_Store
|
||||
webgoat-server/mongo-data/*
|
||||
webgoat-lessons/vulnerable-components/dependency-reduced-pom.xml
|
||||
|
@ -15,7 +15,7 @@ import javax.validation.constraints.Size;
|
||||
public class UserForm {
|
||||
|
||||
@NotNull
|
||||
@Size(min=6, max=10)
|
||||
@Size(min=6, max=20)
|
||||
private String username;
|
||||
@NotNull
|
||||
@Size(min=6, max=10)
|
||||
|
@ -0,0 +1,88 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.owasp.webgoat.session.UserSessionData;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 11/17/17.
|
||||
*/
|
||||
@AssignmentPath("/csrf/feedback")
|
||||
@AssignmentHints({"csrf-feedback-hint1", "csrf-feedback-hint2", "csrf-feedback-hint3"})
|
||||
public class CSRFFeedback extends AssignmentEndpoint {
|
||||
|
||||
@Autowired
|
||||
private UserSessionData userSessionData;
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@PostMapping(value = "/message", produces = {"application/json"})
|
||||
@ResponseBody
|
||||
public AttackResult completed(HttpServletRequest request, @RequestBody String feedback) {
|
||||
try {
|
||||
objectMapper.readValue(feedback.getBytes(), Map.class);
|
||||
} catch (IOException e) {
|
||||
return failed().feedback(ExceptionUtils.getStackTrace(e)).build();
|
||||
}
|
||||
boolean correctCSRF = requestContainsWebGoatCookie(request.getCookies()) && request.getContentType().equals(MediaType.TEXT_PLAIN_VALUE);
|
||||
correctCSRF &= hostOrRefererDifferentHost(request);
|
||||
if (correctCSRF) {
|
||||
String flag = UUID.randomUUID().toString();
|
||||
userSessionData.setValue("csrf-feedback", flag);
|
||||
return success().feedback("csrf-feedback-success").feedbackArgs(flag).build();
|
||||
}
|
||||
return failed().build();
|
||||
}
|
||||
|
||||
@PostMapping(produces = "application/json")
|
||||
@ResponseBody
|
||||
public AttackResult flag(@RequestParam("confirmFlagVal") String flag) {
|
||||
if (flag.equals(userSessionData.getValue("csrf-feedback"))) {
|
||||
return trackProgress(success().build());
|
||||
} else {
|
||||
return trackProgress(failed().build());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hostOrRefererDifferentHost(HttpServletRequest request) {
|
||||
String referer = request.getHeader("referer");
|
||||
String host = request.getHeader("host");
|
||||
return !StringUtils.contains(referer, host);
|
||||
}
|
||||
|
||||
private boolean requestContainsWebGoatCookie(Cookie[] cookies) {
|
||||
if (cookies != null) {
|
||||
for (Cookie c : cookies) {
|
||||
if (c.getName().equals("JSESSIONID")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Solution
|
||||
<form name="attack" enctype="text/plain" action="http://localhost:8080/WebGoat/csrf/feedback/message" METHOD="POST">
|
||||
<input type="hidden" name='{"name": "Test", "email": "test1233@dfssdf.de", "subject": "service", "message":"dsaffd"}'>
|
||||
</form>
|
||||
<script>document.attack.submit();</script>
|
||||
*/
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.owasp.webgoat.users.UserTracker;
|
||||
import org.owasp.webgoat.users.UserTrackerRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 11/17/17.
|
||||
*/
|
||||
@AssignmentPath("/csrf/login")
|
||||
@AssignmentHints({"csrf-login-hint1", "csrf-login-hint2", "csrf-login-hint3"})
|
||||
public class CSRFLogin extends AssignmentEndpoint {
|
||||
|
||||
@Autowired
|
||||
private UserTrackerRepository userTrackerRepository;
|
||||
|
||||
@PostMapping(produces = {"application/json"})
|
||||
@ResponseBody
|
||||
public AttackResult completed() {
|
||||
String userName = getWebSession().getUserName();
|
||||
if (userName.startsWith("csrf")) {
|
||||
markAssignmentSolvedWithRealUser(userName.substring("csrf-".length()));
|
||||
return trackProgress(success().feedback("csrf-login-success").build());
|
||||
}
|
||||
return trackProgress(failed().feedback("csrf-login-failed").feedbackArgs(userName).build());
|
||||
}
|
||||
|
||||
private void markAssignmentSolvedWithRealUser(String username) {
|
||||
UserTracker userTracker = userTrackerRepository.findOne(username);
|
||||
userTracker.assignmentSolved(getWebSession().getCurrentLesson(), this.getClass().getSimpleName());
|
||||
userTrackerRepository.save(userTracker);
|
||||
}
|
||||
}
|
@ -2,62 +2,63 @@
|
||||
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CSRF_intro.adoc"></div>
|
||||
</div>
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CSRF_intro.adoc"></div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CSRF_GET.adoc"></div>
|
||||
</div>
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CSRF_GET.adoc"></div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CSRF_Get_Flag.adoc"></div>
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CSRF_Get_Flag.adoc"></div>
|
||||
|
||||
<form accept-charset="UNKNOWN" id="basic-csrf-get"
|
||||
method="GET" name="form1"
|
||||
<form accept-charset="UNKNOWN" id="basic-csrf-get"
|
||||
method="GET" name="form1"
|
||||
successCallback=""
|
||||
action="/WebGoat/csrf/basic-get-flag"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
<input name="csrf" type="hidden" value="false"/>
|
||||
<input type="submit" name="ubmit="/>
|
||||
|
||||
</form>
|
||||
|
||||
<div class="adoc-content" th:replace="doc:CSRF_Basic_Get-1.adoc"></div>
|
||||
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success">
|
||||
<i class="fa fa-2 fa-check hidden" aria-hidden="true">
|
||||
</i>
|
||||
</div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN" id="confirm-flag-1"
|
||||
method="POST" name="form2"
|
||||
successCallback=""
|
||||
action="/WebGoat/csrf/basic-get-flag"
|
||||
action="/WebGoat/csrf/confirm-flag-1"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
<input name="csrf" type="hidden" value="false" />
|
||||
<input type="submit" name="ubmit=" />
|
||||
|
||||
Confirm Flag Value:
|
||||
<input type="text" length="6" name="confirmFlagVal" value=""/>
|
||||
|
||||
<input name="submit" value="Submit" type="submit"/>
|
||||
|
||||
</form>
|
||||
|
||||
<div class="adoc-content" th:replace="doc:CSRF_Basic_Get-1.adoc"></div>
|
||||
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success">
|
||||
<i class="fa fa-2 fa-check hidden" aria-hidden="true">
|
||||
</i>
|
||||
</div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN" id="confirm-flag-1"
|
||||
method="POST" name="form2"
|
||||
successCallback=""
|
||||
action="/WebGoat/csrf/confirm-flag-1"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
|
||||
Confirm Flag Value:
|
||||
<input type="text" length="6" name="confirmFlagVal" value="" />
|
||||
|
||||
<input name="submit" value="Submit" type="submit"/>
|
||||
|
||||
</form>
|
||||
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="lesson-page-wrapper">
|
||||
|
||||
<div class="adoc-content" th:replace="doc:CSRF_Reviews.adoc"></div>
|
||||
<div class="adoc-content" th:replace="doc:CSRF_Reviews.adoc"></div>
|
||||
|
||||
<!-- comment area -->
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/reviews.css}"/>
|
||||
<script th:src="@{/lesson_js/csrf-review.js}" language="JavaScript"></script>
|
||||
<!-- comment area -->
|
||||
<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="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="panel post">
|
||||
<div class="post-heading">
|
||||
@ -89,16 +90,17 @@
|
||||
method="POST" name="review-form"
|
||||
successCallback=""
|
||||
action="/WebGoat/csrf/review">
|
||||
<input class="form-control" id="reviewText" name="reviewText" placeholder="Add a Review" type="text"/>
|
||||
<input class="form-control" id="reviewStars" name="stars" type="text" />
|
||||
<input type="hidden" name="validateReq" value="2aa14227b9a13d0bede0388a7fba9aa9" />
|
||||
<input class="form-control" id="reviewText" name="reviewText" placeholder="Add a Review"
|
||||
type="text"/>
|
||||
<input class="form-control" id="reviewStars" name="stars" type="text"/>
|
||||
<input type="hidden" name="validateReq" value="2aa14227b9a13d0bede0388a7fba9aa9"/>
|
||||
<input type="submit" name="submit" value="Submit review"/>
|
||||
</form>
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
<!--<span class="input-group-addon">-->
|
||||
<!--<i id="postReview" class="fa fa-edit" style="font-size: 20px"></i>-->
|
||||
<!--</span>-->
|
||||
<!--<i id="postReview" class="fa fa-edit" style="font-size: 20px"></i>-->
|
||||
<!--</span>-->
|
||||
</div>
|
||||
<ul class="comments-list">
|
||||
<div id="list">
|
||||
@ -108,14 +110,144 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end comments -->
|
||||
</div>
|
||||
<!-- end comments -->
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CSRF_Frameworks.adoc"></div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CSRF_JSON.adoc"></div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CSRF_ContentType.adoc"></div>
|
||||
|
||||
<script th:src="@{/lesson_js/feedback.js}" language="JavaScript"></script>
|
||||
<div style="container-fluid; background-color: #f1f1f1; border: 2px solid #a66;
|
||||
border-radius: 12px;
|
||||
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">
|
||||
<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>
|
||||
</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="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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CSRF_Impact_Defense.adoc"></div>
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success">
|
||||
<i class="fa fa-2 fa-check hidden" aria-hidden="true">
|
||||
</i>
|
||||
</div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN" id="confirm-flag-feedback"
|
||||
method="POST" name="form2"
|
||||
action="/WebGoat/csrf/feedback"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
|
||||
Confirm Flag Value:
|
||||
<input type="text" length="6" name="confirmFlagVal" value=""/>
|
||||
|
||||
<input name="submit" value="Submit" type="submit"/>
|
||||
|
||||
</form>
|
||||
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
<!--</div>-->
|
||||
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CSRF_Login.adoc"></div>
|
||||
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success">
|
||||
<i class="fa fa-2 fa-check hidden" aria-hidden="true">
|
||||
</i>
|
||||
</div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN" id="confirm-flag-login"
|
||||
method="POST" name="form2"
|
||||
action="/WebGoat/csrf/login"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
|
||||
Press the button below when your are logged in as the other user<br/>
|
||||
|
||||
<input name="submit" value="Solved!" type="submit"/>
|
||||
|
||||
</form>
|
||||
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CSRF_Impact_Defense.adoc"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<!--</div>-->
|
||||
|
||||
</html>
|
@ -16,4 +16,17 @@ csrf-review.success=It appears you have submitted correctly from another site. G
|
||||
|
||||
csrf-review-hint1=Again, you will need to submit from an external domain/host to trigger this action. While CSRF can often be triggered from the same host (e.g. via persisted payload), this doesn't work that way.
|
||||
csrf-review-hint2=Remember, you need to mimic the existing workflow/form.
|
||||
csrf-review-hint3=This one has a weak anti-CSRF protection, but you do need to overcome (mimic) it
|
||||
csrf-review-hint3=This one has a weak anti-CSRF protection, but you do need to overcome (mimic) it
|
||||
|
||||
csrf-feedback-hint1=Look at the content-type.
|
||||
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-invalid-json=Invalid JSON received.
|
||||
csrf-feedback-success=Congratulations you have found the correct solution, the flag is: {0}
|
||||
|
||||
csrf-login-hint1=First create a new account with csrf-username
|
||||
csrf-login-hint2=Create a form which will log you in as this user (hint 1) and upload it to WebWolf
|
||||
csrf-login-hint3=Visit this assignment again
|
||||
csrf-login-success=Congratulations, now log out and login with your normal user account within WebGoat, remember the attacker knows you solved this assignment
|
||||
csrf-login-failed=The solution is not correct, you are clicking the button while logged in as {0}
|
BIN
webgoat-lessons/csrf/src/main/resources/images/login-csrf.png
Normal file
BIN
webgoat-lessons/csrf/src/main/resources/images/login-csrf.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 212 KiB |
7
webgoat-lessons/csrf/src/main/resources/js/feedback.js
Normal file
7
webgoat-lessons/csrf/src/main/resources/js/feedback.js
Normal file
@ -0,0 +1,7 @@
|
||||
webgoat.customjs.feedback = function() {
|
||||
var data = {};
|
||||
$('#csrf-feedback').find('input, textarea, select').each(function(i, field) {
|
||||
data[field.name] = field.value;
|
||||
});
|
||||
return JSON.stringify(data);
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
== CSRF and content-type
|
||||
|
||||
In the previous section we saw how relying on the content-type is not a protection against
|
||||
CSRF. In this section we will look into another way we can perform a CSRF attack against
|
||||
a APIs which are not protected against CSRF.
|
||||
|
||||
In this assignment you need to achieve to POST the following JSON message to our endpoints:
|
||||
|
||||
[source]
|
||||
----
|
||||
POST /csrf/feedback HTTP/1.1
|
||||
|
||||
{
|
||||
"name" : "WebGoat",
|
||||
"email" : "webgoat@webgoat.org"
|
||||
"content" : "WebGoat is the best!!"
|
||||
}
|
||||
----
|
||||
|
||||
More information can be found http://pentestmonkey.net/blog/csrf-xml-post-request[here]
|
||||
|
||||
Remember you need to make the call from another origin (WebWolf can help here) and you need to be logged in into
|
||||
WebGoat.
|
@ -0,0 +1,22 @@
|
||||
=== Automatic support from frameworks
|
||||
|
||||
Most frameworks now have default support for preventing CSRF. For example with Angular an interceptor reads a token
|
||||
from a cookie by default XSRF-TOKEN and sets it as an HTTP header, X-XSRF-TOKEN. Since only code that runs on your domain
|
||||
could read the cookie, the backend can be certain that the HTTP request came from your client application and not an attacker.
|
||||
|
||||
In order for this to work the backend server sets the token in a cookie. As the value of the cookie should be read
|
||||
by Angular (JavaScript) this cookie should not be marked with the http-only flag. On every request towards the server
|
||||
Angular will put the token in the X-XSRF-TOKEN as a HTTP header. The server can validate whether those two tokens
|
||||
match and this will ensure the server the request is running on the same domain.
|
||||
|
||||
*Important: DEFINE A SEPARATE COOKIE, DO NOT REUSE THE SESSION COOKIE*
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,46 @@
|
||||
**But I only have JSON APIs and no CORS enabled, how can those be susceptible to CSRF?**
|
||||
|
||||
A lot of web applications implement no protection against CSRF they are somehow protected by the fact that
|
||||
they only work with `application/json` as content type. The only way to make a request with this content-type from the
|
||||
browser is with a XHR request. Before the browser can make such a request a preflight request will be made towards
|
||||
the server (remember the CSRF request will be cross origin). If the preflight response does not allow the cross origin
|
||||
request the browser will not make the call.
|
||||
|
||||
To make a long answer short: this is *not* a valid protection against CSRF.
|
||||
|
||||
One example why this protection is not enough can be found https://bugs.chromium.org/p/chromium/issues/detail?id=490015[here].
|
||||
Turns out `Navigator.sendBeacon()` was allowed to send POST request with an arbitrary content-type.
|
||||
|
||||
[qoute, developer.mozilla.org]
|
||||
____
|
||||
The navigator.sendBeacon() method can be used to asynchronously transfer a small amount of
|
||||
data over HTTP to a web server. This method addresses the needs of analytics and diagnostics
|
||||
code that typically attempts to send data to a web server prior to the unloading of the
|
||||
document. Sending the data any sooner may result in a missed opportunity to gather data..."
|
||||
____
|
||||
|
||||
{nbsp} +
|
||||
For example:
|
||||
|
||||
[source]
|
||||
----
|
||||
function postBeacon() {
|
||||
var data= new Blob([JSON.stringify({"author" :"WebGoat"})], {type : 'application/json'});
|
||||
navigator.sendBeacon("http://localhost:8083", data)
|
||||
}
|
||||
----
|
||||
|
||||
[quote, Eduardo Vela]
|
||||
____
|
||||
I think Content-Type restrictions are useful for websites that are accidentally safe against CSRF. They are not meant to be, but they are because they happen to only accept XML or JSON payloads.
|
||||
|
||||
That said, it's somewhat obvious the websites depending on this behavior should be fixed, and any reputable pentesters will point that out. The issue is whether it's the browser responsibility to act as a nanny to weak websites, or we should leave weak websites as sacrifice for great justice. Survival of the fittest.
|
||||
|
||||
IMHO, the answer is somewhere in between, and a good first step would be to document all these Same Origin Policy gotchas that websites might depend upon for security.
|
||||
|
||||
But wrt to this bug in specific, if it never got fixed, I don't think it would be the end of the world. But then again, on this day and age, maybe there's a way to launch nuclear missiles with a XML RPC interface, so maybe it would be the end of the world.
|
||||
____
|
||||
|
||||
{nbsp} +
|
||||
Both Firefox and Chrome fixed this issue, but it shows why you should implement a CSRF protection instead
|
||||
of relying on the content-type of your APIs.
|
@ -0,0 +1,24 @@
|
||||
:blank: pass:[ +]
|
||||
|
||||
== Login CSRF attack
|
||||
|
||||
In a login CSRF attack, the attacker forges a login request to an honest site using the attacker’s username
|
||||
and password at that site. If the forgery succeeds, the honest server responds with a `Set-Cookie` header
|
||||
that instructs the browser to mutate its state by storing a session cookie, logging the user into
|
||||
the honest site as the attacker. This session cookie is used to bind subsequent requests to the user’s session and hence
|
||||
to the attacker’s authentication credentials. Login CSRF attacks can have serious consequences, for example
|
||||
see the picture below where an attacker created an account at google.com the victim visits the malicious
|
||||
website and the user is logged in as the attacker. The attacker could then later on gather information about
|
||||
the activities of the user.
|
||||
|
||||
{blank}
|
||||
|
||||
image::images/login-csrf.png[caption="Figure: ", title="Login CSRF from Robust Defenses for Cross-Site Request Forgery", width="800", height="500", style="lesson-image" link="http://seclab.stanford.edu/websec/csrf/csrf.pdf"]
|
||||
|
||||
{blank}
|
||||
For more information read the following http://seclab.stanford.edu/websec/csrf/csrf.pdf[paper]
|
||||
|
||||
In this assignment try to see if WebGoat is also vulnerable for a login CSRF attack. First create a user
|
||||
based on your own username prefixed with csrf. So if your username is `tom` you must create
|
||||
a new user called `csrf-tom`
|
||||
|
@ -1,9 +1,9 @@
|
||||
== Post a review on someone else's behalf
|
||||
|
||||
The page below simulates a comment/review page. The difference here is that you have to inititate the submission elsewhere as you might
|
||||
The page below simulates a comment/review page. The difference here is that you have to initiate the submission elsewhere as you might
|
||||
with a CSRF attack and like the previous exercise. It's easier than you think. In most cases, the trickier part is
|
||||
finding somewhere that you want to execute the CSRF attack. The classic example is account/wire transfers in someone's bank account.
|
||||
|
||||
But we're keepoing it simple here. In this case, you just need to trigger a review submission on behalf of the currently
|
||||
But we're keeping it simple here. In this case, you just need to trigger a review submission on behalf of the currently
|
||||
logged in user.
|
||||
|
||||
|
@ -0,0 +1,58 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import org.hamcrest.core.StringContains;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.owasp.webgoat.plugins.LessonTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 11/17/17.
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class CSRFFeedbackTest extends LessonTest {
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
CSRF csrf = new CSRF();
|
||||
when(webSession.getCurrentLesson()).thenReturn(csrf);
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||
when(webSession.getUserName()).thenReturn("unit-test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postingJsonMessageThroughWebGoatShouldWork() throws Exception {
|
||||
mockMvc.perform(post("/csrf/feedback/message")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"name\": \"Test\", \"email\": \"test1233@dfssdf.de\", \"subject\": \"service\", \"message\":\"dsaffd\"}"))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void csrfAttack() throws Exception {
|
||||
mockMvc.perform(post("/csrf/feedback/message")
|
||||
.contentType(MediaType.TEXT_PLAIN)
|
||||
.cookie(new Cookie("JSESSIONID", "test"))
|
||||
.header("host", "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: ")));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user