Compare commits
67 Commits
v8.0.0.M15
...
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 | |||
985148ede3 | |||
9587550bc5 | |||
9a0995dae5 | |||
4e07e0ebfa | |||
6e95fdfe56 | |||
e045bc692d | |||
589872ad47 | |||
5f4889cefe | |||
e96ab488ff | |||
31f7ea6985 | |||
186f24f1df | |||
089dd56a15 | |||
6cfefba0ee | |||
20e45da8ae | |||
e34faa13d6 | |||
927bbad488 | |||
a922c00182 | |||
f21fe7f2c3 | |||
3cd349bb4b | |||
e3e7ed004f |
@ -28,12 +28,7 @@ deploy:
|
||||
on:
|
||||
repo: WebGoat/WebGoat
|
||||
tags: true
|
||||
- provider: script
|
||||
skip_cleanup: true
|
||||
script: bash scripts/deploy-webgoat.sh
|
||||
on:
|
||||
repo: WebGoat/WebGoat
|
||||
branch: develop
|
||||
branch: master
|
||||
- provider: releases
|
||||
skip_cleanup: true
|
||||
overwrite: true
|
||||
@ -45,6 +40,7 @@ deploy:
|
||||
on:
|
||||
repo: WebGoat/WebGoat
|
||||
tags: true
|
||||
branch: master
|
||||
env:
|
||||
global:
|
||||
#Docker login
|
||||
|
34
README.MD
@ -29,7 +29,18 @@ first thing that all hackers claim.*
|
||||
|
||||
# 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
|
||||
[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._
|
||||
|
||||
## 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
|
||||
|
@ -6,6 +6,7 @@ services:
|
||||
user: webgoat
|
||||
environment:
|
||||
- WEBWOLF_HOST=webwolf
|
||||
- WEBWOLF_PORT=9090
|
||||
- spring.datasource.url=jdbc:postgresql://webgoat_db:5432/webgoat
|
||||
- spring.datasource.username=webgoat
|
||||
- spring.datasource.password=webgoat
|
||||
@ -22,7 +23,7 @@ services:
|
||||
- spring.datasource.driver-class-name=org.postgresql.Driver
|
||||
- spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL94Dialect
|
||||
ports:
|
||||
- "8081:8081"
|
||||
- "9090:9090"
|
||||
db:
|
||||
container_name: webgoat_db
|
||||
image: postgres:latest
|
||||
|
@ -5,6 +5,7 @@ services:
|
||||
image: webgoat/webgoat-8.0
|
||||
environment:
|
||||
- WEBWOLF_HOST=webwolf
|
||||
- WEBWOLF_PORT=9090
|
||||
- spring.datasource.url=jdbc:hsqldb:hsql://webgoat_db:9001/webgoat
|
||||
ports:
|
||||
- "8080:8080"
|
||||
@ -15,7 +16,7 @@ services:
|
||||
environment:
|
||||
- spring.datasource.url=jdbc:hsqldb:hsql://webgoat_db:9001/webgoat
|
||||
ports:
|
||||
- "8081:8081"
|
||||
- "9090:9090"
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
|
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
@ -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
|
27
pom.xml
@ -1,11 +1,12 @@
|
||||
<?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>
|
||||
<groupId>org.owasp.webgoat</groupId>
|
||||
<artifactId>webgoat-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
|
||||
<name>WebGoat Parent Pom</name>
|
||||
<description>Parent Pom for the WebGoat Project. A deliberately insecure Web Application</description>
|
||||
@ -53,17 +54,17 @@
|
||||
<developer>
|
||||
<id>jwayman</id>
|
||||
<name>Jeff Wayman</name>
|
||||
<email />
|
||||
<email/>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>dcowden</id>
|
||||
<name>Dave Cowden</name>
|
||||
<email />
|
||||
<email/>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>lawson89</id>
|
||||
<name>Richard Lawson</name>
|
||||
<email />
|
||||
<email/>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>dougmorato</id>
|
||||
@ -92,8 +93,8 @@
|
||||
<url>https://github.com/WebGoat/WebGoat</url>
|
||||
<connection>scm:git:git@github.com:WebGoat/WebGoat.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:WebGoat/WebGoat.git</developerConnection>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
|
||||
<issueManagement>
|
||||
<system>Github Issues</system>
|
||||
@ -202,7 +203,7 @@
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>dist</artifactId>
|
||||
@ -214,7 +215,7 @@
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
@ -225,7 +226,9 @@
|
||||
</goals>
|
||||
<phase>generate-resources</phase>
|
||||
<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>
|
||||
<includes>*.jar</includes>
|
||||
</configuration>
|
||||
@ -324,7 +327,7 @@
|
||||
<artifactId>coveralls-maven-plugin</artifactId>
|
||||
<version>${coveralls-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<repoToken />
|
||||
<repoToken/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
@ -332,7 +335,7 @@
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>${cobertura-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<check />
|
||||
<check/>
|
||||
<format>xml</format>
|
||||
<maxmem>256m</maxmem>
|
||||
<!-- aggregated reports for multi-module projects -->
|
||||
|
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
@ -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
@ -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
|
||||
docker build --build-arg webgoat_version=${TRAVIS_TAG:1} -f Dockerfile -t $REPO:latest -t $REPO:${TRAVIS_TAG} .
|
||||
docker push $REPO
|
||||
elif [ ! -z "${TRAVIS_TAG}" ]; then
|
||||
# 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 push $REPO
|
||||
#elif [ ! -z "${TRAVIS_TAG}" ]; then
|
||||
# # 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 push $REPO
|
||||
#elif [ "${BRANCH}" == "develop" ]; then
|
||||
# docker build -f Dockerfile -t $REPO:snapshot .
|
||||
# docker push $REPO
|
||||
|
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>
|
||||
<groupId>org.owasp.webgoat</groupId>
|
||||
<artifactId>webgoat-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
|
||||
<profiles>
|
||||
|
@ -232,7 +232,7 @@ public class CreateDB {
|
||||
|
||||
// Create the new table
|
||||
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)" + ")";
|
||||
statement.executeUpdate(createTableStatement);
|
||||
} catch (SQLException e) {
|
||||
@ -240,11 +240,11 @@ public class CreateDB {
|
||||
}
|
||||
|
||||
// Populate
|
||||
String insertData1 = "INSERT INTO user_system_data VALUES ('101','jsnow','passwd1', '')";
|
||||
String insertData2 = "INSERT INTO user_system_data VALUES ('102','jdoe','passwd2', '')";
|
||||
String insertData3 = "INSERT INTO user_system_data VALUES ('103','jplane','passwd3', '')";
|
||||
String insertData4 = "INSERT INTO user_system_data VALUES ('104','jeff','jeff', '')";
|
||||
String insertData5 = "INSERT INTO user_system_data VALUES ('105','dave','dave', '')";
|
||||
String insertData1 = "INSERT INTO user_system_data VALUES (101,'jsnow','passwd1', '')";
|
||||
String insertData2 = "INSERT INTO user_system_data VALUES (102,'jdoe','passwd2', '')";
|
||||
String insertData3 = "INSERT INTO user_system_data VALUES (103,'jplane','passwd3', '')";
|
||||
String insertData4 = "INSERT INTO user_system_data VALUES (104,'jeff','jeff', '')";
|
||||
String insertData5 = "INSERT INTO user_system_data VALUES (105,'dave','passW0rD', '')";
|
||||
statement.executeUpdate(insertData1);
|
||||
statement.executeUpdate(insertData2);
|
||||
statement.executeUpdate(insertData3);
|
||||
|
@ -4,6 +4,7 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
@ -16,6 +17,7 @@ public class UserForm {
|
||||
|
||||
@NotNull
|
||||
@Size(min=6, max=20)
|
||||
@Pattern(regexp = "[a-zA-Z0-9-]*", message = "can only contain letters, digits, and -")
|
||||
private String username;
|
||||
@NotNull
|
||||
@Size(min=6, max=10)
|
||||
|
@ -37,7 +37,7 @@ webgoat.database.connection.string=jdbc:hsqldb:mem:{USER}
|
||||
webgoat.default.language=en
|
||||
|
||||
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.landingpage=http://${webwolf.host}:${webwolf.port}/landing
|
||||
webwolf.url.mail=http://${webwolf.host}:${webwolf.port}/mail
|
||||
|
@ -79,6 +79,7 @@ define(['jquery',
|
||||
this.listenTo(this.lessonHintView, 'hints:hideButton', this.onHideHintsButton);
|
||||
this.lessonContentView.navToPage(pageNum);
|
||||
this.lessonHintView.hideHints();
|
||||
this.lessonHintView.showFirstHint();
|
||||
//this.lessonHintView.selectHints();
|
||||
this.titleView.render(this.lessonInfoModel.get('lessonTitle'));
|
||||
return;
|
||||
@ -160,7 +161,7 @@ define(['jquery',
|
||||
}
|
||||
//
|
||||
this.lessonHintView.render();
|
||||
if (this.lessonHintView.getHintsCount > 0) {
|
||||
if (this.lessonHintView.getHintsCount() > 0) {
|
||||
this.helpControlsView.showHintsButton();
|
||||
} else {
|
||||
this.helpControlsView.hideHintsButton();
|
||||
|
@ -32,7 +32,11 @@ define(['jquery',
|
||||
}
|
||||
this.set('content',content);
|
||||
this.set('lessonUrl',document.URL.replace(/\.lesson.*/,'.lesson'));
|
||||
this.set('pageNum',document.URL.replace(/.*\.lesson\/(\d{1,4})$/,'$1'));
|
||||
if (/.*\.lesson\/(\d{1,4})$/.test(document.URL)) {
|
||||
this.set('pageNum',document.URL.replace(/.*\.lesson\/(\d{1,4})$/,'$1'));
|
||||
} else {
|
||||
this.set('pageNum',0);
|
||||
}
|
||||
this.trigger('content:loaded',this,loadHelps);
|
||||
},
|
||||
|
||||
|
@ -32,21 +32,19 @@ function($,
|
||||
|
||||
toggleLabel: function() {
|
||||
if (this.isVisible()) {
|
||||
$('show-hints-button').text('Hide hints');
|
||||
$('#show-hints-button').text('Hide hints');
|
||||
} else {
|
||||
$('show-hints-button').text('Show hints');
|
||||
$('#show-hints-button').text('Show hints');
|
||||
}
|
||||
},
|
||||
|
||||
render:function() {
|
||||
if (this.isVisible()) {
|
||||
this.$el.hide(350);
|
||||
this.$el.hide(350, this.toggleLabel.bind(this));
|
||||
} 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) {
|
||||
this.hideShowPrevNextButtons();
|
||||
}
|
||||
@ -90,9 +88,9 @@ function($,
|
||||
|
||||
hideHints: function() {
|
||||
if (this.$el.is(':visible')) {
|
||||
this.$el.hide(350);
|
||||
this.$el.hide(350, this.toggleLabel.bind(this));
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
showNextHint: function() {
|
||||
this.curHint = (this.curHint < this.hintsToShow.length -1) ? this.curHint+1 : this.curHint;
|
||||
@ -106,6 +104,12 @@ function($,
|
||||
this.displayHint(this.curHint);
|
||||
},
|
||||
|
||||
showFirstHint: function() {
|
||||
this.curHint = 0;
|
||||
this.hideShowPrevNextButtons();
|
||||
this.displayHint(this.curHint);
|
||||
},
|
||||
|
||||
displayHint: function(curHint) {
|
||||
if(this.hintsToShow.length == 0) {
|
||||
// this.hideHints();
|
||||
|
@ -123,8 +123,9 @@
|
||||
<section class="main-content-wrapper">
|
||||
<section id="main-content"> <!--ng-controller="goatLesson"-->
|
||||
<div id="lesson-page" class="pages">
|
||||
<span th:text="${numUsers}"> Users in WebGoat</span>
|
||||
<!-- iterate over users below -->su
|
||||
<span th:text="${numUsers}"></span>
|
||||
<span> Users in WebGoat</span>
|
||||
|
||||
<div sec:authorize="hasAuthority('WEBGOAT_ADMIN')">
|
||||
<h3>WebGoat Users</h3>
|
||||
<div th:each="user : ${allUsers}">
|
||||
|
2
webgoat-images/vagrant-training/Vagrantfile
vendored
@ -3,7 +3,7 @@
|
||||
Vagrant.configure(2) do |config|
|
||||
config.vm.box = "ubuntu/trusty64"
|
||||
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|
|
||||
vb.gui = false
|
||||
vb.memory = "4096"
|
||||
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
|
||||
</project>
|
||||
|
@ -6,6 +6,6 @@
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
</project>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
|
||||
|
||||
|
@ -11,7 +11,5 @@ public interface SolutionConstants {
|
||||
//TODO should be random generated when starting the server
|
||||
String PASSWORD = "!!webgoat_admin_1234!!";
|
||||
String PASSWORD_TOM = "thisisasecretfortomonly";
|
||||
String PASSWORD_LARRY = "larryknows";
|
||||
String JWT_PASSWORD = "victory";
|
||||
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
|
||||
challenge2.title=Get it for free
|
||||
challenge3.title=Photo comments
|
||||
challenge4.title=Voting
|
||||
challenge5.title=Without password
|
||||
challenge6.title=Creating a new account
|
||||
challenge7.title=Admin password reset
|
||||
|
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>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
|
||||
</project>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
<build>
|
||||
<plugins>
|
||||
|
@ -49,10 +49,7 @@ import org.owasp.encoder.*;
|
||||
|
||||
import static org.springframework.http.MediaType.ALL_VALUE;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||
|
||||
@ -72,20 +69,19 @@ public class StoredXssComments extends AssignmentEndpoint {
|
||||
comments.add(new Comment("secUriTy", DateTime.now().toString(fmt), "<script>console.warn('unit test me')</script>Comment for Unit Testing"));
|
||||
comments.add(new Comment("webgoat", DateTime.now().toString(fmt), "This comment is safe"));
|
||||
comments.add(new Comment("guest", DateTime.now().toString(fmt), "This one is safe too."));
|
||||
comments.add(new Comment("guest", DateTime.now().toString(fmt), "Can you post a comment, calling webgoat.customjs.phoneHome() ?"));
|
||||
comments.add(new Comment("guest", DateTime.now().toString(fmt), "Can you post a comment, calling webgoat.customjs.phoneHome() ?"));
|
||||
}
|
||||
|
||||
@RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE,consumes = ALL_VALUE)
|
||||
@ResponseBody
|
||||
public Collection<Comment> retrieveComments() {
|
||||
Collection<Comment> allComments = Lists.newArrayList();
|
||||
List<Comment> allComments = Lists.newArrayList();
|
||||
Collection<Comment> newComments = userComments.get(webSession.getUserName());
|
||||
allComments.addAll(comments);
|
||||
if (newComments != null) {
|
||||
allComments.addAll(newComments);
|
||||
}
|
||||
|
||||
allComments.addAll(comments);
|
||||
|
||||
Collections.reverse(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-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-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-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.
|
||||
|
@ -4,7 +4,7 @@ You should have been able to execute script with the last example. At this point
|
||||
|
||||
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):
|
||||
|
||||
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.
|
||||
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).
|
||||
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 ...
|
||||
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/*
|
||||
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 test route for this test code?
|
||||
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.
|
@ -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.
|
||||
|
||||
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>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
</project>
|
@ -64,11 +64,11 @@ public class CSRFFeedback extends AssignmentEndpoint {
|
||||
|
||||
private boolean hostOrRefererDifferentHost(HttpServletRequest request) {
|
||||
String referer = request.getHeader("referer");
|
||||
String origin = request.getHeader("origin");
|
||||
String host = request.getHeader("host");
|
||||
if (referer != null) {
|
||||
return !referer.contains(origin);
|
||||
return !referer.contains(host);
|
||||
} 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"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
<input name="csrf" type="hidden" value="false"/>
|
||||
<input type="submit" name="ubmit="/>
|
||||
<input type="submit" name="submit"/>
|
||||
|
||||
</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"]
|
||||
|
||||
{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
|
||||
based on your own username prefixed with csrf. So if your username is `tom` you must create
|
||||
a new user called `csrf-tom`
|
||||
In this assignment try to see if WebGoat is also vulnerable for a login CSRF attack.
|
||||
Leave this tab open and in another tab create a user based on your own username prefixed with `csrf-`.
|
||||
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")
|
||||
.contentType(MediaType.TEXT_PLAIN)
|
||||
.cookie(new Cookie("JSESSIONID", "test"))
|
||||
.header("origin", "localhost:8080")
|
||||
.header("host", "localhost:8080")
|
||||
.header("referer", "webgoat.org")
|
||||
.content("{\"name\": \"Test\", \"email\": \"test1233@dfssdf.de\", \"subject\": \"service\", \"message\":\"dsaffd\"}"))
|
||||
.andExpect(jsonPath("lessonCompleted", is(true)))
|
||||
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
|
||||
</project>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@ -3,36 +3,27 @@
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
|
||||
<!-- include content here. 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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro3.adoc"></div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<!-- stripped down without extra comments -->
|
||||
<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="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"
|
||||
@ -48,4 +39,8 @@
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro6.adoc"></div>
|
||||
</div>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 170 KiB |
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.
|
||||
That's part of what hackers do ... explore!
|
||||
image::images/zap_exclude_url.png[Exclude internal APIs from WebGoat,style="lesson-image"]
|
||||
|
||||
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>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
|
||||
</project>
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.owasp.webgoat.plugin;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.jsonwebtoken.impl.TextCodec;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||
@ -23,7 +24,7 @@ import java.util.List;
|
||||
@AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"})
|
||||
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 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.JwtException;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.impl.TextCodec;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||
@ -25,7 +26,6 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
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;
|
||||
@ -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"})
|
||||
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 int totalVotes = 38929;
|
||||
@ -143,7 +143,6 @@ public class JWTVotesEndpoint extends AssignmentEndpoint {
|
||||
Claims claims = (Claims) jwt.getBody();
|
||||
boolean isAdmin = Boolean.valueOf((String) claims.get("admin"));
|
||||
if (!isAdmin) {
|
||||
votes.values().forEach(vote -> vote.reset());
|
||||
return trackProgress(failed().feedback("jwt-only-admin").build());
|
||||
} else {
|
||||
votes.values().forEach(vote -> vote.reset());
|
||||
|
@ -16,8 +16,10 @@
|
||||
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/jwt.css}"/>
|
||||
<script th:src="@{/lesson_js/bootstrap.min.js}" language="JavaScript"></script>
|
||||
<script th:src="@{/lesson_js/jwt-signing.js}" language="JavaScript"></script>
|
||||
<script th:src="@{/lesson_js/jwt-voting.js}" language="JavaScript"></script>
|
||||
<div class="attack-container">
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></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"
|
||||
method="POST"
|
||||
@ -37,16 +39,16 @@
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-left">
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
onclick="javascript:login('Guest')"
|
||||
onclick="javascript:loginVotes('Guest')"
|
||||
th:text="Guest">current</a></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
onclick="javascript:login('Tom')"
|
||||
onclick="javascript:loginVotes('Tom')"
|
||||
th:text="Tom">current</a></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
onclick="javascript:login('Jerry')"
|
||||
onclick="javascript:loginVotes('Jerry')"
|
||||
th:text="Jerry">current</a></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
onclick="javascript:login('Sylvester')"
|
||||
onclick="javascript:loginVotes('Sylvester')"
|
||||
th:text="Sylvester">current</a></li>
|
||||
</ul>
|
||||
<button type="button" class="btn btn-default fa fa-refresh" title="Refresh votes"
|
||||
@ -70,8 +72,7 @@
|
||||
</form>
|
||||
|
||||
<br/>
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -110,7 +111,7 @@
|
||||
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/jwt.css}"/>
|
||||
<script th:src="@{/lesson_js/bootstrap.min.js}" language="JavaScript"></script>
|
||||
<script th:src="@{/lesson_js/jwt-refresh.js}" language="JavaScript"></script>
|
||||
<script th:src="@{/lesson_js/jwt-buy.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>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
@ -135,9 +136,11 @@
|
||||
<tr>
|
||||
<td class="col-sm-8 col-md-6">
|
||||
<div class="media">
|
||||
<img class="media-object" src="http://icons.iconarchive.com/icons/custom-icon-design/flatastic-2/72/product-icon.png" style="width: 72px; height: 72px;"></img>
|
||||
<img class="media-object" th:src="@{/images/product-icon.png}"
|
||||
style="width: 72px; height: 72px;"></img>
|
||||
<div class="media-body">
|
||||
<h4 class="media-heading"><a href="#">Learn defending your application with WebGoat</a></h4>
|
||||
<h4 class="media-heading"><a href="#">Learn to defend your application with
|
||||
WebGoat</a></h4>
|
||||
<h5 class="media-heading"> by <a href="#">WebGoat Publishing</a></h5>
|
||||
<span>Status: </span><span
|
||||
class="text-success"><strong>In Stock</strong></span>
|
||||
@ -147,8 +150,11 @@
|
||||
<td class="col-sm-1 col-md-1" style="text-align: center">
|
||||
<input type="text" class="form-control" id="quantity1" value="3"></input>
|
||||
</td>
|
||||
<td class="col-sm-1 col-md-1 text-center"><strong>$4.87</strong></td>
|
||||
<td class="col-sm-1 col-md-1 text-center"><strong>$14.61</strong></td>
|
||||
<td class="col-sm-1 col-md-1 text-center"><strong>$
|
||||
<span id="piecePrice1">4.87</span></strong>
|
||||
</td>
|
||||
<td class="col-sm-1 col-md-1 text-center"><strong>$<span
|
||||
id="totalPrice1">14.61</span></strong></td>
|
||||
<td class="col-sm-1 col-md-1">
|
||||
<button type="button" class="btn btn-danger">
|
||||
<span class="glyphicon glyphicon-remove"></span> Remove
|
||||
@ -158,7 +164,9 @@
|
||||
<tr>
|
||||
<td class="col-md-6">
|
||||
<div class="media">
|
||||
<img class="media-object" src="http://icons.iconarchive.com/icons/custom-icon-design/flatastic-2/72/product-icon.png" style="width: 72px; height: 72px;"></img>
|
||||
<img class="media-object"
|
||||
th:src="@{/images/product-icon.png}"
|
||||
style="width: 72px; height: 72px;"></img>
|
||||
<div class="media-body">
|
||||
<h4 class="media-heading"><a href="#">Pentesting for professionals</a></h4>
|
||||
<h5 class="media-heading"> by <a href="#">WebWolf Publishing</a></h5>
|
||||
@ -166,11 +174,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col-md-1" style="text-align: center">
|
||||
<td class="col-sm-1 col-md-1" style="text-align: center">
|
||||
<input type="text" class="form-control" id="quantity2" value="2"></input>
|
||||
</td>
|
||||
<td class="col-md-1 text-center"><strong>$4.99</strong></td>
|
||||
<td class="col-md-1 text-center"><strong>$9.98</strong></td>
|
||||
<td class="col-sm-1 col-md-1 text-center"><strong>$<span id="piecePrice2">4.99</span></strong>
|
||||
</td>
|
||||
<td class="col-sm-1 col-md-1 text-center"><strong>$<span id="totalPrice2">9.98</span></strong></td>
|
||||
<td class="col-md-1">
|
||||
<button type="button" class="btn btn-danger">
|
||||
<span class="glyphicon glyphicon-remove"></span> Remove
|
||||
@ -185,8 +194,8 @@
|
||||
<td> </td>
|
||||
<td><h5>Subtotal<br></br>Estimated shipping</h5>
|
||||
<h3>Total</h3></td>
|
||||
<td class="text-right"><h5><strong>$24.59<br></br>$6.94</strong></h5>
|
||||
<h3>$31.53</h3></td>
|
||||
<td class="text-right"><h5><strong>$<span id="subtotalJwt">24.59</span><br></br>$6.94</strong></h5>
|
||||
<h3>$<span id="totalJwt">31.53</span></h3></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
|
BIN
webgoat-lessons/jwt/src/main/resources/images/product-icon.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
36
webgoat-lessons/jwt/src/main/resources/js/jwt-buy.js
Normal file
@ -0,0 +1,36 @@
|
||||
$(document).ready(function () {
|
||||
$("#quantity1").on("blur", function () {
|
||||
var quantity = $("#quantity1").val();
|
||||
if (!$.isNumeric(quantity) || quantity < 0) {
|
||||
$("#quantity1").val("1");
|
||||
quantity = 1;
|
||||
}
|
||||
var piecePrice = $("#piecePrice1").text();
|
||||
$('#totalPrice1').text((quantity * piecePrice).toFixed(2));
|
||||
updateTotal();
|
||||
});
|
||||
$("#quantity2").on("blur", function () {
|
||||
var quantity = $("#quantity2").val();
|
||||
if (!$.isNumeric(quantity) || quantity < 0) {
|
||||
$("#quantity2").val("1");
|
||||
quantity = 1;
|
||||
}
|
||||
var piecePrice = $("#piecePrice2").text();
|
||||
$('#totalPrice2').text((quantity * piecePrice).toFixed(2));
|
||||
updateTotal();
|
||||
})
|
||||
})
|
||||
|
||||
function updateTotal() {
|
||||
var price1 = parseFloat($('#totalPrice1').text());
|
||||
var price2 = parseFloat($('#totalPrice2').text());
|
||||
var subTotal = price1 + price2;
|
||||
$('#subtotalJwt').text(subTotal.toFixed(2));
|
||||
var total = subTotal + 6.94;
|
||||
$('#totalJwt').text(total.toFixed(2));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
$(document).ready(function () {
|
||||
login('Guest');
|
||||
loginVotes('Guest');
|
||||
})
|
||||
|
||||
function login(user) {
|
||||
function loginVotes(user) {
|
||||
$("#name").text(user);
|
||||
$.ajax({
|
||||
url: 'JWT/votings/login?user=' + user,
|
@ -1,8 +1,11 @@
|
||||
:linkattrs:
|
||||
|
||||
|
||||
== Refreshing a token
|
||||
|
||||
=== Introduction
|
||||
|
||||
In this section we touch upon refreshing an access token. There are many solutions some might
|
||||
In this section we touch upon refreshing an access token.
|
||||
|
||||
=== Types of tokens
|
||||
|
||||
@ -31,7 +34,7 @@ The server returns:
|
||||
```
|
||||
|
||||
As you can see the refresh token is a random string which the server can keep track of (in memory or store in a database)
|
||||
With storing you can match the refresh token to the specific user the refresh token was granted to.
|
||||
in order to match the refresh token to the user the refresh token was granted to.
|
||||
So in this case whenever the access token is still valid we can speak of a "stateless" session, there is
|
||||
no burden on the server side to setup the user session, the token is self contained.
|
||||
When the access token is no longer valid the server needs to query for the stored refresh token to make sure the token
|
||||
@ -51,7 +54,7 @@ Regardless of the chosen solution you should store enough information on the ser
|
||||
is still trusted. You can think of many things, like store the ip address, keep track of how many times the refresh
|
||||
token is used (using the refresh token multiple times in the valid time window of the access token might indicate strange
|
||||
behavior, you can revoke all the tokens an let the user authenticate again).
|
||||
It is also a good to keep track of which access token belonged to which refresh token. Otherwise an attacker might
|
||||
Also keep track of which access token belonged to which refresh token otherwise an attacker might
|
||||
be able to get a new access token for a different user with the refresh token of the attacker
|
||||
(see https://emtunc.org/blog/11/2017/jwt-refresh-token-manipulation/ for a nice write up about how this attack works)
|
||||
Also a good thing to check for is the ip address or geolocation of the user. If you need to give out a new token check
|
||||
@ -60,19 +63,26 @@ whether the location is still the same if not revoke all the tokens and let the
|
||||
=== 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
|
||||
about storing tokens there are two option: 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
|
||||
there is a difference of course, the access token is send when you make an API call, the refresh token is only send
|
||||
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 chances are the refresh token will also be compromised. Most of the time
|
||||
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
|
||||
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
|
||||
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
|
||||
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).
|
||||
|
||||
=== JWT a good idea?
|
||||
|
||||
There are a lot of resources available which question the usecase for using JWT token for client to server authentication
|
||||
with regards to cookies. The best place to use a JWT token is between server to server communication. In a normal web
|
||||
application you are better of using plain old cookies. See for more information:
|
||||
|
||||
- http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/[stop-using-jwt-for-sessions, window="_blank"]
|
||||
- http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work/[stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work, window="_blank"]
|
||||
- http://cryto.net/~joepie91/blog/attachments/jwt-flowchart.png[flowchart, window="_blank"]
|
||||
|
||||
|
@ -1,5 +1,10 @@
|
||||
:linkattrs:
|
||||
|
||||
== Refreshing a token
|
||||
|
||||
It is important to implement a good strategy for refreshing an access token. This assignment is based on a vulnerability
|
||||
found in a private bug bounty program on Bugcrowd, you can read the full write up https://emtunc.org/blog/11/2017/jwt-refresh-token-manipulation/[here, window="_blank"]
|
||||
|
||||
=== Assignment
|
||||
|
||||
From a breach of last year the following logfile is available link:images/logs.txt[here]
|
||||
|
@ -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.
|
||||
|
||||
```
|
||||
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQsImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJ0b21Ad2ViZ29hdC5jb20iLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.m-jSyfYEsVzD3CBI6N39wZ7AcdKdp_GiO7F_Ym12u-0
|
||||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQsImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJ0b21Ad2ViZ29hdC5jb20iLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.vPe-qQPOt78zK8wrbN1TjNJj3LeX9Qbch6oo23RUJgM
|
||||
```
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
|
||||
</project>
|
||||
|
@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
@AssignmentPath("/access-control/user-hash")
|
||||
@AssignmentHints({"access-control.hash.hint1","access-control.hash.hint2","access-control.hash.hint3",
|
||||
"access-control.hash.hint4","access-control.hash.hint5","access-control.hash.hint6","access-control.hash.hint7",
|
||||
"access-control.hash.hint8","access-control.hash.hint9"})
|
||||
"access-control.hash.hint8","access-control.hash.hint9","access-control.hash.hint10","access-control.hash.hint11","access-control.hash.hint12"})
|
||||
public class MissingFunctionACYourHash extends AssignmentEndpoint {
|
||||
|
||||
@Autowired
|
||||
|
@ -51,7 +51,7 @@ public class Users extends Endpoint{
|
||||
userMap.put("cc", results.getString(3));
|
||||
userMap.put("ccType", results.getString(4));
|
||||
userMap.put("cookie", results.getString(5));
|
||||
userMap.put("loginCOunt",Integer.toString(results.getInt(6)));
|
||||
userMap.put("loginCount",Integer.toString(results.getInt(6)));
|
||||
allUsersMap.put(id,userMap);
|
||||
}
|
||||
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.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.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.hint3=Have you tried tampering the GET request? Can you find supported or unsupported methods? Can you trigger 500 errors?
|
||||
access-control.hash.hint4=There are actually two ways to solve this one. The first involves just changing a request header.
|
||||
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.hint6=If you're still looking for hints ... try changing the Content-type header in the GET request.
|
||||
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.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.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.hint1=There is an easier way and a 'harder' way to achieve this, the easier way involves one simple change in a GET request.
|
||||
access-control.hash.hint2= If you haven't found the hidden menus from the earlier exercise, go do that first.
|
||||
access-control.hash.hint3=When you look at the users page, there is a hint that more info is viewable by a given role.
|
||||
access-control.hash.hint4=For the easy way, have you tried tampering the GET request? Different content-types?
|
||||
access-control.hash.hint5=For the 'easy' way, modify the GET request to /users to include 'Content-Type: application/json'
|
||||
access-control.hash.hint6=Now for the harder way ... it builds on the easier way
|
||||
access-control.hash.hint7=If the request to view users, were a 'service' or 'RESTful' endpoint, what would be different about it?
|
||||
access-control.hash.hint8=If you're still looking for hints ... try changing the Content-type header as in the GET request.
|
||||
access-control.hash.hint9=You also need to deliver a proper payload for the request (look at how registration works). This should be formatted in line with the content-type you just defined.
|
||||
access-control.hash.hint10=You will want to add WEBGOAT_ADMIN for the user's role. Yes, you'd have to guess/fuzz this in a real-world setting.
|
||||
access-control.hash.hint11=OK, here it is. First, create an admin user ... Change the method to POST, change the content-type to "application/json". And your payload should look something like: {"username":"newUser2","password":"newUser12","matchingPassword":"newUser12","role":"WEBGOAT_ADMIN"}
|
||||
access-control.hash.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>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@ -62,7 +62,7 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
|
||||
resetLinks.add(resetLink);
|
||||
String host = request.getHeader("host");
|
||||
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);
|
||||
fakeClickingLinkEmail(host, resetLink);
|
||||
} else {
|
||||
|
@ -21,11 +21,10 @@
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-2">
|
||||
<h4 style="border-bottom: 1px solid #c5c5c5;"><i class="glyphicon glyphicon-user"></i> Account
|
||||
Access</h4>
|
||||
<div style="padding: 20px;" id="form-olvidado">
|
||||
<div class="col-md-4">
|
||||
<div style="padding: 20px;" id="password-login-2">
|
||||
<h4 style="border-bottom: 1px solid #c5c5c5;"><i class="glyphicon glyphicon-user"></i> Account
|
||||
Access</h4>
|
||||
<fieldset>
|
||||
<div class="form-group input-group">
|
||||
<span class="input-group-addon">@</span>
|
||||
@ -42,7 +41,7 @@
|
||||
Access
|
||||
</button>
|
||||
<p class="help-block">
|
||||
<a class="pull-right text-muted" href="#" id="olvidado">
|
||||
<a class="pull-right text-muted" href="#" id="olvidado" onclick="showPasswordReset()">
|
||||
<small>Forgot your password?</small>
|
||||
</a>
|
||||
</p>
|
||||
@ -50,7 +49,7 @@
|
||||
</fieldset>
|
||||
|
||||
</div>
|
||||
<div style="display: none;" id="form-olvidado">
|
||||
<div style="display: none;" id="password-reset-2">
|
||||
<h4 class="">Forgot your password?</h4>
|
||||
|
||||
<fieldset>
|
||||
@ -63,7 +62,7 @@
|
||||
<button type="submit" class="btn btn-primary btn-block" id="btn-olvidado">Continue
|
||||
</button>
|
||||
<p class="help-block">
|
||||
<a class="text-muted" href="#" id="acceso">
|
||||
<a class="text-muted" href="#" id="acceso" onclick="showPassword()">
|
||||
<small>Account Access</small>
|
||||
</a>
|
||||
</p>
|
||||
@ -99,7 +98,7 @@
|
||||
action="/WebGoat/PasswordReset/questions"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
<div class="container-fluid">
|
||||
<div class="col-md-2">
|
||||
<div class="col-md-4">
|
||||
<article class="card-body">
|
||||
<a href="" class="float-right btn btn-outline-primary">Sign up</a>
|
||||
<a href="" class="float-right btn btn-outline-primary">Login</a>
|
||||
@ -143,7 +142,7 @@
|
||||
enctype="application/json;charset=UTF-8">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="col-md-4">
|
||||
<h4 style="border-bottom: 1px solid #c5c5c5;">
|
||||
<i class="glyphicon glyphicon-user"></i>
|
||||
Account Access
|
||||
|
@ -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-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-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
|
||||
login_failed=Login failed
|
||||
login_failed.tom=Sorry only Tom can login at the moment
|
@ -1,22 +1,13 @@
|
||||
$(document).ready(function() {
|
||||
$('#olvidado').click(function(e) {
|
||||
e.preventDefault();
|
||||
$('div#form-olvidado').toggle('500');
|
||||
});
|
||||
$('#acceso').click(function(e) {
|
||||
e.preventDefault();
|
||||
$('div#form-olvidado').toggle('500');
|
||||
});
|
||||
});
|
||||
|
||||
function showPasswordReset() {
|
||||
console.log("clicking")
|
||||
$('#password-reset').show();
|
||||
$('#password-login').hide();
|
||||
$('#password-reset-2').show();
|
||||
$('#password-login-2').hide();
|
||||
}
|
||||
|
||||
function showPassword() {
|
||||
console.log("clicking")
|
||||
$('#password-login').show();
|
||||
$('#password-reset').hide();
|
||||
$('#password-login-2').show();
|
||||
$('#password-reset-2').hide();
|
||||
}
|
@ -5,12 +5,12 @@
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat</groupId>
|
||||
<artifactId>webgoat-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@ -6,6 +6,6 @@
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
</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.AssignmentHints;
|
||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.owasp.webgoat.plugin.introduction.SqlInjectionLesson5a;
|
||||
import org.owasp.webgoat.session.DatabaseUtilities;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
@ -55,7 +56,6 @@ public class SqlInjectionLesson6a extends AssignmentEndpoint {
|
||||
AttackResult completed(@RequestParam String userid_6a) throws IOException {
|
||||
return injectableQuery(userid_6a);
|
||||
// The answer: Smith' union select userid,user_name, password,cookie,cookie, cookie,userid from user_system_data --
|
||||
|
||||
}
|
||||
|
||||
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.AssignmentPath;
|
@ -2,9 +2,9 @@
|
||||
|
||||
The query in the code builds a dynamic query as seen in the previous example. The query in the code builds a dynamic query by concatenating strings making it susceptible to String SQL injection:
|
||||
|
||||
-------------------------------------------------------
|
||||
"select * from users where name = ‘" + userName + "'";
|
||||
-------------------------------------------------------
|
||||
------------------------------------------------------------
|
||||
"select * from users where LAST_NAME = ‘" + userName + "'";
|
||||
------------------------------------------------------------
|
||||
|
||||
Using the form below try to retrieve all the users from the users table. You shouldn't need to know any specific user name to get the complete list, however you can use 'Smith' to see the data for one user.
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
The query in the code builds a dynamic query as seen in the previous example. The query in the code builds a dynamic query by concatenating a number making it susceptible to Numeric SQL injection:
|
||||
|
||||
-------------------------------------------------------
|
||||
"select * from users where employee_id = " + userID;
|
||||
-------------------------------------------------------
|
||||
--------------------------------------------------
|
||||
"select * from users where USERID = " + userID;
|
||||
--------------------------------------------------
|
||||
|
||||
Using the form below try to retrieve all the users from the users table. You shouldn't need to know any specific user name to get the complete list, however you can use '101' to see the data for one user.
|
||||
|
@ -3,7 +3,7 @@
|
||||
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),
|
||||
password varchar(10),
|
||||
cookie varchar(30));
|
||||
|
@ -64,7 +64,7 @@ public class SqlInjectionLesson6aTest extends LessonTest {
|
||||
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.lessonCompleted", is(true)))
|
||||
.andExpect(jsonPath("$.feedback", containsString("dave")));
|
||||
.andExpect(jsonPath("$.feedback", containsString("passW0rD")));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -30,7 +30,7 @@ public class SqlInjectionLesson6bTest extends LessonTest {
|
||||
@Test
|
||||
public void submitCorrectPassword() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6b")
|
||||
.param("userid_6b", "dave"))
|
||||
.param("userid_6b", "passW0rD"))
|
||||
|
||||
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true)));
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0.M15</version>
|
||||
<version>v8.0.0.M20</version>
|
||||
</parent>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|