Adjust lesson template (#704)

* Remove method `getId()` from all lessons as it defaults to the class name

* remove clean up endpoint

* remove unused class `RequestParameter`

* remove unused class `PluginLoadingFailure`

* Move `CourseConfiguration` to lesson package

* Add more content around the lesson template lesson and make it visible as a lesson in WebGoat

* Remove explicit invocation `trackProgress()` inside WebGoat framework so assignments only need to return an `AttackResult`

* Put original solution back as well for SQL string injection

* review comments

* Add
This commit is contained in:
Nanne Baars
2019-11-17 13:39:56 +01:00
committed by René Zubcevic
parent f40b6ffd31
commit 5dd6b31905
139 changed files with 769 additions and 870 deletions

View File

@ -22,17 +22,13 @@ There are a number of moving parts and this sample lesson will help you navigate
```
1. The Base Class
* The name of the class (file and class name) to better match your lesson. (e.g. `sql-injection` >> `SqlInjection`)
* The category in which you want your lesson to be in. You can create a new category if you want, or put in an issue to have one added.
* The `defaultRanking` will move your lesson up or down in the categories list.
* The name of the class (file and class name) to better match your lesson. (e.g. `sql-injection` >> `SqlInjection`)
* The category in which you want your lesson to be in. You can create a new category if you want, or put in an issue to have one added.
* Implement a new key name pair `lesson-template.title` (the key) and update the same key/value pair `your.key=your value` in src/main/resources/i18n/WebGoatLabels.properties.
* Implement a new value for the `getId` method, which leads us to...
2. The HTML content framing
* Rename the provided file in src/main/resources/html using your value from the `getId` method in your lesson's base class:
e.g.
`public String getId() { return "your-lesson"; }` >> `your-lesson.html`
* Rename the provided file in src/main/resources/html using the name of the lesson class.
* Modify that file following the commented instructions in there.
* In conjunction with this file you.
@ -41,7 +37,6 @@ There are a number of moving parts and this sample lesson will help you navigate
* You can also create supporting (non-assignment) endpoints, that are not evaluated/graded.
* See other lesson examples for creating unit/integration tests for your project as well.
4. Getting your lesson to show up
* Modify the webgoat-lessons/pom.xml to include your project in the `<modules>` section:
@ -63,8 +58,8 @@ There are a number of moving parts and this sample lesson will help you navigate
<version>${project.version}</version>
</dependency>
<!-- .... >
</dependencies>
```
</dependencies>
```
5. You should be ready to run and test your project. Please create issues at https://github.com/WebGoat/WebGoat if there errors or confusion with this documentation/template

View File

@ -1,38 +1,31 @@
/*
* 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.template;
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/
* <p>
* Copyright (c) 2002 - 20014 Bruce Mayhew
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* Getting Source ==============
* <p>
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
* <p>
*
* @author misfir3
* @version $Id: $Id
* @since January 3, 2017
*/
@Component
public class LessonTemplate extends Lesson {
@ -45,10 +38,4 @@ public class LessonTemplate extends Lesson {
public String getTitle() {
return "lesson-template.title";
}
@Override
public String getId() {
return "LessonTemplate";
}
}

View File

@ -22,24 +22,22 @@
package org.owasp.webgoat.template;
import lombok.AllArgsConstructor;
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.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* Created by jason on 1/5/17.
*/
@RestController
@AssignmentHints({"lesson-template.hints.1", "lesson-template.hints.2", "lesson-template.hints.3"})
public class SampleAttack extends AssignmentEndpoint {
String secretValue = "secr37Value";
@ -48,28 +46,40 @@ public class SampleAttack extends AssignmentEndpoint {
@Autowired
UserSessionData userSessionData;
@GetMapping(path = "/lesson-template/sample-attack", produces = {"application/json"})
public @ResponseBody
AttackResult completed(String param1, String param2, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
@PostMapping("/lesson-template/sample-attack")
@ResponseBody
public AttackResult completed(@RequestParam("param1") String param1, @RequestParam("param2") String param2) {
if (userSessionData.getValue("some-value") != null) {
// do any session updating you want here ... or not, just comment/example here
//return trackProgress(failed().feedback("lesson-template.sample-attack.failure-2").build());
//return failed().feedback("lesson-template.sample-attack.failure-2").build());
}
//overly simple example for success. See other existing lesssons for ways to detect 'success' or 'failure'
if (secretValue.equals(param1)) {
return trackProgress(success()
return success(this)
.output("Custom Output ...if you want, for success")
.feedback("lesson-template.sample-attack.success")
.build());
.build();
//lesson-template.sample-attack.success is defined in src/main/resources/i18n/WebGoatLabels.properties
}
// else
return trackProgress(failed()
return failed(this)
.feedback("lesson-template.sample-attack.failure-2")
.output("Custom output for this failure scenario, usually html that will get rendered directly ... yes, you can self-xss if you want")
.build());
.build();
}
@GetMapping("lesson-template/shop/{user}")
@ResponseBody
public List<Item> getItemsInBasket(@PathVariable("user") String user) {
return List.of(new Item("WG-1", "WebGoat promo", 12.0), new Item("WG-2", "WebGoat sticker", 0.00));
}
@AllArgsConstructor
private class Item {
private String number;
private String description;
private double price;
}
}

View File

@ -0,0 +1,13 @@
--CREATE TABLE servers(
-- id varchar(10),
-- hostname varchar(20),
-- ip varchar(20),
-- mac varchar(20),
-- status varchar(20),
-- description varchar(40)
--);
--INSERT INTO servers VALUES ('1', 'webgoat-dev', '192.168.4.0', 'AA:BB:11:22:CC:DD', 'online', 'Development server');
--INSERT INTO servers VALUES ('2', 'webgoat-tst', '192.168.2.1', 'EE:FF:33:44:AB:CD', 'online', 'Test server');
--INSERT INTO servers VALUES ('3', 'webgoat-acc', '192.168.3.3', 'EF:12:FE:34:AA:CC', 'offline', 'Acceptance server');
--INSERT INTO servers VALUES ('4', 'webgoat-pre-prod', '192.168.6.4', 'EF:12:FE:34:AA:CC', 'offline', 'Pre-production server');
--INSERT INTO servers VALUES ('4', 'webgoat-prd', '104.130.219.202', 'FA:91:EB:82:DC:73', 'out of order', 'Production server');

View File

@ -7,12 +7,34 @@
<div class="adoc-content" th:replace="doc:lesson-template-intro.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse the above 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:lesson-template-content.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse the above 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:lesson-template-video.adoc"></div>
<!-- can use multiple adoc's in a page-wrapper if you want ... or not-->
<div class="adoc-content" th:replace="doc:lesson-template-video-more.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:lesson-template-lesson-class.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:lesson-template-glue.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse the above 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:lesson-template-attack.adoc"></div>
<!-- WebGoat will automatically style and scaffold some functionality by using the div.attack-container as below -->
@ -23,10 +45,9 @@
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
<!-- modify the action to point to the intended endpoint and set other attributes as desired -->
<script th:src="@{/lesson_js/idor.js}" />
<form class="attack-form" accept-charset="UNKNOWN"
method="GET" name="form"
action="/WebGoat/lesson-template/sample-attack"
method="POST" name="form"
action="/WebGoat/lesson-template/sample-attack"
enctype="application/json;charset=UTF-8">
<table>
<tr>
@ -34,8 +55,7 @@
<td>parameter 1:<input name="param1" value="" type="TEXT" /></td>
<td>parameter 2:<input name="param2" value="" type="TEXT" /></td>
<td>
<input
name="submit" value="Submit" type="SUBMIT"/>
<input name="submit" value="Submit" type="SUBMIT"/>
</td>
</tr>
</table>
@ -51,4 +71,8 @@
<!-- repeat and mix-and-match the lesson-page-wrappers with or wihtout the attack-containers as you like ...
see other lessons for other more complex examples -->
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:lesson-template-database.adoc"></div>
</div>
</html>

View File

@ -1,4 +1,8 @@
lesson-template.title=Lesson Template
lesson-template.title=Writing new lesson
lesson-template.hints.1=Hint 1
lesson-template.hints.2=Hint 2
lesson-template.hints.3=Hint 3
lesson-template.sample-attack.failure-1=Sample failure message
lesson-template.sample-attack.failure-2=Sample failure message 2

View File

@ -1,83 +1,51 @@
=== Attack Explanation
=== Step 4: Add an assignment to your lesson
Each lesson can contain multiple assignments, first let's define a lesson class in Java
With an assignment a user can practise within a lesson. A lesson can consist of multiple assignment, each assignment
needs to extend the class `AssignmentEndpoint`, let's look at an example:
[source]
[source,java]
----
@Component
public class LessonTemplate extends AbstractLesson {
@Override
public Category getDefaultCategory() {
return Category.GENERAL;
}
@RestController // <1>
@AssignmentHints({"lesson-template.hints.1", "lesson-template.hints.2", "lesson-template.hints.3"}) // <2>
public class SampleAttack extends AssignmentEndpoint { // <3>
@Override
public List<String> getHints() {
return Lists.newArrayList();
}
private final String secretValue = "secr37Value";
@Override
public Integer getDefaultRanking() {
return 30;
}
@Override
public String getTitle() {
return "lesson-template.title";
}
@Override
public String getId() {
return "LessonTemplate";
}
}
----
This implementation is quite straightforward. Now for an assignment you need to implement:
[source]
----
@RestController
public class SampleAttack extends AssignmentEndpoint {
String secretValue = "secr37Value";
//UserSessionData is bound to session and can be used to persist data across multiple assignments
@Autowired
UserSessionData userSessionData;
private UserSessionData userSessionData; // <4>
@GetMapping(path = "/lesson-template/sample-attack", produces = {"application/json"})
@PostMapping("/lesson-template/sample-attack") <5>
@ResponseBody
public AttackResult completed(String param1, String param2, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
public AttackResult completed(@RequestParam("param1") String param1, @RequestParam("param2") String param2) { <6>
if (userSessionData.getValue("some-value") != null) {
// do any session updating you want here ... or not, just comment/example here
//return trackProgress(failed().feedback("lesson-template.sample-attack.failure-2").build());
//return failed(this).feedback("lesson-template.sample-attack.failure-2").build();
}
//overly simple example for success. See other existing lesssons for ways to detect 'success' or 'failure'
if (secretValue.equals(param1)) {
return trackProgress(success()
return success(this) // <7>
.output("Custom Output ...if you want, for success")
.feedback("lesson-template.sample-attack.success")
.build());
.build();
//lesson-template.sample-attack.success is defined in src/main/resources/i18n/WebGoatLabels.properties
}
// else
return trackProgress(failed()
return failed(this) // <8>
.feedback("lesson-template.sample-attack.failure-2")
.output("Custom output for this failure scenario, usually html that will get rendered directly ... yes, you can self-xss if you want")
.build());
.build();
}
@GetMapping("lesson-template/shop/{user}")
@ResponseBody
public List<Items> getItemsInBasket(@PathVariable("user") String user) {
....
}
}
----
<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
<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`
<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:
@ -90,9 +58,20 @@ public AttackResult solve(String param) {
}
----
Other endpoints can be added in the assignment to support different cases for the assignment.
=== Extra endpoints
### Glue between html and assignment
Other endpoints can be added in the assignment to support different cases for the assignment, for example:
[source]
----
@GetMapping("lesson-template/shop/{user}")
@ResponseBody
public List<Item> getItemsInBasket(@PathVariable("user") String user) {
return List.of(new Item("WG-1", "WebGoat promo", 12.0), new Item("WG-2", "WebGoat sticker", 0.00));
}
----
=== 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
@ -100,19 +79,27 @@ green when the user solves the assignment. To make this work in the html we need
[source]
----
div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<!-- using attack-form class on your form, will allow your request to be ajaxified and stay within the display framework for webgoat -->
<!-- you can write your own custom forms, but standard form submission will take you to your endpoint and outside of the WebGoat framework -->
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:lesson-template-attack.adoc"></div>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN"
method="GET" name="form"
method="POST" name="form"
action="/WebGoat/lesson-template/sample-attack"
enctype="application/json;charset=UTF-8">
....
<table>
<tr>
<td>two random params</td>
<td>parameter 1:<input name="param1" value="" type="TEXT" /></td>
<td>parameter 2:<input name="param2" value="" type="TEXT" /></td>
<td>
<input name="submit" value="Submit" type="SUBMIT"/>
</td>
</tr>
</table>
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
----
@ -120,4 +107,4 @@ div class="attack-container">
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.
That's it you now successfully created your first WebGoat lesson including an assignment!

View File

@ -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 of the constructs which are quite 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 so that it is these *.adoc files. The asciidoc files should be place in the
directory `{lesson}/src/main/resources/lessonPlans/en` the last part depends on the locale.
=== Images
Images can be referenced as below including setting style (recommended to use lesson-image as the style). The root is `{lesson}/src/main/resources/images`
image::images/firefox-proxy-config.png[Firefox Proxy Config,510,634,style="lesson-image"]
=== Code block
Code blocks can be written as follows:
```
[source]
----
public class A {
private String test;
}
----
```

View File

@ -0,0 +1,25 @@
=== 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.
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:
[source]
----
CREATE TABLE servers(
id varchar(10),
hostname varchar(20),
ip varchar(20),
mac varchar(20),
status varchar(20),
description varchar(40)
);
INSERT INTO servers VALUES ('1', 'webgoat-dev', '192.168.4.0', 'AA:BB:11:22:CC:DD', 'online', 'Development server');
INSERT INTO servers VALUES ('2', 'webgoat-tst', '192.168.2.1', 'EE:FF:33:44:AB:CD', 'online', 'Test server');
INSERT INTO servers VALUES ('3', 'webgoat-acc', '192.168.3.3', 'EF:12:FE:34:AA:CC', 'offline', 'Acceptance server');
INSERT INTO servers VALUES ('4', 'webgoat-pre-prod', '192.168.6.4', 'EF:12:FE:34:AA:CC', 'offline', 'Pre-production server');
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.

View File

@ -0,0 +1,59 @@
=== Step 3: Write glue 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:
[source]
----
<html xmlns:th="http://www.thymeleaf.org">
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:lesson-template-intro.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:lesson-template-content.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:lesson-template-lesson-class.adoc"></div>
</div>
</html>
----
This file needs to be places in: `{lesson}/src/main/resources/html/`. The name of the file should be the same as
the Java class we created in step 2.
This will create 3 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.
==== Create pom.xml
See the `pom.xml` of this project copy it to your new lesson and change the name.
==== Extend lesson project
Change the file in `webgoat-lesson/pom.xml` and add:
[source]
----
<module>new-lesson</module>
----
inside the existing `<modules>` tag.
==== Add project to WebGoat
Next step is to add a reference to the new project so WebGoat bundles it while building, open `pom.xml` in `webgoat-server/pom.xml`
and add:
[source]
----
<dependency>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>new-lesson</artifactId>
<version>${project.version}</version>
</dependency>
----
That's it start WebGoat and your lesson will appear in the menu.

View File

@ -1,19 +1,9 @@
This lesson describes the steps needed to add a new lesson to WebGoat. In general there are three steps:
== Lesson Template Intro
- 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
- Add one of more assignments within the lesson
This is the lesson template intro.
=== 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 so that it is these *.adoc files.
=== Images
Images can be referenced as below including setting style (recommended to use lesson-image as the style). The root is {lesson}/src/main/resources
image::images/firefox-proxy-config.png[Firefox Proxy Config,510,634,style="lesson-image"]
Let's see how to create a new lesson.

View File

@ -0,0 +1,20 @@
=== Step 2: adding a new lesson class
Each lesson can contain multiple assignments, first let's define a lesson class in Java
[source]
----
@Component
public class LessonTemplate extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.GENERAL;
}
@Override
public String getTitle() {
return "lesson-template.title";
}
}
----

View File

@ -0,0 +1,11 @@
=== Even more content
You can include multiple adoc files in one page, by including them in the same `<div>`:
[source]
----
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:lesson-template-video.adoc"></div>
<div class="adoc-content" th:replace="doc:lesson-template-video-more.adoc"></div>
</div>
----