- Introduced user registration
- Now using Spring Boot for classloading, this way local development does not need to restart the complete server - Fixed all kinds of dependencies on the names of the lessons necessary to keep in mind during the creation of a lesson. - Simplied loading of resources, by adding resource mappings in MvcConfig. - Refactored plugin loading, now only one class is left for loading the lessons.
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
|
||||
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
|
||||
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
|
||||
<div class="adoc-content" th:replace="doc:Challenge_content1.adoc"></div>
|
||||
</div>
|
||||
|
||||
</html>
|
@ -0,0 +1 @@
|
||||
challenge.title=WebGoat Challenge
|
@ -0,0 +1 @@
|
||||
This is the challenge
|
@ -1,6 +1,7 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@ -40,14 +41,15 @@ import java.io.IOException;
|
||||
* @since August 11, 2016
|
||||
*/
|
||||
@AssignmentPath("/clientSideFiltering/attack1")
|
||||
public class Attack extends AssignmentEndpoint {
|
||||
@AssignmentHints({"ClientSideFilteringHint1", "ClientSideFilteringHint2", "ClientSideFilteringHint3", "ClientSideFilteringHint4"})
|
||||
public class ClientSideFilteringAssignment extends AssignmentEndpoint {
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
public @ResponseBody AttackResult completed(@RequestParam String answer) throws IOException {
|
||||
if ("450000".equals(answer)) {
|
||||
return trackProgress(success().build());
|
||||
} else {
|
||||
return trackProgress(failed().build());
|
||||
}
|
||||
public
|
||||
@ResponseBody
|
||||
AttackResult completed(@RequestParam String answer) throws IOException {
|
||||
return trackProgress("450000".equals(answer) ?
|
||||
success().feedback("assignment.solved").build() :
|
||||
failed().feedback("ClientSideFiltering.incorrect").build());
|
||||
}
|
||||
}
|
@ -6,34 +6,51 @@ package org.owasp.webgoat.plugin;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import lombok.SneakyThrows;
|
||||
import org.owasp.webgoat.assignments.Endpoint;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Salaries extends Endpoint {
|
||||
|
||||
@Value("${webgoat.user.directory}")
|
||||
private String webGoatHomeDirectory;
|
||||
|
||||
@PostConstruct
|
||||
@SneakyThrows
|
||||
public void copyFiles() {
|
||||
ClassPathResource classPathResource = new ClassPathResource("employees.xml");
|
||||
File targetDirectory = new File(webGoatHomeDirectory, "/ClientSideFiltering");
|
||||
if (!targetDirectory.exists()) {
|
||||
targetDirectory.mkdir();
|
||||
}
|
||||
FileCopyUtils.copy(classPathResource.getInputStream(), new FileOutputStream(new File(targetDirectory, "employees.xml")));
|
||||
}
|
||||
|
||||
@RequestMapping(produces = {"application/json"})
|
||||
@ResponseBody
|
||||
public List<Map<String, Object>> invoke(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
String userId = req.getParameter("userId");
|
||||
public List<Map<String, Object>> invoke() throws ServletException, IOException {
|
||||
NodeList nodes = null;
|
||||
File d = new File(getPluginDirectory(), "ClientSideFiltering/html/employees.xml");
|
||||
File d = new File(webGoatHomeDirectory, "ClientSideFiltering/employees.xml");
|
||||
XPathFactory factory = XPathFactory.newInstance();
|
||||
XPath xPath = factory.newXPath();
|
||||
InputSource inputSource = new InputSource(new FileInputStream(d));
|
||||
@ -49,8 +66,7 @@ public class Salaries extends Endpoint {
|
||||
String expression = sb.toString();
|
||||
|
||||
try {
|
||||
nodes = (NodeList) xPath.evaluate(expression, inputSource,
|
||||
XPathConstants.NODESET);
|
||||
nodes = (NodeList) xPath.evaluate(expression, inputSource, XPathConstants.NODESET);
|
||||
} catch (XPathExpressionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -58,7 +74,7 @@ public class Salaries extends Endpoint {
|
||||
List json = Lists.newArrayList();
|
||||
java.util.Map<String, Object> employeeJson = Maps.newHashMap();
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
if (i != 0 && i % COLUMNS == 0) {
|
||||
if (i % COLUMNS == 0) {
|
||||
employeeJson = Maps.newHashMap();
|
||||
json.add(employeeJson);
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
#lesson_wrapper {height: 435px;width: 500px;}
|
||||
#lesson_header {background-image: url(../images/lesson1_header.jpg); width: 490px;padding-right: 10px;padding-top: 60px;background-repeat: no-repeat;}
|
||||
.lesson_workspace {background-image: url(../images/lesson1_workspace.jpg); width: 489px;height: 325px;padding-left: 10px;padding-top: 10px;background-repeat: no-repeat;}
|
||||
.lesson_workspace {background-image: url(../images/lesson1_workspace.jpg); width: 490px;height: 325px;padding-left: 10px;padding-top: 10px;background-repeat: no-repeat;}
|
@ -1,22 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
|
||||
<div class="lesson-page-wrapper"><!-- reuse this block for each 'page' of content -->
|
||||
<!-- include content here ... will be first page/tab multiple -->
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:ClientSideFiltering_plan.adoc"></div>
|
||||
</div>
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:ClientSideFiltering_assignment.adoc"></div>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<input type="hidden" id="user_id" value="102"/>
|
||||
<!-- using attack-form class on your form, will allow your request to be ajaxified and stay within the display framework for webgoat -->
|
||||
<form class="attack-form" accept-charset="UNKNOWN" method="POST" name="form" action="/WebGoat/clientSideFiltering/attack1">
|
||||
<form class="attack-form" accept-charset="UNKNOWN" method="POST" name="form"
|
||||
action="/WebGoat/clientSideFiltering/attack1">
|
||||
<link rel="stylesheet" type="text/css"
|
||||
th:href="@{/plugin_lessons/plugin/ClientSideFiltering/html/clientSideFiltering-stage1.css}"/>
|
||||
<script th:src="@{/plugin_lessons/plugin/ClientSideFiltering/js/clientSideFiltering.js}"
|
||||
th:href="@{/lesson_css/clientSideFiltering-stage1.css}"/>
|
||||
<script th:src="@{/lesson_js/clientSideFiltering.js}"
|
||||
language="JavaScript"></script>
|
||||
<input id="userID" value="102" name="userID" type="HIDDEN"/>
|
||||
<input id="userID" value="101" name="userID" type="HIDDEN"/>
|
||||
<div id="lesson_wrapper">
|
||||
<div id="lesson_header"></div>
|
||||
<div class="lesson_workspace"><br/><br/>
|
||||
@ -34,7 +37,8 @@
|
||||
<option value="110" label="Joanne McDougal">Joanne McDougal</option>
|
||||
</select></p>
|
||||
<p></p>
|
||||
<table style="display: none" id="hiddenEmployeeRecords" align="center" border="1" cellpadding="2"
|
||||
<table style="display: none" id="hiddenEmployeeRecords" align="center" border="1"
|
||||
cellpadding="2"
|
||||
cellspacing="0" width="90%">
|
||||
<div>
|
||||
</div>
|
||||
@ -63,11 +67,11 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<!-- do not remove the two following div's, this is where your feedback/output will land -->
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
<!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons -->
|
||||
</div>
|
||||
<!-- do not remove the two following div's, this is where your feedback/output will land -->
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
<!-- ... of course, you can move them if you want to, but that will not look consistent to other lessons -->
|
||||
</div>
|
||||
|
||||
|
@ -10,10 +10,10 @@ ClientSideFilteringStage1Question=What is Neville Bartholomew's salary?
|
||||
ClientSideFilteringStage1SubmitAnswer=Submit Answer
|
||||
ClientSideFilteringStage2Finish=Click here when you believe you have completed the lesson.
|
||||
ClientSideFilteringChoose=Choose Employee
|
||||
ClientSideFilteringHint1=Stage 1: The information displayed when an employee is chosen from the drop down menu is stored on the client side.
|
||||
ClientSideFilteringHint2=Stage 1: Use Firebug to find where the information is stored on the client side.
|
||||
ClientSideFilteringHint3=Stage 1: Examine the hidden table to see if there is anyone listed who is not in the drop down menu.
|
||||
ClientSideFilteringHint4=Stage 1: Look in the last row of the hidden table.
|
||||
ClientSideFilteringHint1=The information displayed when an employee is chosen from the drop down menu is stored on the client side.
|
||||
ClientSideFilteringHint2=Use Firebug to find where the information is stored on the client side.
|
||||
ClientSideFilteringHint3=Examine the hidden table to see if there is anyone listed who is not in the drop down menu.
|
||||
ClientSideFilteringHint4=Look in the last row of the hidden table.
|
||||
ClientSideFilteringHint5a=Stage 1: You can access the server directly
|
||||
ClientSideFilteringHint5b=here
|
||||
ClientSideFilteringHint5c=to see what results are being returned
|
||||
@ -22,5 +22,6 @@ ClientSideFilteringHint7=Stage 2: The query currently returns all of the content
|
||||
ClientSideFilteringHint8=Stage 2: The query should only return the information of employees who are managed by Moe Stooge, whose userID is 102
|
||||
ClientSideFilteringHint9=Stage 2: Try using a filter operator.
|
||||
ClientSideFilteringHint10=Stage 2: Your filter operator should look something like: [Managers/Manager/text()=
|
||||
ClientSideFilteringInstructions1=STAGE 1: 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 shouldn't have access to the CEO's information. For this exercise, examine the contents of the page to see what extra information you can find.
|
||||
ClientSideFilteringInstructions1=STAGE 1: 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, . Or at least you shouldn't have access to the CEO's information. For this exercise, examine the contents of the page to see what extra information you can find.
|
||||
ClientSideFilteringInstructions2=STAGE 2: Now, fix the problem. Modify the server to only return results that Moe Stooge is allowed to see.
|
||||
ClientSideFiltering.incorrect=This is not the salary from Neville Bartholomew...
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
@ -0,0 +1,5 @@
|
||||
== Salary manager
|
||||
|
||||
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 shouldn't have access to the CEO's information. For this assignment,
|
||||
examine the contents of the page to see what extra information you can find.
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
@ -1,9 +1,9 @@
|
||||
|
||||
== HTTP Proxy Overview
|
||||
|
||||
Many times proxies are used as a way of accessing otehrwise blocked content. A user might connect to server A, which relays content from server B
|
||||
... Because Server B is blocked wihtin the user's network. That's not the use case we will be dealing with here, but the concept is the same.
|
||||
HTTP Proxies receive requesets from a client and relay them. They also typically record them. They act as a man-in-the-middle (keep that in mind if you decide to
|
||||
Many times proxies are used as a way of accessing otherwise blocked content. A user might connect to server A, which relays content from server B
|
||||
... Because Server B is blocked within the user's network. That's not the use case we will be dealing with here, but the concept is the same.
|
||||
HTTP Proxies receive requests from a client and relay them. They also typically record them. They act as a man-in-the-middle (keep that in mind if you decide to
|
||||
use a proxy server to connect to some other system that is otherwise blocked). We won't get into HTTP vs HTTPS just yet, but that's an important topic in
|
||||
relationship to proxies.
|
||||
|
||||
@ -17,4 +17,4 @@ analyzing the security of a website.
|
||||
|
||||
ZAP specifically can also be used in the development process in a CI/CD, DevOps or otherwise automated build/test environment. This lesson does
|
||||
not currently have any details on that, but it is worth mentioning. There are a number of examples on the internet of it being integrated into a
|
||||
CI/CD with Jenkins, maven or other build processes.
|
||||
CI/CD with Jenkins, Maven or other build processes.
|
@ -24,7 +24,7 @@
|
||||
</goals>
|
||||
<configuration>
|
||||
<backend>html</backend>
|
||||
<sourceDirectory>src/main/resources/plugin/CrossSiteScripting/lessonPlans/en/</sourceDirectory>
|
||||
<sourceDirectory>src/main/resources/lessonPlans/en/</sourceDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
|
@ -64,7 +64,7 @@
|
||||
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
|
||||
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
|
||||
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content5.adoc"></div>
|
||||
<img align="middle" th:src="@{/plugin_lessons/plugin/CrossSiteScripting/images/Reflected-XSS.png}" />
|
||||
<img align="middle" th:src="@{/images/Reflected-XSS.png}" />
|
||||
</div>
|
||||
<div class="lesson-page-wrapper">
|
||||
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 140 KiB |
@ -5,7 +5,7 @@
|
||||
<div class="lesson-page-wrapper">
|
||||
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
|
||||
<!-- include content here, or can be placed in another location. Content will be presented via asciidocs files,
|
||||
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
|
||||
which you put in src/main/resources/lessonplans/{lang}/{fileName}.adoc -->
|
||||
<div class="adoc-content" th:replace="doc:HttpBasics_plan.adoc"></div>
|
||||
</div>
|
||||
|
Before Width: | Height: | Size: 279 KiB After Width: | Height: | Size: 279 KiB |
Before Width: | Height: | Size: 200 KiB After Width: | Height: | Size: 200 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 205 KiB After Width: | Height: | Size: 205 KiB |
Before Width: | Height: | Size: 406 KiB After Width: | Height: | Size: 406 KiB |
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 210 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
@ -1,15 +1,15 @@
|
||||
=== Use the intercept
|
||||
|
||||
To incercept a request, you start by clicking the green button. This will set a break point for the next request.
|
||||
To intercept a request, you start by clicking the green button. This will set a break point for the next request.
|
||||
|
||||
image::plugin_lessons/plugin/HttpProxies/images/proxy-intercept-button.png[Set break/intercept button,style="lesson-image"]
|
||||
image::images/proxy-intercept-button.png[Set break/intercept button,style="lesson-image"]
|
||||
|
||||
NOTE: It is also possible set breakpoints that are triggered on conditions. That won't be covered in this lesson though. You are encouraged to explore.
|
||||
That's part of what hackers do ... explore!
|
||||
|
||||
Once you are interecepting requests and a request is made, it should look something like this:
|
||||
Once you are intercepting requests and a request is made, it should look something like this:
|
||||
|
||||
image::plugin_lessons/plugin/HttpProxies/images/proxy-intercept-details.png[ZAP history tab,1269,337,style="lesson-image"]
|
||||
image::images/proxy-intercept-details.png[ZAP history tab,1269,337,style="lesson-image"]
|
||||
|
||||
=== Intercept and modify a request
|
||||
|
@ -15,7 +15,7 @@ Once you have 'installed' ZAP (you don't really install it, just unpack it and r
|
||||
=== Start ZAP
|
||||
When ZAP starts, you will be presented with a dialog such as the one below ...
|
||||
|
||||
image::plugin_lessons/plugin/HttpProxies/images/zap-start.png[ZAP Start,548,256,style="lesson-image"]
|
||||
image::images/zap-start.png[ZAP Start,548,256,style="lesson-image"]
|
||||
|
||||
=== Configure Proxy's Port
|
||||
|
||||
@ -24,4 +24,4 @@ image::plugin_lessons/plugin/HttpProxies/images/zap-start.png[ZAP Start,548,256,
|
||||
. Choose an available port ... Since WebGoat is using port 8080, use something different like 8090
|
||||
. Click OK
|
||||
|
||||
image::plugin_lessons/plugin/HttpProxies/images/zap-local-proxy.png[ZAP local proxy,800,648,style="lesson-image"]
|
||||
image::images/zap-local-proxy.png[ZAP local proxy,800,648,style="lesson-image"]
|
@ -14,7 +14,7 @@ This will send all of your traffic to the proxy. Since we haven't set up a trust
|
||||
.. input *8090* as the port
|
||||
.. check the _Use this proxy server for all protocols_ checkbox
|
||||
|
||||
image::plugin_lessons/plugin/HttpProxies/images/firefox-proxy-config.png[Firefox Proxy Config,510,634,style="lesson-image"]
|
||||
image::images/firefox-proxy-config.png[Firefox Proxy Config,510,634,style="lesson-image"]
|
||||
|
||||
=== Chrome Proxy Config
|
||||
|
||||
@ -26,7 +26,7 @@ image::plugin_lessons/plugin/HttpProxies/images/firefox-proxy-config.png[Firefox
|
||||
. Input 127..0.0.1 in the first box under _Web Proxy Server_ and your port # (8090 is what used earlier) 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::plugin_lessons/plugin/HttpProxies/images/chrome-manual-proxy.png[Chrome Proxy Config,700,447,style="lesson-image"]
|
||||
image::images/chrome-manual-proxy.png[Chrome Proxy Config,700,447,style="lesson-image"]
|
||||
|
||||
=== Other Proxy Configuration Options
|
||||
|
@ -3,4 +3,4 @@
|
||||
You should now be able to browse somewhere. We suggest starting with a plain http host.
|
||||
If it's working, ZAP's history tab will start to look something like this.
|
||||
|
||||
image::plugin_lessons/plugin/HttpProxies/images/zap-history.png[ZAP history tab,1269,337,style="lesson-image"]
|
||||
image::images/zap-history.png[ZAP history tab,1269,337,style="lesson-image"]
|
@ -25,7 +25,7 @@
|
||||
</goals>
|
||||
<configuration>
|
||||
<backend>html</backend>
|
||||
<sourceDirectory>src/main/resources/plugin/IDOR/lessonPlans/en/</sourceDirectory>
|
||||
<sourceDirectory>src/main/resources/lessonPlans/en/</sourceDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
|
@ -11,7 +11,6 @@ import org.springframework.web.bind.annotation.*;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.Path;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|