Add path traversal lesson
@ -64,7 +64,7 @@ public abstract class AssignmentEndpoint {
|
||||
* @param assignment
|
||||
*/
|
||||
protected AttackResult.AttackResultBuilder success(AssignmentEndpoint assignment) {
|
||||
return AttackResult.builder(messages).lessonCompleted(true).feedback("assignment.solved").assignment(assignment);
|
||||
return AttackResult.builder(messages).lessonCompleted(true).attemptWasMade().feedback("assignment.solved").assignment(assignment);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,7 +79,7 @@ public abstract class AssignmentEndpoint {
|
||||
* @param assignment
|
||||
*/
|
||||
protected AttackResult.AttackResultBuilder failed(AssignmentEndpoint assignment) {
|
||||
return AttackResult.builder(messages).lessonCompleted(false).feedback("assignment.not.solved").assignment(assignment);
|
||||
return AttackResult.builder(messages).lessonCompleted(false).attemptWasMade().feedback("assignment.not.solved").assignment(assignment);
|
||||
}
|
||||
|
||||
protected AttackResult.AttackResultBuilder informationMessage(AssignmentEndpoint assignment) {
|
||||
|
@ -29,8 +29,6 @@ import lombok.Getter;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.owasp.webgoat.i18n.PluginMessages;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class AttackResult {
|
||||
|
||||
|
||||
@ -43,6 +41,7 @@ public class AttackResult {
|
||||
private String output;
|
||||
private Object[] outputArgs;
|
||||
private AssignmentEndpoint assignment;
|
||||
private boolean attemptWasMade = false;
|
||||
|
||||
public AttackResultBuilder(PluginMessages messages) {
|
||||
this.messages = messages;
|
||||
@ -80,8 +79,13 @@ public class AttackResult {
|
||||
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());
|
||||
return new AttackResult(lessonCompleted, messages.getMessage(feedbackResourceBundleKey, feedbackArgs), messages.getMessage(output, output, outputArgs), assignment.getClass().getSimpleName(), attemptWasMade);
|
||||
}
|
||||
|
||||
public AttackResultBuilder assignment(AssignmentEndpoint assignment) {
|
||||
@ -98,12 +102,15 @@ public class AttackResult {
|
||||
private String output;
|
||||
@Getter
|
||||
private final String assignment;
|
||||
@Getter
|
||||
private boolean attemptWasMade;
|
||||
|
||||
public AttackResult(boolean lessonCompleted, String feedback, String output, String assignment) {
|
||||
public AttackResult(boolean lessonCompleted, String feedback, String output, String assignment, boolean attemptWasMade) {
|
||||
this.lessonCompleted = lessonCompleted;
|
||||
this.feedback = StringEscapeUtils.escapeJson(feedback);
|
||||
this.output = StringEscapeUtils.escapeJson(output);
|
||||
this.assignment = assignment;
|
||||
this.attemptWasMade = attemptWasMade;
|
||||
}
|
||||
|
||||
public static AttackResultBuilder builder(PluginMessages messages) {
|
||||
|
@ -0,0 +1,100 @@
|
||||
package org.owasp.webgoat;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.springframework.security.core.token.Sha512DigestUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Map;
|
||||
|
||||
public class PathTraversalTest extends IntegrationTest {
|
||||
|
||||
private static String OS = System.getProperty("os.name").toLowerCase();
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
private File folder;
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
this.folder = temporaryFolder.newFolder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assignment1() throws IOException {
|
||||
startLesson("PathTraversal");
|
||||
var fileToUpload = temporaryFolder.newFile("test.jpg");
|
||||
Files.write(fileToUpload.toPath(), "This is a test" .getBytes());
|
||||
|
||||
Assert.assertThat(
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.multiPart("uploadedFile", "test.jpg", Files.readAllBytes(fileToUpload.toPath()))
|
||||
.param("fullName", "../John Doe")
|
||||
.post("/WebGoat/PathTraversal/profile-upload")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assignment2() throws IOException {
|
||||
startLesson("PathTraversal");
|
||||
var fileToUpload = temporaryFolder.newFile("test.jpg");
|
||||
Files.write(fileToUpload.toPath(), "This is a test" .getBytes());
|
||||
|
||||
Assert.assertThat(
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.multiPart("uploadedFileFix", "test.jpg", Files.readAllBytes(fileToUpload.toPath()))
|
||||
.param("fullNameFix", "..././John Doe")
|
||||
.post("/WebGoat/PathTraversal/profile-upload-fix")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assignment3() throws IOException {
|
||||
startLesson("PathTraversal");
|
||||
var fileToUpload = temporaryFolder.newFile("test.jpg");
|
||||
Files.write(fileToUpload.toPath(), "This is a test" .getBytes());
|
||||
|
||||
Assert.assertThat(
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.multiPart("uploadedFileRetrieval", "../test.jpg", Files.readAllBytes(fileToUpload.toPath()))
|
||||
.post("/WebGoat/PathTraversal/profile-upload-remove-user-input")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assignment4() throws IOException {
|
||||
startLesson("PathTraversal");
|
||||
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get("/WebGoat/PathTraversal/random?id=../../path-traversal-secret")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.content(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);
|
||||
}
|
||||
}
|
@ -23,8 +23,7 @@
|
||||
<form class="attack-form" accept-charset="UNKNOWN" id="verify-account-form"
|
||||
method="POST" name="form"
|
||||
successCallback="onBypassResponse"
|
||||
action="/WebGoat/auth-bypass/verify-account"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/auth-bypass/verify-account">
|
||||
<p>Verify Your Account by answering the questions below:</p>
|
||||
|
||||
<p>What is the name of your favorite teacher?</p>
|
||||
@ -45,7 +44,6 @@
|
||||
method="POST" name="form"
|
||||
successCallback="onBypassResponse"
|
||||
action="/WebGoat/auth-bypass/verify-account"
|
||||
enctype="application/json;charset=UTF-8"
|
||||
style="display:none"><!-- start off hidden -->
|
||||
<p>Please provide a new password for your account</p>
|
||||
|
||||
|
@ -16,8 +16,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN" name="fieldRestrictions"
|
||||
method="POST"
|
||||
action="/WebGoat/BypassRestrictions/FieldRestrictions"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/BypassRestrictions/FieldRestrictions">
|
||||
|
||||
<div>Select field with two possible values</div>
|
||||
<select name="select">
|
||||
@ -49,7 +48,6 @@
|
||||
id="frontendValidation"
|
||||
method="POST"
|
||||
action="/WebGoat/BypassRestrictions/frontendValidation/"
|
||||
enctype="application/json;charset=UTF-8"
|
||||
onsubmit="return validate()">
|
||||
<div>
|
||||
<strong>Field 1:</strong> exactly three lowercase characters(^[a-z]{3}$)
|
||||
|
@ -18,8 +18,7 @@
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/challenge/1"
|
||||
style="width: 200px;"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
style="width: 200px;">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1" th:text="#{username}">Username</label>
|
||||
|
@ -25,8 +25,7 @@
|
||||
<div class="col-lg-12">
|
||||
<form id="login-form" class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/challenge/5"
|
||||
enctype="application/json;charset=UTF-8" role="form">
|
||||
action="/WebGoat/challenge/5" role="form">
|
||||
<div class="form-group">
|
||||
<input type="text" name="username_login" id="username4" tabindex="1"
|
||||
class="form-control" placeholder="Username" value=""/>
|
||||
|
@ -29,8 +29,7 @@
|
||||
<div class="col-lg-12">
|
||||
<form id="login-form" class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/challenge/6"
|
||||
enctype="application/json;charset=UTF-8" role="form">
|
||||
action="/WebGoat/challenge/6" role="form">
|
||||
<div class="form-group">
|
||||
<input type="text" name="username_login" id="username4" tabindex="1"
|
||||
class="form-control" placeholder="Username" value=""/>
|
||||
@ -65,8 +64,7 @@
|
||||
</form>
|
||||
<form id="register-form" class="attack-form" accept-charset="UNKNOWN"
|
||||
method="PUT" name="form"
|
||||
action="/WebGoat/challenge/6"
|
||||
enctype="application/json;charset=UTF-8" style="display: none;" role="form">
|
||||
action="/WebGoat/challenge/6" style="display: none;" role="form">
|
||||
<div class="form-group">
|
||||
<input type="text" name="username_reg" id="username" tabindex="1"
|
||||
class="form-control" placeholder="Username" value=""/>
|
||||
|
@ -28,8 +28,7 @@ f94008f801fceb8833a30fe56a8b26976347edcf First version of WebGoat Cloud website
|
||||
|
||||
<form id="login-form" class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/challenge/7"
|
||||
enctype="application/json;charset=UTF-8" role="form">
|
||||
action="/WebGoat/challenge/7" role="form">
|
||||
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
|
@ -24,8 +24,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="DOMFollowUp"
|
||||
action="/WebGoat/ChromeDevTools/dummy"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/ChromeDevTools/dummy">
|
||||
<input name="successMessage" value="" type="TEXT" />
|
||||
<input name="submitMessage" value="Submit" type="SUBMIT"/>
|
||||
</form>
|
||||
@ -46,8 +45,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/ChromeDevTools/network"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/ChromeDevTools/network">
|
||||
<script>
|
||||
// sample custom javascript in the recommended way ...
|
||||
// a namespace has been assigned for it, but you can roll your own if you prefer
|
||||
@ -68,8 +66,7 @@
|
||||
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/ChromeDevTools/network"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/ChromeDevTools/network">
|
||||
<table>
|
||||
<tr>
|
||||
<td>What is the number you found: </td>
|
||||
|
@ -29,8 +29,7 @@
|
||||
<div class="container-fluid">
|
||||
<form id="quiz-form" class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="cia/quiz"
|
||||
enctype="application/json;charset=UTF-8" role="form">
|
||||
action="cia/quiz" role="form">
|
||||
<div id="q_container"></div>
|
||||
<br />
|
||||
<input name="Quiz_solutions" value="Submit answers" type="SUBMIT"/>
|
||||
|
@ -83,8 +83,7 @@
|
||||
<div class="container-fluid">
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/clientSideFiltering/getItForFree"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/clientSideFiltering/getItForFree">
|
||||
|
||||
<input id="discount" type="hidden" value="0"/>
|
||||
<div class="row">
|
||||
|
@ -37,8 +37,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN" name="intercept-request"
|
||||
method="POST"
|
||||
action="/WebGoat/HttpBasics/intercept-request"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/HttpBasics/intercept-request">
|
||||
|
||||
<input type="text" value="doesn't matter really" name="changeMe" />
|
||||
<input type="submit" value="Submit" />
|
||||
|
@ -12,8 +12,7 @@
|
||||
<div id="lessonContent">
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/CrossSiteScripting/attack1"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/CrossSiteScripting/attack1">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Were the cookies the same on each tab?</td>
|
||||
@ -48,8 +47,7 @@
|
||||
<div id="lessonContent">
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="GET" name="xss-5a"
|
||||
action="/WebGoat/CrossSiteScripting/attack5a"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/CrossSiteScripting/attack5a">
|
||||
<hr width="90%" />
|
||||
<center>
|
||||
<h1>Shopping Cart</h1>
|
||||
@ -145,8 +143,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="DOMTestRoute"
|
||||
action="/WebGoat/CrossSiteScripting/attack6a"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/CrossSiteScripting/attack6a">
|
||||
<input name="DOMTestRoute" value="" type="TEXT" />
|
||||
<input name="SubmitTestRoute" value="Submit" type="SUBMIT"/>
|
||||
</form>
|
||||
@ -161,8 +158,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="DOMFollowUp"
|
||||
action="/WebGoat/CrossSiteScripting/dom-follow-up"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/CrossSiteScripting/dom-follow-up">
|
||||
<input name="successMessage" value="" type="TEXT" />
|
||||
<input name="submitMessage" value="Submit" type="SUBMIT"/>
|
||||
</form>
|
||||
@ -182,8 +178,7 @@
|
||||
<div class="container-fluid">
|
||||
<form id="quiz-form" class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/CrossSiteScripting/quiz"
|
||||
enctype="application/json;charset=UTF-8" role="form">
|
||||
action="/WebGoat/CrossSiteScripting/quiz" role="form">
|
||||
<div id="q_container"></div>
|
||||
<br />
|
||||
<input name="Quiz_solutions" value="Submit answers" type="SUBMIT"/>
|
||||
|
@ -21,7 +21,7 @@
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content8b.adoc"></div>
|
||||
<div class="attack-container" style="height: 100%; border: none !important;min-height: 450px;">
|
||||
<form id="codesubmit" style="height: 100%; min-height: 350px;" class="attack-form" accept-charset="UNKNOWN" method="POST" name="form" action="/WebGoat/CrossSiteScripting/attack3" enctype="application/json;charset=UTF-8">
|
||||
<form id="codesubmit" style="height: 100%; min-height: 350px;" class="attack-form" accept-charset="UNKNOWN" method="POST" name="form" action="/WebGoat/CrossSiteScripting/attack3">
|
||||
<div>
|
||||
<div id="editor" style="position: absolute; top: 0; right: 0; bottom: 0; left: 0; height: 350px;" name="editor"></div>
|
||||
<script th:src="@{/js/libs/ace/src-noconflict/ace.js}" type="text/javascript" charset="utf-8"></script>
|
||||
@ -41,7 +41,7 @@
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:CrossSiteScripting_content8c.adoc"></div>
|
||||
<div class="attack-container" style="height: 100%; border: none !important;min-height: 450px;">
|
||||
<form id="codesubmit2" style="height: 100%; min-height: 350px;" class="attack-form" accept-charset="UNKNOWN" method="POST" name="form" action="/WebGoat/CrossSiteScripting/attack4" enctype="application/json;charset=UTF-8">
|
||||
<form id="codesubmit2" style="height: 100%; min-height: 350px;" class="attack-form" accept-charset="UNKNOWN" method="POST" name="form" action="/WebGoat/CrossSiteScripting/attack4">
|
||||
<div>
|
||||
<div id="editor2" style="position: absolute; top: 0; right: 0; bottom: 0; left: 0; height: 350px;" name="editor2"></div>
|
||||
<script th:src="@{/js/libs/ace/src-noconflict/ace.js}" type="text/javascript" charset="utf-8"></script>
|
||||
|
@ -67,8 +67,7 @@
|
||||
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="DOMFollowUp"
|
||||
action="/WebGoat/CrossSiteScripting/stored-xss-follow-up"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/CrossSiteScripting/stored-xss-follow-up">
|
||||
<input name="successMessage" value="" type="TEXT" />
|
||||
<input name="submitMessage" value="Submit" type="SUBMIT"/>
|
||||
</form>
|
||||
|
@ -17,8 +17,7 @@
|
||||
method="POST" name="form1"
|
||||
target="_blank"
|
||||
successCallback=""
|
||||
action="/WebGoat/csrf/basic-get-flag"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/csrf/basic-get-flag">
|
||||
<input name="csrf" type="hidden" value="false"/>
|
||||
<input type="submit" name="submit"/>
|
||||
|
||||
@ -36,8 +35,7 @@
|
||||
<form class="attack-form" accept-charset="UNKNOWN" id="confirm-flag-1"
|
||||
method="POST" name="form2"
|
||||
successCallback=""
|
||||
action="/WebGoat/csrf/confirm-flag-1"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/csrf/confirm-flag-1">
|
||||
|
||||
Confirm Flag Value:
|
||||
<input type="text" length="6" name="confirmFlagVal" value=""/>
|
||||
@ -214,8 +212,7 @@
|
||||
</div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN" id="confirm-flag-feedback"
|
||||
method="POST" name="form2"
|
||||
action="/WebGoat/csrf/feedback"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/csrf/feedback">
|
||||
|
||||
Confirm Flag Value:
|
||||
<input type="text" length="6" name="confirmFlagVal" value=""/>
|
||||
@ -239,8 +236,7 @@
|
||||
</div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN" id="confirm-flag-login"
|
||||
method="POST" name="form2"
|
||||
action="/WebGoat/csrf/login"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/csrf/login">
|
||||
|
||||
Press the button below when your are logged in as the other user<br/>
|
||||
|
||||
|
@ -13,8 +13,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN" id="task" name="task"
|
||||
method="POST"
|
||||
action="/WebGoat/HtmlTampering/task"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/HtmlTampering/task">
|
||||
<script>
|
||||
var regex = /^2999.99$/;
|
||||
var price = 2999.99;
|
||||
|
@ -21,11 +21,10 @@
|
||||
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/HttpBasics/attack1"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/HttpBasics/attack1">
|
||||
<div id="lessonContent">
|
||||
<form accept-charset="UNKNOWN" method="POST" name="form"
|
||||
action="#attack/307/100" enctype="">
|
||||
action="#attack/307/100">
|
||||
Enter Your Name: <input name="person" value="" type="TEXT"/><input
|
||||
name="SUBMIT" value="Go!" type="SUBMIT"/>
|
||||
</form>
|
||||
@ -52,8 +51,7 @@
|
||||
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/HttpBasics/attack2"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/HttpBasics/attack2">
|
||||
<script>
|
||||
// sample custom javascript in the recommended way ...
|
||||
// a namespace has been assigned for it, but you can roll your own if you prefer
|
||||
|
@ -32,8 +32,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN" name="intercept-request"
|
||||
method="POST"
|
||||
action="/WebGoat/HttpProxies/intercept-request"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/HttpProxies/intercept-request">
|
||||
|
||||
<input type="text" value="doesn't matter really" name="changeMe" />
|
||||
<input type="submit" value="Submit" />
|
||||
|
@ -22,8 +22,7 @@
|
||||
<!-- modify the action to point to the intended endpoint -->
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/IDOR/login"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/IDOR/login">
|
||||
<table>
|
||||
<tr>
|
||||
<td>user/pass</td>
|
||||
@ -58,8 +57,7 @@
|
||||
<!-- modify the action to point to the intended endpoint -->
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="GET" name="form"
|
||||
action="/WebGoat/IDOR/profile"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/IDOR/profile">
|
||||
<script th:src="@{/lesson_js/idor.js}" />
|
||||
|
||||
<input name="View Profile" value="View Profile" type="button" onclick="onViewProfile();" />
|
||||
@ -82,8 +80,7 @@
|
||||
<!-- modify the action to point to the intended endpoint -->
|
||||
<form class="attack-form"
|
||||
method="POST" name="diff-form"
|
||||
action="IDOR/diff-attributes"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="IDOR/diff-attributes">
|
||||
<input name="attributes" type="text" />
|
||||
<input name="Submit Diffs" value="Submit Diffs" type="submit" />
|
||||
</form>
|
||||
@ -110,8 +107,7 @@
|
||||
<!-- modify the action to point to the intended endpoint -->
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/IDOR/profile/alt-path"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/IDOR/profile/alt-path">
|
||||
<div class="adoc-content" th:replace="doc:IDOR_inputAltPath.adoc"></div>
|
||||
<input name="url" value="WebGoat/" type="text"/>
|
||||
<input name="submit" value="Submit" type="SUBMIT"/>
|
||||
@ -138,8 +134,7 @@
|
||||
<!-- modify the action to point to the intended endpoint -->
|
||||
<form class="attack-form" accept-charset="UNKNOWN" id="view-other"
|
||||
method="GET" name="view-other-profile"
|
||||
action="/WebGoat/IDOR/profile/{userId}"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/IDOR/profile/{userId}">
|
||||
<script th:src="@{/lesson_js/idor.js}" />
|
||||
|
||||
<input name="View Profile" value="View Profile" type="submit" />
|
||||
@ -163,8 +158,7 @@
|
||||
<!-- modify the action to point to the intended endpoint -->
|
||||
<form class="attack-form" accept-charset="UNKNOWN" id="edit-other"
|
||||
method="GET" name="edit-other-profile"
|
||||
action="/WebGoat/IDOR/profile/{userId}"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/IDOR/profile/{userId}">
|
||||
<script th:src="@{/lesson_js/idor.js}" />
|
||||
|
||||
<input name="View Profile" value="View Profile" type="submit" />
|
||||
|
@ -27,8 +27,7 @@
|
||||
language="JavaScript"></script>
|
||||
<form class="attack-form" accept-charset="UNKNOWN" name="task"
|
||||
method="POST"
|
||||
action="/WebGoat/InsecureDeserialization/task"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/InsecureDeserialization/task">
|
||||
|
||||
<input type="textarea" rows="4" cols="40" value="" name="token" placeholder="token"/>
|
||||
<input type="submit" value="Submit" />
|
||||
|
@ -18,8 +18,7 @@
|
||||
language="JavaScript"></script>
|
||||
<form class="attack-form" accept-charset="UNKNOWN" name="task"
|
||||
method="POST"
|
||||
action="#attack/307/100"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="#attack/307/100">
|
||||
<!---
|
||||
<input type="hidden" value="" name="username" id="SecretUsername"/>
|
||||
<input type="hidden" value="" name="password" id="SecretPassword"/>
|
||||
@ -30,8 +29,7 @@
|
||||
<br></br>
|
||||
<form class="attack-form" accept-charset="UNKNOWN" name="task"
|
||||
method="POST"
|
||||
action="/WebGoat/InsecureLogin/task"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/InsecureLogin/task">
|
||||
|
||||
<input type="text" value="" name="username" placeholder="username"/>
|
||||
<input type="password" value="" name="password" placeholder="password" />
|
||||
|
@ -72,8 +72,7 @@ $(document).ready(
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST"
|
||||
successCallback="jwtSigningCallback"
|
||||
action="/WebGoat/JWT/votings"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/JWT/votings">
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="row">
|
||||
@ -167,8 +166,7 @@ $(document).ready(
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST"
|
||||
additionalHeaders="addBearerToken"
|
||||
action="/WebGoat/JWT/refresh/checkout"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/JWT/refresh/checkout">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-10 col-md-offset-1">
|
||||
@ -284,8 +282,7 @@ $(document).ready(
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST"
|
||||
action="/WebGoat/JWT/final/delete?token=eyJ0eXAiOiJKV1QiLCJraWQiOiJ3ZWJnb2F0X2tleSIsImFsZyI6IkhTMjU2In0.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQsImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJqZXJyeUB3ZWJnb2F0LmNvbSIsInVzZXJuYW1lIjoiSmVycnkiLCJFbWFpbCI6ImplcnJ5QHdlYmdvYXQuY29tIiwiUm9sZSI6WyJDYXQiXX0.CgZ27DzgVW8gzc0n6izOU638uUCi6UhiOJKYzoEZGE8"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/JWT/final/delete?token=eyJ0eXAiOiJKV1QiLCJraWQiOiJ3ZWJnb2F0X2tleSIsImFsZyI6IkhTMjU2In0.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQsImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJqZXJyeUB3ZWJnb2F0LmNvbSIsInVzZXJuYW1lIjoiSmVycnkiLCJFbWFpbCI6ImplcnJ5QHdlYmdvYXQuY29tIiwiUm9sZSI6WyJDYXQiXX0.CgZ27DzgVW8gzc0n6izOU638uUCi6UhiOJKYzoEZGE8">
|
||||
<div class="container-fluid">
|
||||
<div id="toast"></div>
|
||||
<div class="col-sm-6 col-md-4 col-lg-3 mt-4">
|
||||
|
@ -21,8 +21,7 @@
|
||||
<div class="col-md-4">
|
||||
<form class="attack-form" accept-charset="UNKNOWN" novalidate="novalidate"
|
||||
method="POST"
|
||||
action="/WebGoat/PasswordReset/simple-mail"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/PasswordReset/simple-mail">
|
||||
<div style="padding: 20px;" id="password-login-2">
|
||||
<h4 style="border-bottom: 1px solid #c5c5c5;"><i class="glyphicon glyphicon-user"></i>
|
||||
Account
|
||||
@ -56,8 +55,7 @@
|
||||
|
||||
<form class="attack-form" accept-charset="UNKNOWN" novalidate="novalidate"
|
||||
method="POST"
|
||||
action="/WebGoat/PasswordReset/simple-mail/reset"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/PasswordReset/simple-mail/reset">
|
||||
<div style="display: none;" id="password-reset-2">
|
||||
<h4 class="">Forgot your password?</h4>
|
||||
|
||||
@ -104,8 +102,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST"
|
||||
action="/WebGoat/PasswordReset/questions"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/PasswordReset/questions">
|
||||
<div class="container-fluid">
|
||||
<div class="col-md-4">
|
||||
<article class="card-body">
|
||||
@ -145,8 +142,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/PasswordReset/SecurityQuestions"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/PasswordReset/SecurityQuestions">
|
||||
<select name="question">
|
||||
<option>What is your favorite animal?</option>
|
||||
<option>In what year was your mother born?</option>
|
||||
@ -178,8 +174,7 @@
|
||||
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST"
|
||||
action="/WebGoat/PasswordReset/reset/login"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/PasswordReset/reset/login">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
@ -191,7 +186,7 @@
|
||||
<form id="login-form" class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/PasswordReset/reset/login"
|
||||
enctype="application/json;charset=UTF-8" role="form">
|
||||
role="form">
|
||||
<fieldset>
|
||||
<div class="form-group input-group">
|
||||
<span class="input-group-addon"> @ </span>
|
||||
@ -227,7 +222,7 @@
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/PasswordReset/ForgotPassword/create-password-reset-link"
|
||||
enctype="application/json;charset=UTF-8" role="form">
|
||||
role="form">
|
||||
<fieldset>
|
||||
<span class="help-block">
|
||||
Email address you use to log in to your account
|
||||
|
11
webgoat-lessons/path-traversal/pom.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>path-traversal</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<parent>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lessons-parent</artifactId>
|
||||
<version>v8.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
</project>
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.path_traversal;
|
||||
|
||||
import org.owasp.webgoat.lessons.Category;
|
||||
import org.owasp.webgoat.lessons.Lesson;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class PathTraversal extends Lesson {
|
||||
|
||||
@Override
|
||||
public Category getDefaultCategory() {
|
||||
return Category.INJECTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "path-traversal-title";
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package org.owasp.webgoat.path_traversal;
|
||||
|
||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.owasp.webgoat.session.WebSession;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
import static org.springframework.http.MediaType.ALL_VALUE;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package org.owasp.webgoat.path_traversal;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.owasp.webgoat.session.WebSession;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
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.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
@AllArgsConstructor
|
||||
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();
|
||||
}
|
||||
|
||||
var uploadDirectory = new File(this.webGoatHomeDirectory, "/PathTraversal/" + webSession.getUserName());
|
||||
if (uploadDirectory.exists()) {
|
||||
FileSystemUtils.deleteRecursively(uploadDirectory);
|
||||
}
|
||||
|
||||
try {
|
||||
uploadDirectory.mkdirs();
|
||||
var uploadedFile = new File(uploadDirectory, fullName);
|
||||
uploadedFile.createNewFile();
|
||||
FileCopyUtils.copy(file.getBytes(), uploadedFile);
|
||||
if (userAttemptedToSolveLesson(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();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean userAttemptedToSolveLesson(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).build();
|
||||
}
|
||||
|
||||
public ResponseEntity<?> getProfilePicture() {
|
||||
var profilePictureDirectory = new File(this.webGoatHomeDirectory, "/PathTraversal/" + webSession.getUserName());
|
||||
var profileDirectoryFiles = profilePictureDirectory.listFiles();
|
||||
|
||||
if (profileDirectoryFiles != null && profileDirectoryFiles.length > 0) {
|
||||
try (var inputStream = new FileInputStream(profileDirectoryFiles[0])) {
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE))
|
||||
.body(Base64.getEncoder().encode(FileCopyUtils.copyToByteArray(inputStream)));
|
||||
} catch (IOException e) {
|
||||
return defaultImage();
|
||||
}
|
||||
} else {
|
||||
return defaultImage();
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private ResponseEntity<?> defaultImage() {
|
||||
var inputStream = getClass().getResourceAsStream("/images/account.png");
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE))
|
||||
.body(Base64.getEncoder().encode(FileCopyUtils.copyToByteArray(inputStream)));
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package org.owasp.webgoat.path_traversal;
|
||||
|
||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.owasp.webgoat.session.WebSession;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import static org.springframework.http.MediaType.ALL_VALUE;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
|
||||
@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.replaceAll("../", "") : "");
|
||||
}
|
||||
|
||||
@GetMapping("/PathTraversal/profile-picture-fix")
|
||||
@ResponseBody
|
||||
public ResponseEntity<?> getProfilePicture() {
|
||||
return super.getProfilePicture();
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package org.owasp.webgoat.path_traversal;
|
||||
|
||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.owasp.webgoat.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;
|
||||
|
||||
import static org.springframework.http.MediaType.ALL_VALUE;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
|
||||
@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("uploadedFileRetrieval") MultipartFile file) {
|
||||
return super.execute(file, file.getOriginalFilename());
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package org.owasp.webgoat.path_traversal;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.token.Sha512DigestUtils;
|
||||
import org.springframework.security.crypto.codec.Hex;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Base64;
|
||||
|
||||
import static org.springframework.util.FileCopyUtils.copy;
|
||||
import static org.springframework.util.ResourceUtils.getFile;
|
||||
|
||||
@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"})
|
||||
@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 {
|
||||
copy(getFile(getClass().getResource("/images/cats/" + i + ".jpg")), new File(catPicturesDirectory, i + ".jpg"));
|
||||
} catch (Exception e) {
|
||||
log.error("Unable to copy pictures", e);
|
||||
}
|
||||
}
|
||||
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")
|
||||
protected 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")
|
||||
@ResponseBody
|
||||
public ResponseEntity<?> getProfilePicture(@RequestParam(value = "id", required = false) String id) {
|
||||
var catPicture = new File(catPicturesDirectory, (id == null ? RandomUtils.nextInt(1, 11) : id) + ".jpg");
|
||||
|
||||
try {
|
||||
if (catPicture.getName().toLowerCase().contains("path-traversal-secret.jpg")) {
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE))
|
||||
.body(FileCopyUtils.copyToByteArray(catPicture));
|
||||
}
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE))
|
||||
.location(new URI("/PathTraversal/random?id=" + catPicture.getName()))
|
||||
.body(Base64.getEncoder().encode(FileCopyUtils.copyToByteArray(catPicture)));
|
||||
} catch (IOException | URISyntaxException e) {
|
||||
log.error("Unable to download picture", e);
|
||||
}
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE))
|
||||
.body(StringUtils.arrayToCommaDelimitedString(catPicture.getParentFile().listFiles()).getBytes());
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
.upload-container
|
||||
{
|
||||
width: 500px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.preview
|
||||
{
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.preview i
|
||||
{
|
||||
color: white;
|
||||
font-size: 35px;
|
||||
transform: translate(50px,130px);
|
||||
}
|
||||
|
||||
.preview-img
|
||||
{
|
||||
border-radius: 100%;
|
||||
box-shadow: 0px 0px 5px 2px rgba(0,0,0,0.7);
|
||||
}
|
||||
|
||||
.browse-button
|
||||
{
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border-radius: 100%;
|
||||
position: absolute; /* Tweak the position property if the element seems to be unfit */
|
||||
top: 10px;
|
||||
left: 150px;
|
||||
background: linear-gradient(180deg, transparent, black);
|
||||
opacity: 0;
|
||||
transition: 0.3s ease;
|
||||
}
|
||||
|
||||
.browse-button:hover
|
||||
{
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.browse-input
|
||||
{
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border-radius: 100%;
|
||||
transform: translate(-1px,-26px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.Error
|
||||
{
|
||||
color: crimson;
|
||||
font-size: 13px;
|
||||
}
|
@ -0,0 +1,212 @@
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<script th:src="@{/lesson_js/path_traversal.js}" language="JavaScript"></script>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/path_traversal.css}"/>
|
||||
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:PathTraversal_intro.adoc"></div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:PathTraversal_upload.adoc"></div>
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<div class="upload-container">
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
onsubmit='return false'
|
||||
contentType="false"
|
||||
successCallback="profileUploadCallback"
|
||||
failureCallback="profileUploadCallback"
|
||||
informationalCallback="profileUploadCallback"
|
||||
prepareData="profileUpload"
|
||||
enctype="multipart/form-data"
|
||||
action="/WebGoat/PathTraversal/profile-upload">
|
||||
<div class="preview text-center">
|
||||
<img class="preview-img" th:src="@{/images/account.png}" alt="Preview Image" width="200"
|
||||
height="200" id="preview"/>
|
||||
<div class="browse-button">
|
||||
<i class="fa fa-pencil"></i>
|
||||
<input class="browse-input" type="file" required name="uploadedFile" id="uploadedFile"/>
|
||||
</div>
|
||||
<span class="Error"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Full Name:</label>
|
||||
<input class="form-control" type="text" id="fullName" name="fullName" required value="test"
|
||||
placeholder="Enter Your Full Name"/>
|
||||
<span class="Error"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Email:</label>
|
||||
<input class="form-control" type="email" id="email" name="email" required
|
||||
placeholder="Enter Your Email" value="test@test.com"/>
|
||||
<span class="Error"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Password:</label>
|
||||
<input class="form-control" type="password" id="password" name="password" required
|
||||
placeholder="Enter Password" value="test"/>
|
||||
<span class="Error"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary btn-block" value="Submit">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:PathTraversal_upload_fix.adoc"></div>
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<div class="upload-container">
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
onsubmit='return false'
|
||||
contentType="false"
|
||||
successCallback="profileUploadCallbackFix"
|
||||
failureCallback="profileUploadCallbackFix"
|
||||
informationalCallback="profileUploadCallbackFix"
|
||||
prepareData="profileUploadFix"
|
||||
enctype="multipart/form-data"
|
||||
action="/WebGoat/PathTraversal/profile-upload-fix">
|
||||
<div class="preview text-center">
|
||||
<img class="preview-img-fix" th:src="@{/images/account.png}" alt="Preview Image" width="200"
|
||||
height="200" id="previewFix"/>
|
||||
<div class="browse-button">
|
||||
<i class="fa fa-pencil"></i>
|
||||
<input class="browse-input" type="file" required name="uploadedFile" id="uploadedFileFix"/>
|
||||
</div>
|
||||
<span class="Error"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Full Name:</label>
|
||||
<input class="form-control" type="text" id="fullNameFix" name="fullName" required value="test"
|
||||
placeholder="Enter Your Full Name"/>
|
||||
<span class="Error"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Email:</label>
|
||||
<input class="form-control" type="email" id="emailFix" name="email" required
|
||||
placeholder="Enter Your Email" value="test@test.com"/>
|
||||
<span class="Error"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Password:</label>
|
||||
<input class="form-control" type="password" id="passwordFix" name="password" required
|
||||
placeholder="Enter Password" value="test"/>
|
||||
<span class="Error"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary btn-block" value="Submit">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:PathTraversal_upload_remove_user_input.adoc"></div>
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<div class="upload-container">
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
onsubmit='return false'
|
||||
contentType="false"
|
||||
successCallback="profileUploadCallbackRemoveUserInput"
|
||||
failureCallback="profileUploadCallbackRemoveUserInput"
|
||||
informationalCallback="profileUploadCallbackRemoveUserInput"
|
||||
prepareData="profileUploadFix"
|
||||
enctype="multipart/form-data"
|
||||
action="/WebGoat/PathTraversal/profile-upload-remove-user-input">
|
||||
<div class="preview text-center">
|
||||
<img class="preview-img-fix" th:src="@{/images/account.png}" alt="Preview Image" width="200"
|
||||
height="200" id="previewRemoveUserInput"/>
|
||||
<div class="browse-button">
|
||||
<i class="fa fa-pencil"></i>
|
||||
<input class="browse-input" type="file" required name="uploadedFile"
|
||||
id="uploadedFileRemoveUserInput"/>
|
||||
</div>
|
||||
<span class="Error"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Full Name:</label>
|
||||
<input class="form-control" type="text" id="fullNameRemoveUserInput" name="fullName" required
|
||||
value="test"
|
||||
placeholder="Enter Your Full Name"/>
|
||||
<span class="Error"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Email:</label>
|
||||
<input class="form-control" type="email" id="emailRemoveUserInput" name="email" required
|
||||
placeholder="Enter Your Email" value="test@test.com"/>
|
||||
<span class="Error"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Password:</label>
|
||||
<input class="form-control" type="password" id="passwordRemoveUserInput" name="password" required
|
||||
placeholder="Enter Password" value="test"/>
|
||||
<span class="Error"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary btn-block" value="Submit">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:PathTraversal_retrieval.adoc"></div>
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<div class="upload-container">
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary btn-block" onclick="newRandomPicture()">Show random cat picture</button>
|
||||
</div>
|
||||
<div>
|
||||
<img id="randomCatPicture" th:src="@{/images/cats/1.jpg}"/>
|
||||
</div>
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
<form class="attack-form" method="POST" name="form" action="/WebGoat/PathTraversal/random">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon"><i class="fa fa-flag-checkered" aria-hidden="true"
|
||||
style="font-size:20px"></i></div>
|
||||
<input type="text" class="form-control" id="pathTraversalSecret" name="secret"/>
|
||||
</div>
|
||||
<div class="input-group" style="margin-top: 10px">
|
||||
<button type="submit" class="btn btn-primary">Submit secret</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</html>
|
@ -0,0 +1,46 @@
|
||||
#
|
||||
# This file is part of WebGoat, an Open Web Application Security Project utility. For details,
|
||||
# please see http://www.owasp.org/
|
||||
# <p>
|
||||
# Copyright (c) 2002 - 2017 Bruce Mayhew
|
||||
# <p>
|
||||
# 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.
|
||||
# <p>
|
||||
# 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.
|
||||
# <p>
|
||||
# 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.
|
||||
# <p>
|
||||
# Getting Source ==============
|
||||
# <p>
|
||||
# Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
|
||||
# projects.
|
||||
# <p>
|
||||
#
|
||||
path-traversal-title=Path traversal
|
||||
path-traversal-profile-updated=Profile has been updated, your image is available at: {0}"
|
||||
path-traversal-profile-empty-file=File appears to be empty please upload a non empty file
|
||||
path-traversal-profile-empty-name=Name is empty
|
||||
path-traversal-profile.hint1=Try updating the profile WebGoat will display the location
|
||||
path-traversal-profile.hint2=Look at the displayed location how is the file name on the server constructed?
|
||||
path-traversal-profile.hint3=Does the server validate any input given in the full name field?
|
||||
|
||||
path-traversal-profile-fix.hint1=Take a look what happens compared to the previous assignment
|
||||
path-traversal-profile-fix.hint2=The new and improved version removes `../` from the input, can you bypass this?
|
||||
path-traversal-profile-fix.hint3=Try to construct a full name which after cleaning still has `../` in the full name
|
||||
|
||||
path-traversal-profile-remove-user-input.hint1=Take a look what happened to the file name
|
||||
path-traversal-profile-remove-user-input.hint2=Can we still manipulate the request?
|
||||
path-traversal-profile-remove-user-input.hint3=You can try to use a proxy to intercept the POST request
|
||||
|
||||
|
||||
path-traversal-profile-retrieve.hint1=Can you specify the image to be fetched?
|
||||
path-traversal-profile-retrieve.hint2=Look at the location header...
|
||||
path-traversal-profile-retrieve.hint3=Use /random?id=1 for example to fetch a specific image
|
||||
path-traversal-profile-retrieve.hint4=Use /random/?id=../../1.jpg to navigate to a different directory
|
||||
path-traversal-profile-retrieve.hint5=Do you see a difference when retrieving a file or a directory?
|
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 108 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 118 KiB |
After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 54 KiB |
@ -0,0 +1,62 @@
|
||||
webgoat.customjs.profileUpload = function () {
|
||||
|
||||
var picture = document.getElementById("uploadedFile").files[0];
|
||||
var formData = new FormData();
|
||||
formData.append("uploadedFile", picture);
|
||||
formData.append("fullName", $("#fullName").val());
|
||||
formData.append("email", $("#email").val());
|
||||
formData.append("password", $("#password").val());
|
||||
return formData;
|
||||
}
|
||||
|
||||
webgoat.customjs.profileUploadCallback = function () {
|
||||
$.get("PathTraversal/profile-picture", function (result, status) {
|
||||
document.getElementById("preview").src = "data:image/png;base64," + result;
|
||||
});
|
||||
}
|
||||
|
||||
webgoat.customjs.profileUploadFix = function () {
|
||||
var picture = document.getElementById("uploadedFileFix").files[0];
|
||||
var formData = new FormData();
|
||||
formData.append("uploadedFileFix", picture);
|
||||
formData.append("fullNameFix", $("#fullNameFix").val());
|
||||
formData.append("emailFix", $("#emailFix").val());
|
||||
formData.append("passwordFix", $("#passwordFix").val());
|
||||
return formData;
|
||||
}
|
||||
|
||||
webgoat.customjs.profileUploadCallbackFix = function () {
|
||||
$.get("PathTraversal/profile-picture", function (result, status) {
|
||||
document.getElementById("previewFix").src = "data:image/png;base64," + result;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
webgoat.customjs.profileUploadRemoveUserInput = function () {
|
||||
var picture = document.getElementById("uploadedFileRemoveUserInput").files[0];
|
||||
var formData = new FormData();
|
||||
formData.append("uploadedFile", picture);
|
||||
formData.append("fullName", $("#fullNameRemoveUserInput").val());
|
||||
formData.append("email", $("#emailRemoveUserInput").val());
|
||||
formData.append("password", $("#passwordRemoveUserInput").val());
|
||||
return formData;
|
||||
}
|
||||
|
||||
webgoat.customjs.profileUploadCallbackRemoveUserInput = function () {
|
||||
$.get("PathTraversal/profile-picture", function (result, status) {
|
||||
document.getElementById("previewRemoveUserInput").src = "data:image/png;base64," + result;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
webgoat.customjs.profileUploadCallbackRetrieval = function () {
|
||||
$.get("PathTraversal/profile-picture", function (result, status) {
|
||||
document.getElementById("previewRetrieval").src = "data:image/png;base64," + result;
|
||||
});
|
||||
}
|
||||
|
||||
function newRandomPicture() {
|
||||
$.get("PathTraversal/random", function (result, status) {
|
||||
document.getElementById("randomCatPicture").src = "data:image/png;base64," + result;
|
||||
});
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
=== Path traversal
|
||||
|
||||
A path(directory) traversal is a vulnerability where an attacker is able to access or store files and directories outside
|
||||
the location where the application is running. This may lead to reading files from other directories and in case of a file
|
||||
upload overwriting critical system files.
|
||||
|
||||
=== How does it work?
|
||||
|
||||
For example let's assume we have an application which hosts some files and they can be requested 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/../../../../../etc/passwd`. In this case you try walk 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 which is another name
|
||||
for this attack.
|
||||
|
||||
Of course this is a very simple example and in most cases this will not work as frameworks implemented controls for
|
||||
this, 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 web server 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 in the case where you have a system A which calls system B. System A will only decode once and
|
||||
will call B with the still encoded URL.
|
||||
|
||||
Now let's try some examples
|
||||
|
||||
1. Example with upload where you have to overwrite one of the files
|
||||
1a Example upload where developer implemented a fix by removing ../ from the path
|
||||
1b No longer using name of user to store file so you need proxy to intercept
|
||||
2. Example with download for example the idea is to download a pdf but there is also a txt file
|
||||
3. Same as 2 with blacklisting implemented (see example from Workpress plugin)
|
||||
4. Zip path traversal where you have to create a zip file which overwrites a file in another directory
|
||||
|
||||
|
||||
- Run app with least priviledge
|
||||
- Before storing the file calculate the location
|
||||
- Same for reading is it actually reading from the directory you allow
|
||||
|
@ -0,0 +1,6 @@
|
||||
=== Retrieving other files with a path traversal
|
||||
|
||||
Path traversals are not limited to file uploads also 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 `secret.txt`
|
||||
|
||||
|
@ -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 which is outside the normal upload location.
|
||||
|
||||
|===
|
||||
|OS |Location
|
||||
|
||||
|`operatingSystem:os[]`
|
||||
|`webGoatTempDir:temppath[]PathTraversal/`
|
||||
|
||||
|===
|
@ -0,0 +1,11 @@
|
||||
=== Path traversal while uploading files
|
||||
|
||||
Again the same assignment but can you bypass the implemented fix?
|
||||
|
||||
|===
|
||||
|OS |Location
|
||||
|
||||
|`operatingSystem:os[]`
|
||||
|`webGoatTempDir:temppath[]PathTraversal/`
|
||||
|
||||
|===
|
@ -0,0 +1,12 @@
|
||||
=== 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:
|
||||
|
||||
|===
|
||||
|OS |Location
|
||||
|
||||
|`operatingSystem:os[]`
|
||||
|`webGoatTempDir:temppath[]PathTraversal/secret.txt`
|
||||
|
||||
|===
|
@ -0,0 +1,42 @@
|
||||
=== Path traversal mitigation
|
||||
|
||||
As we saw in the previous assignments protecting a file upload can be a daunting task. The thing comes down to trusting
|
||||
input without validating it.
|
||||
In the examples shown before a solution might be to not trust user input and create a random file name on the
|
||||
server side.
|
||||
|
||||
If you really need to save it based on user input the best way to keep you save is to check the canonical path the
|
||||
file will be saved. 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 a absolute path, removing `.` and `..` etc. By checking whether the canonical
|
||||
path is inside the expected directory the path traversal will be avoided.
|
||||
|
||||
|
||||
For path traversals while retrieving one can apply the same technique described above but as a defence in depth you
|
||||
can also implement a mitigation by running the application under a specific not privileged user which is not allowed to read and write
|
||||
in any other directory.
|
||||
|
||||
Make sure that in any case you build detection for catching these cases but be careful with returning explicit information
|
||||
to the user. Every small detail might give the attacker knowledge about your system.
|
||||
|
||||
==== Spring Boot protection
|
||||
|
||||
By default Spring Boot has protection for usage of for example `../` in a path. The implementation can be found in
|
||||
`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.
|
@ -0,0 +1,14 @@
|
||||
=== Path traversal while uploading files
|
||||
|
||||
The developer 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.
|
||||
|
||||
Again the same assignment but can you bypass the implemented fix?
|
||||
|
||||
|===
|
||||
|OS |Location
|
||||
|
||||
|`operatingSystem:os[]`
|
||||
|`webGoatTempDir:temppath[]PathTraversal/`
|
||||
|
||||
|===
|
@ -0,0 +1,58 @@
|
||||
package org.owasp.webgoat.path_traversal;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.owasp.webgoat.plugins.LessonTest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class ProfileUploadFixTest extends LessonTest {
|
||||
|
||||
@Autowired
|
||||
private PathTraversal pathTraversal;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
Mockito.when(webSession.getCurrentLesson()).thenReturn(pathTraversal);
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||
Mockito.when(webSession.getUserName()).thenReturn("unit-test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void solve() throws Exception {
|
||||
var profilePicture = new MockMultipartFile("uploadedFileFix", "../picture.jpg", "text/plain", "an image".getBytes());
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.multipart("/PathTraversal/profile-upload-fix")
|
||||
.file(profilePicture)
|
||||
.param("fullNameFix", "..././John Doe"))
|
||||
.andExpect(status().is(200))
|
||||
.andExpect(jsonPath("$.assignment", CoreMatchers.equalTo("ProfileUploadFix")))
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void normalUpdate() throws Exception {
|
||||
var profilePicture = new MockMultipartFile("uploadedFileFix", "picture.jpg", "text/plain", "an image".getBytes());
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.multipart("/PathTraversal/profile-upload-fix")
|
||||
.file(profilePicture)
|
||||
.param("fullNameFix", "John Doe"))
|
||||
.andExpect(status().is(200))
|
||||
.andExpect(jsonPath("$.feedback", CoreMatchers.containsString("/unit-test\\/John Doe\\\"")))
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package org.owasp.webgoat.path_traversal;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.owasp.webgoat.plugins.LessonTest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class ProfileUploadRemoveUserInputTest extends LessonTest {
|
||||
|
||||
@Autowired
|
||||
private PathTraversal pathTraversal;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
Mockito.when(webSession.getCurrentLesson()).thenReturn(pathTraversal);
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||
Mockito.when(webSession.getUserName()).thenReturn("unit-test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void solve() throws Exception {
|
||||
var profilePicture = new MockMultipartFile("uploadedFileRetrieval", "../picture.jpg", "text/plain", "an image".getBytes());
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.multipart("/PathTraversal/profile-upload-remove-user-input")
|
||||
.file(profilePicture)
|
||||
.param("fullNameFix", "John Doe"))
|
||||
.andExpect(status().is(200))
|
||||
.andExpect(jsonPath("$.assignment", CoreMatchers.equalTo("ProfileUploadRemoveUserInput")))
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void normalUpdate() throws Exception {
|
||||
var profilePicture = new MockMultipartFile("uploadedFileRetrieval", "picture.jpg", "text/plain", "an image".getBytes());
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.multipart("/PathTraversal/profile-upload-remove-user-input")
|
||||
.file(profilePicture)
|
||||
.param("fullNameFix", "John Doe"))
|
||||
.andExpect(status().is(200))
|
||||
.andExpect(jsonPath("$.feedback", CoreMatchers.containsString("/unit-test\\/picture.jpg\\\"")))
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package org.owasp.webgoat.path_traversal;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.owasp.webgoat.plugins.LessonTest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.core.token.Sha512DigestUtils;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class ProfileUploadRetrievalTest extends LessonTest {
|
||||
|
||||
@Autowired
|
||||
private PathTraversal pathTraversal;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
Mockito.when(webSession.getCurrentLesson()).thenReturn(pathTraversal);
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||
Mockito.when(webSession.getUserName()).thenReturn("unit-test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void solve() throws Exception {
|
||||
//Look at the response
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/PathTraversal/random"))
|
||||
.andExpect(status().is(200))
|
||||
.andExpect(header().exists("Location"))
|
||||
.andExpect(header().string("Location", containsString("?id=")))
|
||||
.andExpect(content().contentTypeCompatibleWith(MediaType.IMAGE_JPEG));
|
||||
|
||||
//Browse the directories
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/PathTraversal/random?id=../../"))
|
||||
.andExpect(status().is(200))
|
||||
.andExpect(content().string(containsString("/path-traversal-secret.jpg")))
|
||||
.andExpect(content().contentTypeCompatibleWith(MediaType.IMAGE_JPEG));
|
||||
|
||||
//Retrieve the secret file (note: .jpg is added by the server)
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/PathTraversal/random?id=../../path-traversal-secret"))
|
||||
.andExpect(status().is(200))
|
||||
.andExpect(content().string("You found it submit the SHA-512 hash of your username as answer"))
|
||||
.andExpect(content().contentTypeCompatibleWith(MediaType.IMAGE_JPEG));
|
||||
|
||||
//Post flag
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/PathTraversal/random").param("secret", Sha512DigestUtils.shaHex("unit-test")))
|
||||
.andExpect(status().is(200))
|
||||
.andExpect(jsonPath("$.assignment", equalTo("ProfileUploadRetrieval")))
|
||||
.andExpect(jsonPath("$.lessonCompleted", is(true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReceiveRandomPicture() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/PathTraversal/random"))
|
||||
.andExpect(status().is(200))
|
||||
.andExpect(header().exists("Location"))
|
||||
.andExpect(content().contentTypeCompatibleWith(MediaType.IMAGE_JPEG));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unknownFileShouldGiveDirectoryContents() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/PathTraversal/random?id=test"))
|
||||
.andExpect(status().is(200))
|
||||
.andExpect(content().string(containsString("cats/8.jpg")))
|
||||
.andExpect(content().contentTypeCompatibleWith(MediaType.IMAGE_JPEG));
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package org.owasp.webgoat.path_traversal;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.owasp.webgoat.plugins.LessonTest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class ProfileUploadTest extends LessonTest {
|
||||
|
||||
@Autowired
|
||||
private PathTraversal pathTraversal;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
Mockito.when(webSession.getCurrentLesson()).thenReturn(pathTraversal);
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||
Mockito.when(webSession.getUserName()).thenReturn("unit-test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void solve() throws Exception {
|
||||
var profilePicture = new MockMultipartFile("uploadedFile", "../picture.jpg", "text/plain", "an image".getBytes());
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.multipart("/PathTraversal/profile-upload")
|
||||
.file(profilePicture)
|
||||
.param("fullName", "../John Doe"))
|
||||
.andExpect(status().is(200))
|
||||
.andExpect(jsonPath("$.assignment", CoreMatchers.equalTo("ProfileUpload")))
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void normalUpdate() throws Exception {
|
||||
var profilePicture = new MockMultipartFile("uploadedFile", "picture.jpg", "text/plain", "an image".getBytes());
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.multipart("/PathTraversal/profile-upload")
|
||||
.file(profilePicture)
|
||||
.param("fullName", "John Doe"))
|
||||
.andExpect(status().is(200))
|
||||
.andExpect(jsonPath("$.feedback", CoreMatchers.containsString("/PathTraversal\\/unit-test\\/John Doe\\\"")))
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||
}
|
||||
|
||||
}
|
@ -40,6 +40,7 @@
|
||||
<module>secure-passwords</module>
|
||||
<module>webgoat-lesson-template</module>
|
||||
<module>crypto</module>
|
||||
<module>path-traversal</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
|
@ -21,7 +21,6 @@
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SecurePasswords/assignment"
|
||||
enctype="application/json;charset=UTF-8"
|
||||
autocomplete="off">
|
||||
<table>
|
||||
<tr>
|
||||
|
@ -16,7 +16,6 @@
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SqlInjection/attack2"
|
||||
enctype="application/json;charset=UTF-8"
|
||||
autocomplete="off">
|
||||
<table>
|
||||
<tr>
|
||||
@ -41,7 +40,6 @@
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SqlInjection/attack3"
|
||||
enctype="application/json;charset=UTF-8"
|
||||
autocomplete="off">
|
||||
<table>
|
||||
<tr>
|
||||
@ -66,7 +64,6 @@
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SqlInjection/attack4"
|
||||
enctype="application/json;charset=UTF-8"
|
||||
autocomplete="off">
|
||||
<table>
|
||||
<tr>
|
||||
@ -91,7 +88,6 @@
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SqlInjection/attack5"
|
||||
enctype="application/json;charset=UTF-8"
|
||||
autocomplete="off">
|
||||
<table>
|
||||
<tr>
|
||||
@ -147,8 +143,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SqlInjection/assignment5a"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/SqlInjection/assignment5a">
|
||||
<table>
|
||||
<tr>
|
||||
<td>SELECT * FROM user_data WHERE first_name = 'John' AND last_name = '</td>
|
||||
@ -193,8 +188,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SqlInjection/assignment5b"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/SqlInjection/assignment5b">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Login_Count:</td>
|
||||
@ -223,7 +217,6 @@
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SqlInjection/attack8"
|
||||
enctype="application/json;charset=UTF-8"
|
||||
autocomplete="off">
|
||||
<table>
|
||||
<tr>
|
||||
@ -252,7 +245,6 @@
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SqlInjection/attack9"
|
||||
enctype="application/json;charset=UTF-8"
|
||||
autocomplete="off">
|
||||
<table>
|
||||
<tr>
|
||||
@ -282,7 +274,6 @@
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SqlInjection/attack10"
|
||||
enctype="application/json;charset=UTF-8"
|
||||
autocomplete="off">
|
||||
<table>
|
||||
<tr>
|
||||
|
@ -20,8 +20,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SqlInjectionAdvanced/attack6a"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/SqlInjectionAdvanced/attack6a">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Name:</td>
|
||||
@ -34,8 +33,7 @@
|
||||
</form>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SqlInjectionAdvanced/attack6b"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/SqlInjectionAdvanced/attack6b">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Password:</td>
|
||||
@ -82,7 +80,7 @@
|
||||
<form id="login-form" class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SqlInjectionAdvanced/challenge_Login"
|
||||
enctype="application/json;charset=UTF-8" role="form">
|
||||
role="form">
|
||||
<div class="form-group">
|
||||
<input type="text" name="username_login" id="username4" tabindex="1"
|
||||
class="form-control" placeholder="Username" value=""/>
|
||||
@ -118,7 +116,7 @@
|
||||
<form id="register-form" class="attack-form" accept-charset="UNKNOWN"
|
||||
method="PUT" name="form"
|
||||
action="/WebGoat/SqlInjectionAdvanced/challenge"
|
||||
enctype="application/json;charset=UTF-8" style="display: none;" role="form">
|
||||
style="display: none;" role="form">
|
||||
<div class="form-group">
|
||||
<input type="text" name="username_reg" id="username" tabindex="1"
|
||||
class="form-control" placeholder="Username" value=""/>
|
||||
@ -171,7 +169,7 @@
|
||||
<form id="quiz-form" class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SqlInjectionAdvanced/quiz"
|
||||
enctype="application/json;charset=UTF-8" role="form">
|
||||
role="form">
|
||||
<div id="q_container"></div>
|
||||
<br />
|
||||
<input name="Quiz_solutions" value="Submit answers" type="SUBMIT"/>
|
||||
|
@ -23,7 +23,7 @@
|
||||
<div class="adoc-content" th:replace="doc:SqlInjection_jdbc_completion.adoc"></div>
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN" method="POST" name="form" action="/WebGoat/SqlInjectionMitigations/attack10a" enctype="application/json;charset=UTF-8">
|
||||
<form class="attack-form" accept-charset="UNKNOWN" method="POST" name="form" action="/WebGoat/SqlInjectionMitigations/attack10a">
|
||||
<div>
|
||||
<p>Connection conn = DriverManager.<input type="text" name="field1" id="field1" />(DBURL, DBUSER, DBPW);</p>
|
||||
<p><input type="text" name="field2" id="field2" /> = conn.<input type="text" name="field3" id="field3" />("SELECT status FROM users WHERE name=<input type="text" name="field4" id="field4" /> AND mail=<input type="text" name="field5" id="field5" />");</p>
|
||||
@ -42,7 +42,7 @@
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:SqlInjection_jdbc_newcode.adoc"></div>
|
||||
<div class="attack-container" style="border: none !important; height: 100%; min-height: 300px;">
|
||||
<form id="codesubmit" style="height: 100%; min-height: 300px;" class="attack-form" accept-charset="UNKNOWN" method="POST" name="form" action="/WebGoat/SqlInjectionMitigations/attack10b" enctype="application/json;charset=UTF-8">
|
||||
<form id="codesubmit" style="height: 100%; min-height: 300px;" class="attack-form" accept-charset="UNKNOWN" method="POST" name="form" action="/WebGoat/SqlInjectionMitigations/attack10b">
|
||||
<div>
|
||||
<div id="editor" style="position: absolute; top: 0; right: 0; bottom: 0; left: 0; height: 300px;" name="editor"></div>
|
||||
<script th:src="@{/js/libs/ace/src-noconflict/ace.js}" type="text/javascript" charset="utf-8"></script>
|
||||
@ -78,8 +78,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SqlInjectionMitigations/attack12a"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/SqlInjectionMitigations/attack12a">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="panel panel-primary">
|
||||
|
@ -12,8 +12,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SSRF/task1"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/SSRF/task1">
|
||||
<table>
|
||||
<tr>
|
||||
<input type="hidden" id="url" name="url" value="images/tom.png"/>
|
||||
@ -35,8 +34,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/SSRF/task2"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/SSRF/task2">
|
||||
<table>
|
||||
<tr>
|
||||
<input type="hidden" id="url" name="url" value="images/cat.png"/>
|
||||
|
@ -144,11 +144,10 @@
|
||||
<!-- of course, you can write your own ajax submission /handling in your own javascript if you like -->
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/VulnerableComponents/attack1"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/VulnerableComponents/attack1">
|
||||
<div id="lessonContent">
|
||||
<form accept-charset="UNKNOWN" method="POST" name="form"
|
||||
action="#attack/307/100" enctype="">
|
||||
action="#attack/307/100">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Enter the contact's xml representation:</td>
|
||||
|
@ -47,8 +47,7 @@
|
||||
<!-- modify the action to point to the intended endpoint and set other attributes as desired -->
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/lesson-template/sample-attack"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/lesson-template/sample-attack">
|
||||
<table>
|
||||
<tr>
|
||||
<td>two random params</td>
|
||||
|
@ -85,8 +85,7 @@ green when the user solves the assignment. To make this work in the html we need
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/lesson-template/sample-attack"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/lesson-template/sample-attack">
|
||||
<table>
|
||||
<tr>
|
||||
<td>two random params</td>
|
||||
|
@ -16,8 +16,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/WebWolf/mail/"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/WebWolf/mail/">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
@ -38,8 +37,7 @@
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/WebWolf/mail/send"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/WebWolf/mail/send">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
@ -76,8 +74,7 @@
|
||||
<br/>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/WebWolf/landing/"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
action="/WebGoat/WebWolf/landing/">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
|
@ -34,6 +34,11 @@
|
||||
<artifactId>client-side-filtering</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>crypto</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>cross-site-scripting</artifactId>
|
||||
@ -89,6 +94,11 @@
|
||||
<artifactId>jwt</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>path-traversal</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>sql-injection</artifactId>
|
||||
@ -143,11 +153,6 @@
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>webgoat-lesson-template</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||
<artifactId>crypto</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|