* Some initial refactoring * Make it one application * Got it working * Fix problem on Windows * Move WebWolf * Move first lesson * Moved all lessons * Fix pom.xml * Fix tests * Add option to initialize a lesson This way we can create content for each user inside a lesson. The initialize method will be called when a new user is created or when a lesson reset happens * Clean up pom.xml files * Remove fetching labels based on language. We only support English at the moment, all the lesson explanations are written in English which makes it very difficult to translate. If we only had labels it would make sense to support multiple languages * Fix SonarLint issues * And move it all to the main project * Fix for documentation paths * Fix pom warnings * Remove PMD as it does not work * Update release notes about refactoring Update release notes about refactoring Update release notes about refactoring * Fix lesson template * Update release notes * Keep it in the same repo in Dockerhub * Update documentation to show how the connection is obtained. Resolves: #1180 * Rename all integration tests * Remove command from Dockerfile * Simplify GitHub actions Currently, we use a separate actions for pull-requests and branch build. This is now consolidated in one action. The PR action triggers always, it now only trigger when the PR is opened and not in draft. Running all platforms on a branch build is a bit too much, it is better to only run all platforms when someone opens a PR. * Remove duplicate entry from release notes * Add explicit registry for base image * Lesson scanner not working when fat jar When running the fat jar we have to take into account we are reading from the jar file and not the filesystem. In this case you cannot use `getFile` for example. * added info in README and fixed release docker * changed base image and added ignore file Co-authored-by: Zubcevic.com <rene@zubcevic.com>
260 lines
10 KiB
Java
260 lines
10 KiB
Java
package org.owasp.webgoat;
|
|
|
|
|
|
import io.restassured.RestAssured;
|
|
import io.restassured.http.ContentType;
|
|
import lombok.Data;
|
|
import lombok.SneakyThrows;
|
|
import org.junit.jupiter.api.AfterEach;
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
import org.junit.jupiter.api.DynamicTest;
|
|
import org.junit.jupiter.api.TestFactory;
|
|
import org.owasp.webgoat.container.lessons.Assignment;
|
|
|
|
import java.io.IOException;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
import java.util.Arrays;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
|
|
|
public class CSRFIntegrationTest extends IntegrationTest {
|
|
|
|
private static final String trickHTML3 = "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n" +
|
|
"<input type=\"hidden\" name=\"csrf\" value=\"thisisnotchecked\"/>\n" +
|
|
"<input type=\"submit\" name=\"submit\" value=\"assignment 3\"/>\n" +
|
|
"</form></body></html>";
|
|
|
|
private static final String trickHTML4 = "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n" +
|
|
"<input type=\"hidden\" name=\"reviewText\" value=\"hoi\"/>\n" +
|
|
"<input type=\"hidden\" name=\"starts\" value=\"3\"/>\n" +
|
|
"<input type=\"hidden\" name=\"validateReq\" value=\"2aa14227b9a13d0bede0388a7fba9aa9\"/>\n" +
|
|
"<input type=\"submit\" name=\"submit\" value=\"assignment 4\"/>\n" +
|
|
"</form>\n" +
|
|
"</body></html>";
|
|
|
|
private static final String trickHTML7 = "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" enctype='text/plain' method=\"POST\">\n" +
|
|
"<input type=\"hidden\" name='{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the best!!' value='\"}' />\n" +
|
|
"<input type=\"submit\" value=\"assignment 7\"/>\n" +
|
|
"</form></body></html>";
|
|
|
|
private static final String trickHTML8 = "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n" +
|
|
"<input type=\"hidden\" name=\"username\" value=\"csrf-USERNAME\"/>\n" +
|
|
"<input type=\"hidden\" name=\"password\" value=\"password\"/>\n" +
|
|
"<input type=\"hidden\" name=\"matchingPassword\" value=\"password\"/>\n" +
|
|
"<input type=\"hidden\" name=\"agree\" value=\"agree\"/>\n" +
|
|
"<input type=\"submit\" value=\"assignment 8\"/>\n" +
|
|
"</form></body></html>";
|
|
|
|
private String webwolfFileDir;
|
|
|
|
@BeforeEach
|
|
@SneakyThrows
|
|
public void init() {
|
|
startLesson("CSRF");
|
|
webwolfFileDir = getWebWolfFileServerLocation();
|
|
uploadTrickHtml("csrf3.html", trickHTML3.replace("WEBGOATURL", url("/csrf/basic-get-flag")));
|
|
uploadTrickHtml("csrf4.html", trickHTML4.replace("WEBGOATURL", url("/csrf/review")));
|
|
uploadTrickHtml("csrf7.html", trickHTML7.replace("WEBGOATURL", url("/csrf/feedback/message")));
|
|
uploadTrickHtml("csrf8.html", trickHTML8.replace("WEBGOATURL", url("/login")).replace("USERNAME", this.getUser()));
|
|
}
|
|
|
|
@TestFactory
|
|
Iterable<DynamicTest> testCSRFLesson() {
|
|
return Arrays.asList(
|
|
dynamicTest("assignment 3", () -> checkAssignment3(callTrickHtml("csrf3.html"))),
|
|
dynamicTest("assignment 4", () -> checkAssignment4(callTrickHtml("csrf4.html"))),
|
|
dynamicTest("assignment 7", () -> checkAssignment7(callTrickHtml("csrf7.html"))),
|
|
dynamicTest("assignment 8", () -> checkAssignment8(callTrickHtml("csrf8.html")))
|
|
);
|
|
}
|
|
|
|
@AfterEach
|
|
public void shutdown() throws IOException {
|
|
//logout();
|
|
login();//because old cookie got replaced and invalidated
|
|
startLesson("CSRF", false);
|
|
checkResults("/csrf");
|
|
}
|
|
|
|
private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException {
|
|
|
|
//remove any left over html
|
|
Path webWolfFilePath = Paths.get(webwolfFileDir);
|
|
if (webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)).toFile().exists()) {
|
|
Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)));
|
|
}
|
|
|
|
//upload trick html
|
|
RestAssured.given()
|
|
.when()
|
|
.relaxedHTTPSValidation()
|
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
|
.multiPart("file", htmlName, htmlContent.getBytes())
|
|
.post(webWolfUrl("/WebWolf/fileupload"))
|
|
.then()
|
|
.extract().response().getBody().asString();
|
|
}
|
|
|
|
private String callTrickHtml(String htmlName) {
|
|
String result = RestAssured.given()
|
|
.when()
|
|
.relaxedHTTPSValidation()
|
|
.cookie("JSESSIONID", getWebGoatCookie())
|
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
|
.get(webWolfUrl("/files/" + this.getUser() + "/" + htmlName))
|
|
.then()
|
|
.extract().response().getBody().asString();
|
|
result = result.substring(8 + result.indexOf("action=\""));
|
|
result = result.substring(0, result.indexOf("\""));
|
|
|
|
return result;
|
|
}
|
|
|
|
private void checkAssignment3(String goatURL) {
|
|
String flag = RestAssured.given()
|
|
.when()
|
|
.relaxedHTTPSValidation()
|
|
.cookie("JSESSIONID", getWebGoatCookie())
|
|
.header("Referer", webWolfUrl("/files/fake.html"))
|
|
.post(goatURL)
|
|
.then()
|
|
.extract().path("flag").toString();
|
|
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.clear();
|
|
params.put("confirmFlagVal", flag);
|
|
checkAssignment(url("/WebGoat/csrf/confirm-flag-1"), params, true);
|
|
}
|
|
|
|
private void checkAssignment4(String goatURL) {
|
|
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.clear();
|
|
params.put("reviewText", "test review");
|
|
params.put("stars", "5");
|
|
params.put("validateReq", "2aa14227b9a13d0bede0388a7fba9aa9");//always the same token is the weakness
|
|
|
|
boolean result = RestAssured.given()
|
|
.when()
|
|
.relaxedHTTPSValidation()
|
|
.cookie("JSESSIONID", getWebGoatCookie())
|
|
.header("Referer", webWolfUrl("/files/fake.html"))
|
|
.formParams(params)
|
|
.post(goatURL)
|
|
.then()
|
|
.extract().path("lessonCompleted");
|
|
assertEquals(true, result);
|
|
|
|
}
|
|
|
|
private void checkAssignment7(String goatURL) {
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.put("{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the best!!", "\"}");
|
|
|
|
String flag = RestAssured.given()
|
|
.when()
|
|
.relaxedHTTPSValidation()
|
|
.cookie("JSESSIONID", getWebGoatCookie())
|
|
.header("Referer", webWolfUrl("/files/fake.html"))
|
|
.contentType(ContentType.TEXT)
|
|
.body("{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the best!!" + "=\"}")
|
|
.post(goatURL)
|
|
.then()
|
|
.extract().asString();
|
|
flag = flag.substring(9 + flag.indexOf("flag is:"));
|
|
flag = flag.substring(0, flag.indexOf("\""));
|
|
|
|
params.clear();
|
|
params.put("confirmFlagVal", flag);
|
|
checkAssignment(url("/WebGoat/csrf/feedback"), params, true);
|
|
|
|
}
|
|
|
|
private void checkAssignment8(String goatURL) {
|
|
|
|
//first make sure there is an attack csrf- user
|
|
registerCSRFUser();
|
|
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.clear();
|
|
params.put("username", "csrf-" + this.getUser());
|
|
params.put("password", "password");
|
|
|
|
//login and get the new cookie
|
|
String newCookie = RestAssured.given()
|
|
.when()
|
|
.relaxedHTTPSValidation()
|
|
.cookie("JSESSIONID", getWebGoatCookie())
|
|
.header("Referer", webWolfUrl("/files/fake.html"))
|
|
.params(params)
|
|
.post(goatURL)
|
|
.then()
|
|
.extract().cookie("JSESSIONID");
|
|
|
|
//select the lesson
|
|
RestAssured.given()
|
|
.when()
|
|
.relaxedHTTPSValidation()
|
|
.cookie("JSESSIONID", newCookie)
|
|
.get(url("CSRF.lesson.lesson"))
|
|
.then()
|
|
.statusCode(200);
|
|
|
|
//click on the assignment
|
|
boolean result = RestAssured.given()
|
|
.when()
|
|
.relaxedHTTPSValidation()
|
|
.cookie("JSESSIONID", newCookie)
|
|
.post(url("/csrf/login"))
|
|
.then()
|
|
.statusCode(200)
|
|
.extract().path("lessonCompleted");
|
|
|
|
assertThat(result).isTrue();
|
|
|
|
login();
|
|
startLesson("CSRF", false);
|
|
|
|
Overview[] assignments = RestAssured.given()
|
|
.cookie("JSESSIONID", getWebGoatCookie())
|
|
.get(url("/service/lessonoverview.mvc"))
|
|
.then()
|
|
.extract()
|
|
.jsonPath()
|
|
.getObject("$", Overview[].class);
|
|
// assertThat(assignments)
|
|
// .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin"))
|
|
// .extracting(o -> o.solved)
|
|
// .containsExactly(true);
|
|
}
|
|
|
|
@Data
|
|
private static class Overview {
|
|
Assignment assignment;
|
|
boolean solved;
|
|
}
|
|
|
|
/**
|
|
* Try to register the new user. Ignore the result.
|
|
*/
|
|
private void registerCSRFUser() {
|
|
|
|
RestAssured.given()
|
|
.when()
|
|
.relaxedHTTPSValidation()
|
|
.formParam("username", "csrf-" + this.getUser())
|
|
.formParam("password", "password")
|
|
.formParam("matchingPassword", "password")
|
|
.formParam("agree", "agree")
|
|
.post(url("register.mvc"));
|
|
|
|
}
|
|
|
|
}
|