Apply formatting

This will make sure we have a consistent style across our project and the PRs are only concerned with actual changes and no longer about style.
This commit is contained in:
Nanne Baars
2023-01-04 08:07:23 +01:00
committed by GitHub
parent b03777d39b
commit d2a1546dff
336 changed files with 13921 additions and 12688 deletions

View File

@@ -25,63 +25,72 @@ package org.owasp.webgoat.lessons.authbypass;
import java.util.HashMap;
import java.util.Map;
/**
* Created by appsec on 7/18/17.
*/
/** Created by appsec on 7/18/17. */
public class AccountVerificationHelper {
//simulating database storage of verification credentials
private static final Integer verifyUserId = 1223445;
private static final Map<String, String> userSecQuestions = new HashMap<>();
// simulating database storage of verification credentials
private static final Integer verifyUserId = 1223445;
private static final Map<String, String> userSecQuestions = new HashMap<>();
static {
userSecQuestions.put("secQuestion0", "Dr. Watson");
userSecQuestions.put("secQuestion1", "Baker Street");
static {
userSecQuestions.put("secQuestion0", "Dr. Watson");
userSecQuestions.put("secQuestion1", "Baker Street");
}
private static final Map<Integer, Map> secQuestionStore = new HashMap<>();
static {
secQuestionStore.put(verifyUserId, userSecQuestions);
}
// end 'data store set up'
// this is to aid feedback in the attack process and is not intended to be part of the
// 'vulnerable' code
public boolean didUserLikelylCheat(HashMap<String, String> submittedAnswers) {
boolean likely = false;
if (submittedAnswers.size() == secQuestionStore.get(verifyUserId).size()) {
likely = true;
}
private static final Map<Integer, Map> secQuestionStore = new HashMap<>();
static {
secQuestionStore.put(verifyUserId, userSecQuestions);
if ((submittedAnswers.containsKey("secQuestion0")
&& submittedAnswers
.get("secQuestion0")
.equals(secQuestionStore.get(verifyUserId).get("secQuestion0")))
&& (submittedAnswers.containsKey("secQuestion1")
&& submittedAnswers
.get("secQuestion1")
.equals(secQuestionStore.get(verifyUserId).get("secQuestion1")))) {
likely = true;
} else {
likely = false;
}
// end 'data store set up'
// this is to aid feedback in the attack process and is not intended to be part of the 'vulnerable' code
public boolean didUserLikelylCheat(HashMap<String, String> submittedAnswers) {
boolean likely = false;
if (submittedAnswers.size() == secQuestionStore.get(verifyUserId).size()) {
likely = true;
}
if ((submittedAnswers.containsKey("secQuestion0") && submittedAnswers.get("secQuestion0").equals(secQuestionStore.get(verifyUserId).get("secQuestion0")))
&& (submittedAnswers.containsKey("secQuestion1") && submittedAnswers.get("secQuestion1").equals(secQuestionStore.get(verifyUserId).get("secQuestion1")))) {
likely = true;
} else {
likely = false;
}
return likely;
return likely;
}
// end of cheating check ... the method below is the one of real interest. Can you find the flaw?
public boolean verifyAccount(Integer userId, HashMap<String, String> submittedQuestions) {
// short circuit if no questions are submitted
if (submittedQuestions.entrySet().size() != secQuestionStore.get(verifyUserId).size()) {
return false;
}
//end of cheating check ... the method below is the one of real interest. Can you find the flaw?
public boolean verifyAccount(Integer userId, HashMap<String, String> submittedQuestions) {
//short circuit if no questions are submitted
if (submittedQuestions.entrySet().size() != secQuestionStore.get(verifyUserId).size()) {
return false;
}
if (submittedQuestions.containsKey("secQuestion0") && !submittedQuestions.get("secQuestion0").equals(secQuestionStore.get(verifyUserId).get("secQuestion0"))) {
return false;
}
if (submittedQuestions.containsKey("secQuestion1") && !submittedQuestions.get("secQuestion1").equals(secQuestionStore.get(verifyUserId).get("secQuestion1"))) {
return false;
}
// else
return true;
if (submittedQuestions.containsKey("secQuestion0")
&& !submittedQuestions
.get("secQuestion0")
.equals(secQuestionStore.get(verifyUserId).get("secQuestion0"))) {
return false;
}
if (submittedQuestions.containsKey("secQuestion1")
&& !submittedQuestions
.get("secQuestion1")
.equals(secQuestionStore.get(verifyUserId).get("secQuestion1"))) {
return false;
}
// else
return true;
}
}

View File

@@ -29,13 +29,13 @@ import org.springframework.stereotype.Component;
@Component
public class AuthBypass extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.A7;
}
@Override
public Category getDefaultCategory() {
return Category.A7;
}
@Override
public String getTitle() {
return "auth-bypass.title";
}
@Override
public String getTitle() {
return "auth-bypass.title";
}
}

View File

@@ -22,6 +22,13 @@
package org.owasp.webgoat.lessons.authbypass;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
@@ -33,63 +40,54 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by jason on 1/5/17.
*/
/** Created by jason on 1/5/17. */
@RestController
@AssignmentHints({"auth-bypass.hints.verify.1", "auth-bypass.hints.verify.2", "auth-bypass.hints.verify.3", "auth-bypass.hints.verify.4"})
@AssignmentHints({
"auth-bypass.hints.verify.1",
"auth-bypass.hints.verify.2",
"auth-bypass.hints.verify.3",
"auth-bypass.hints.verify.4"
})
public class VerifyAccount extends AssignmentEndpoint {
@Autowired
private WebSession webSession;
@Autowired private WebSession webSession;
@Autowired
UserSessionData userSessionData;
@PostMapping(path = "/auth-bypass/verify-account", produces = {"application/json"})
@ResponseBody
public AttackResult completed(@RequestParam String userId, @RequestParam String verifyMethod, HttpServletRequest req) throws ServletException, IOException {
AccountVerificationHelper verificationHelper = new AccountVerificationHelper();
Map<String, String> submittedAnswers = parseSecQuestions(req);
if (verificationHelper.didUserLikelylCheat((HashMap) submittedAnswers)) {
return failed(this)
.feedback("verify-account.cheated")
.output("Yes, you guessed correctly, but see the feedback message")
.build();
}
// else
if (verificationHelper.verifyAccount(Integer.valueOf(userId), (HashMap) submittedAnswers)) {
userSessionData.setValue("account-verified-id", userId);
return success(this)
.feedback("verify-account.success")
.build();
} else {
return failed(this)
.feedback("verify-account.failed")
.build();
}
@Autowired UserSessionData userSessionData;
@PostMapping(
path = "/auth-bypass/verify-account",
produces = {"application/json"})
@ResponseBody
public AttackResult completed(
@RequestParam String userId, @RequestParam String verifyMethod, HttpServletRequest req)
throws ServletException, IOException {
AccountVerificationHelper verificationHelper = new AccountVerificationHelper();
Map<String, String> submittedAnswers = parseSecQuestions(req);
if (verificationHelper.didUserLikelylCheat((HashMap) submittedAnswers)) {
return failed(this)
.feedback("verify-account.cheated")
.output("Yes, you guessed correctly, but see the feedback message")
.build();
}
private HashMap<String, String> parseSecQuestions(HttpServletRequest req) {
Map<String, String> userAnswers = new HashMap<>();
List<String> paramNames = Collections.list(req.getParameterNames());
for (String paramName : paramNames) {
//String paramName = req.getParameterNames().nextElement();
if (paramName.contains("secQuestion")) {
userAnswers.put(paramName, req.getParameter(paramName));
}
}
return (HashMap) userAnswers;
// else
if (verificationHelper.verifyAccount(Integer.valueOf(userId), (HashMap) submittedAnswers)) {
userSessionData.setValue("account-verified-id", userId);
return success(this).feedback("verify-account.success").build();
} else {
return failed(this).feedback("verify-account.failed").build();
}
}
private HashMap<String, String> parseSecQuestions(HttpServletRequest req) {
Map<String, String> userAnswers = new HashMap<>();
List<String> paramNames = Collections.list(req.getParameterNames());
for (String paramName : paramNames) {
// String paramName = req.getParameterNames().nextElement();
if (paramName.contains("secQuestion")) {
userAnswers.put(paramName, req.getParameter(paramName));
}
}
return (HashMap) userAnswers;
}
}

View File

@@ -28,13 +28,13 @@ import org.springframework.stereotype.Component;
@Component
public class BypassRestrictions extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.CLIENT_SIDE;
}
@Override
public Category getDefaultCategory() {
return Category.CLIENT_SIDE;
}
@Override
public String getTitle() {
return "bypass-restrictions.title";
}
@Override
public String getTitle() {
return "bypass-restrictions.title";
}
}

View File

@@ -32,24 +32,29 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
public class BypassRestrictionsFieldRestrictions extends AssignmentEndpoint {
@PostMapping("/BypassRestrictions/FieldRestrictions")
@ResponseBody
public AttackResult completed(@RequestParam String select, @RequestParam String radio, @RequestParam String checkbox, @RequestParam String shortInput, @RequestParam String readOnlyInput) {
if (select.equals("option1") || select.equals("option2")) {
return failed(this).build();
}
if (radio.equals("option1") || radio.equals("option2")) {
return failed(this).build();
}
if (checkbox.equals("on") || checkbox.equals("off")) {
return failed(this).build();
}
if (shortInput.length() <= 5) {
return failed(this).build();
}
if ("change".equals(readOnlyInput)) {
return failed(this).build();
}
return success(this).build();
@PostMapping("/BypassRestrictions/FieldRestrictions")
@ResponseBody
public AttackResult completed(
@RequestParam String select,
@RequestParam String radio,
@RequestParam String checkbox,
@RequestParam String shortInput,
@RequestParam String readOnlyInput) {
if (select.equals("option1") || select.equals("option2")) {
return failed(this).build();
}
if (radio.equals("option1") || radio.equals("option2")) {
return failed(this).build();
}
if (checkbox.equals("on") || checkbox.equals("off")) {
return failed(this).build();
}
if (shortInput.length() <= 5) {
return failed(this).build();
}
if ("change".equals(readOnlyInput)) {
return failed(this).build();
}
return success(this).build();
}
}

View File

@@ -32,40 +32,48 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
public class BypassRestrictionsFrontendValidation extends AssignmentEndpoint {
@PostMapping("/BypassRestrictions/frontendValidation")
@ResponseBody
public AttackResult completed(@RequestParam String field1, @RequestParam String field2, @RequestParam String field3, @RequestParam String field4, @RequestParam String field5, @RequestParam String field6, @RequestParam String field7, @RequestParam Integer error) {
final String regex1 = "^[a-z]{3}$";
final String regex2 = "^[0-9]{3}$";
final String regex3 = "^[a-zA-Z0-9 ]*$";
final String regex4 = "^(one|two|three|four|five|six|seven|eight|nine)$";
final String regex5 = "^\\d{5}$";
final String regex6 = "^\\d{5}(-\\d{4})?$";
final String regex7 = "^[2-9]\\d{2}-?\\d{3}-?\\d{4}$";
if (error > 0) {
return failed(this).build();
}
if (field1.matches(regex1)) {
return failed(this).build();
}
if (field2.matches(regex2)) {
return failed(this).build();
}
if (field3.matches(regex3)) {
return failed(this).build();
}
if (field4.matches(regex4)) {
return failed(this).build();
}
if (field5.matches(regex5)) {
return failed(this).build();
}
if (field6.matches(regex6)) {
return failed(this).build();
}
if (field7.matches(regex7)) {
return failed(this).build();
}
return success(this).build();
@PostMapping("/BypassRestrictions/frontendValidation")
@ResponseBody
public AttackResult completed(
@RequestParam String field1,
@RequestParam String field2,
@RequestParam String field3,
@RequestParam String field4,
@RequestParam String field5,
@RequestParam String field6,
@RequestParam String field7,
@RequestParam Integer error) {
final String regex1 = "^[a-z]{3}$";
final String regex2 = "^[0-9]{3}$";
final String regex3 = "^[a-zA-Z0-9 ]*$";
final String regex4 = "^(one|two|three|four|five|six|seven|eight|nine)$";
final String regex5 = "^\\d{5}$";
final String regex6 = "^\\d{5}(-\\d{4})?$";
final String regex7 = "^[2-9]\\d{2}-?\\d{3}-?\\d{4}$";
if (error > 0) {
return failed(this).build();
}
if (field1.matches(regex1)) {
return failed(this).build();
}
if (field2.matches(regex2)) {
return failed(this).build();
}
if (field3.matches(regex3)) {
return failed(this).build();
}
if (field4.matches(regex4)) {
return failed(this).build();
}
if (field5.matches(regex5)) {
return failed(this).build();
}
if (field6.matches(regex6)) {
return failed(this).build();
}
if (field7.matches(regex7)) {
return failed(this).build();
}
return success(this).build();
}
}

View File

@@ -9,13 +9,13 @@ import org.owasp.webgoat.container.lessons.Lesson;
*/
public class ChallengeIntro extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.CHALLENGE;
}
@Override
public Category getDefaultCategory() {
return Category.CHALLENGE;
}
@Override
public String getTitle() {
return "challenge0.title";
}
@Override
public String getTitle() {
return "challenge0.title";
}
}

View File

@@ -22,11 +22,10 @@
package org.owasp.webgoat.lessons.challenges;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Builder;
import lombok.Data;
/**
* @author nbaars
@@ -36,9 +35,9 @@ import java.time.LocalDateTime;
@Data
public class Email implements Serializable {
private LocalDateTime time;
private String contents;
private String sender;
private String title;
private String recipient;
private LocalDateTime time;
private String contents;
private String sender;
private String title;
private String recipient;
}

View File

@@ -22,6 +22,11 @@
package org.owasp.webgoat.lessons.challenges;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.IntStream;
import javax.annotation.PostConstruct;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
@@ -37,12 +42,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.IntStream;
/**
* @author nbaars
* @since 3/23/17.
@@ -50,39 +49,41 @@ import java.util.stream.IntStream;
@RestController
public class Flag extends AssignmentEndpoint {
public static final Map<Integer, String> FLAGS = new HashMap<>();
@Autowired
private UserTrackerRepository userTrackerRepository;
@Autowired
private WebSession webSession;
public static final Map<Integer, String> FLAGS = new HashMap<>();
@Autowired private UserTrackerRepository userTrackerRepository;
@Autowired private WebSession webSession;
@AllArgsConstructor
private class FlagPosted {
@Getter
private boolean lessonCompleted;
}
@AllArgsConstructor
private class FlagPosted {
@Getter private boolean lessonCompleted;
}
@PostConstruct
public void initFlags() {
IntStream.range(1, 10).forEach(i -> FLAGS.put(i, UUID.randomUUID().toString()));
}
@PostConstruct
public void initFlags() {
IntStream.range(1, 10).forEach(i -> FLAGS.put(i, UUID.randomUUID().toString()));
}
@RequestMapping(path = "/challenge/flag", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public AttackResult postFlag(@RequestParam String flag) {
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
String currentChallenge = webSession.getCurrentLesson().getName();
int challengeNumber = Integer.valueOf(currentChallenge.substring(currentChallenge.length() - 1, currentChallenge.length()));
String expectedFlag = FLAGS.get(challengeNumber);
final AttackResult attackResult;
if (expectedFlag.equals(flag)) {
userTracker.assignmentSolved(webSession.getCurrentLesson(), "Assignment" + challengeNumber);
attackResult = success(this).feedback("challenge.flag.correct").build();
} else {
userTracker.assignmentFailed(webSession.getCurrentLesson());
attackResult = failed(this).feedback("challenge.flag.incorrect").build();
}
userTrackerRepository.save(userTracker);
return attackResult;
@RequestMapping(
path = "/challenge/flag",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public AttackResult postFlag(@RequestParam String flag) {
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
String currentChallenge = webSession.getCurrentLesson().getName();
int challengeNumber =
Integer.valueOf(
currentChallenge.substring(currentChallenge.length() - 1, currentChallenge.length()));
String expectedFlag = FLAGS.get(challengeNumber);
final AttackResult attackResult;
if (expectedFlag.equals(flag)) {
userTracker.assignmentSolved(webSession.getCurrentLesson(), "Assignment" + challengeNumber);
attackResult = success(this).feedback("challenge.flag.correct").build();
} else {
userTracker.assignmentFailed(webSession.getCurrentLesson());
attackResult = failed(this).feedback("challenge.flag.incorrect").build();
}
userTrackerRepository.save(userTracker);
return attackResult;
}
}

View File

@@ -30,8 +30,8 @@ package org.owasp.webgoat.lessons.challenges;
*/
public interface SolutionConstants {
//TODO should be random generated when starting the server
String PASSWORD = "!!webgoat_admin_1234!!";
String PASSWORD_TOM = "thisisasecretfortomonly";
String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2";
// TODO should be random generated when starting the server
String PASSWORD = "!!webgoat_admin_1234!!";
String PASSWORD_TOM = "thisisasecretfortomonly";
String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2";
}

View File

@@ -1,5 +1,8 @@
package org.owasp.webgoat.lessons.challenges.challenge1;
import static org.owasp.webgoat.lessons.challenges.SolutionConstants.PASSWORD;
import javax.servlet.http.HttpServletRequest;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.lessons.challenges.Flag;
@@ -9,33 +12,30 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import static org.owasp.webgoat.lessons.challenges.SolutionConstants.PASSWORD;
/**
* ************************************************************************************************
* 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 - 2014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
*
* <p>Copyright (c) 2002 - 2014 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
*
* <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>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* <p>
*
* @author WebGoat
@@ -45,20 +45,25 @@ import static org.owasp.webgoat.lessons.challenges.SolutionConstants.PASSWORD;
@RestController
public class Assignment1 extends AssignmentEndpoint {
@PostMapping("/challenge/1")
@ResponseBody
public AttackResult completed(@RequestParam String username, @RequestParam String password, HttpServletRequest request) {
boolean ipAddressKnown = true;
boolean passwordCorrect = "admin".equals(username) && PASSWORD.replace("1234", String.format("%04d",ImageServlet.PINCODE)).equals(password);
if (passwordCorrect && ipAddressKnown) {
return success(this).feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(1)).build();
} else if (passwordCorrect) {
return failed(this).feedback("ip.address.unknown").build();
}
return failed(this).build();
@PostMapping("/challenge/1")
@ResponseBody
public AttackResult completed(
@RequestParam String username, @RequestParam String password, HttpServletRequest request) {
boolean ipAddressKnown = true;
boolean passwordCorrect =
"admin".equals(username)
&& PASSWORD
.replace("1234", String.format("%04d", ImageServlet.PINCODE))
.equals(password);
if (passwordCorrect && ipAddressKnown) {
return success(this).feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(1)).build();
} else if (passwordCorrect) {
return failed(this).feedback("ip.address.unknown").build();
}
return failed(this).build();
}
public static boolean containsHeader(HttpServletRequest request) {
return StringUtils.hasText(request.getHeader("X-Forwarded-For"));
}
public static boolean containsHeader(HttpServletRequest request) {
return StringUtils.hasText(request.getHeader("X-Forwarded-For"));
}
}

View File

@@ -11,13 +11,13 @@ import org.springframework.stereotype.Component;
@Component
public class Challenge1 extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.CHALLENGE;
}
@Override
public Category getDefaultCategory() {
return Category.CHALLENGE;
}
@Override
public String getTitle() {
return "challenge1.title";
}
@Override
public String getTitle() {
return "challenge1.title";
}
}

View File

@@ -1,36 +1,41 @@
package org.owasp.webgoat.lessons.challenges.challenge1;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import java.io.IOException;
import java.security.SecureRandom;
import javax.servlet.http.HttpServlet;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServlet;
import java.io.IOException;
import java.security.SecureRandom;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
@RestController
public class ImageServlet extends HttpServlet {
private static final long serialVersionUID = 9132775506936676850L;
static final public int PINCODE = new SecureRandom().nextInt(10000);
@RequestMapping(method = {GET, POST}, value = "/challenge/logo", produces = MediaType.IMAGE_PNG_VALUE)
@ResponseBody
public byte[] logo() throws IOException {
byte[] in = new ClassPathResource("lessons/challenges/images/webgoat2.png").getInputStream().readAllBytes();
String pincode = String.format("%04d", PINCODE);
in[81216]=(byte) pincode.charAt(0);
in[81217]=(byte) pincode.charAt(1);
in[81218]=(byte) pincode.charAt(2);
in[81219]=(byte) pincode.charAt(3);
private static final long serialVersionUID = 9132775506936676850L;
public static final int PINCODE = new SecureRandom().nextInt(10000);
return in;
}
@RequestMapping(
method = {GET, POST},
value = "/challenge/logo",
produces = MediaType.IMAGE_PNG_VALUE)
@ResponseBody
public byte[] logo() throws IOException {
byte[] in =
new ClassPathResource("lessons/challenges/images/webgoat2.png")
.getInputStream()
.readAllBytes();
String pincode = String.format("%04d", PINCODE);
in[81216] = (byte) pincode.charAt(0);
in[81217] = (byte) pincode.charAt(1);
in[81218] = (byte) pincode.charAt(2);
in[81219] = (byte) pincode.charAt(3);
return in;
}
}

View File

@@ -22,6 +22,8 @@
package org.owasp.webgoat.lessons.challenges.challenge5;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.container.LessonDataSource;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
@@ -33,38 +35,41 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@RestController
@Slf4j
public class Assignment5 extends AssignmentEndpoint {
private final LessonDataSource dataSource;
private final LessonDataSource dataSource;
public Assignment5(LessonDataSource dataSource) {
this.dataSource = dataSource;
public Assignment5(LessonDataSource dataSource) {
this.dataSource = dataSource;
}
@PostMapping("/challenge/5")
@ResponseBody
public AttackResult login(
@RequestParam String username_login, @RequestParam String password_login) throws Exception {
if (!StringUtils.hasText(username_login) || !StringUtils.hasText(password_login)) {
return failed(this).feedback("required4").build();
}
@PostMapping("/challenge/5")
@ResponseBody
public AttackResult login(@RequestParam String username_login, @RequestParam String password_login) throws Exception {
if (!StringUtils.hasText(username_login) || !StringUtils.hasText(password_login)) {
return failed(this).feedback("required4").build();
}
if (!"Larry".equals(username_login)) {
return failed(this).feedback("user.not.larry").feedbackArgs(username_login).build();
}
try (var connection = dataSource.getConnection()) {
PreparedStatement statement = connection.prepareStatement("select password from challenge_users where userid = '" + username_login + "' and password = '" + password_login + "'");
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
return success(this).feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(5)).build();
} else {
return failed(this).feedback("challenge.close").build();
}
}
if (!"Larry".equals(username_login)) {
return failed(this).feedback("user.not.larry").feedbackArgs(username_login).build();
}
try (var connection = dataSource.getConnection()) {
PreparedStatement statement =
connection.prepareStatement(
"select password from challenge_users where userid = '"
+ username_login
+ "' and password = '"
+ password_login
+ "'");
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
return success(this).feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(5)).build();
} else {
return failed(this).feedback("challenge.close").build();
}
}
}
}

View File

@@ -33,13 +33,13 @@ import org.springframework.stereotype.Component;
@Component
public class Challenge5 extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.CHALLENGE;
}
@Override
public Category getDefaultCategory() {
return Category.CHALLENGE;
}
@Override
public String getTitle() {
return "challenge5.title";
}
@Override
public String getTitle() {
return "challenge5.title";
}
}

View File

@@ -1,11 +1,15 @@
package org.owasp.webgoat.lessons.challenges.challenge7;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.LocalDateTime;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.lessons.challenges.Email;
import org.owasp.webgoat.lessons.challenges.SolutionConstants;
import org.owasp.webgoat.lessons.challenges.Flag;
import org.owasp.webgoat.lessons.challenges.SolutionConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
@@ -21,11 +25,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.LocalDateTime;
/**
* @author nbaars
* @since 4/8/17.
@@ -34,53 +33,67 @@ import java.time.LocalDateTime;
@Slf4j
public class Assignment7 extends AssignmentEndpoint {
private static final String TEMPLATE = "Hi, you requested a password reset link, please use this "
+ "<a target='_blank' href='%s:8080/WebGoat/challenge/7/reset-password/%s'>link</a> to reset your password."
+ "\n \n\n"
+ "If you did not request this password change you can ignore this message."
+ "\n"
+ "If you have any comments or questions, please do not hesitate to reach us at support@webgoat-cloud.org"
+ "\n\n"
+ "Kind regards, \nTeam WebGoat";
private static final String TEMPLATE =
"Hi, you requested a password reset link, please use this <a target='_blank'"
+ " href='%s:8080/WebGoat/challenge/7/reset-password/%s'>link</a> to reset your"
+ " password.\n"
+ " \n\n"
+ "If you did not request this password change you can ignore this message.\n"
+ "If you have any comments or questions, please do not hesitate to reach us at"
+ " support@webgoat-cloud.org\n\n"
+ "Kind regards, \n"
+ "Team WebGoat";
@Autowired
private RestTemplate restTemplate;
@Value("${webwolf.mail.url}")
private String webWolfMailURL;
@Autowired private RestTemplate restTemplate;
@GetMapping("/challenge/7/reset-password/{link}")
public ResponseEntity<String> resetPassword(@PathVariable(value = "link") String link) {
if (link.equals(SolutionConstants.ADMIN_PASSWORD_LINK)) {
return ResponseEntity.accepted().body("<h1>Success!!</h1>"
+ "<img src='/WebGoat/images/hi-five-cat.jpg'>"
+ "<br/><br/>Here is your flag: " + "<b>" + Flag.FLAGS.get(7) + "</b>");
}
return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT).body("That is not the reset link for admin");
@Value("${webwolf.mail.url}")
private String webWolfMailURL;
@GetMapping("/challenge/7/reset-password/{link}")
public ResponseEntity<String> resetPassword(@PathVariable(value = "link") String link) {
if (link.equals(SolutionConstants.ADMIN_PASSWORD_LINK)) {
return ResponseEntity.accepted()
.body(
"<h1>Success!!</h1>"
+ "<img src='/WebGoat/images/hi-five-cat.jpg'>"
+ "<br/><br/>Here is your flag: "
+ "<b>"
+ Flag.FLAGS.get(7)
+ "</b>");
}
return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT)
.body("That is not the reset link for admin");
}
@PostMapping("/challenge/7")
@ResponseBody
public AttackResult sendPasswordResetLink(@RequestParam String email, HttpServletRequest request) throws URISyntaxException {
if (StringUtils.hasText(email)) {
String username = email.substring(0, email.indexOf("@"));
if (StringUtils.hasText(username)) {
URI uri = new URI(request.getRequestURL().toString());
Email mail = Email.builder()
.title("Your password reset link for challenge 7")
.contents(String.format(TEMPLATE, uri.getScheme() + "://" + uri.getHost(), new PasswordResetLink().createPasswordReset(username, "webgoat")))
.sender("password-reset@webgoat-cloud.net")
.recipient(username)
.time(LocalDateTime.now()).build();
restTemplate.postForEntity(webWolfMailURL, mail, Object.class);
}
}
return success(this).feedback("email.send").feedbackArgs(email).build();
@PostMapping("/challenge/7")
@ResponseBody
public AttackResult sendPasswordResetLink(@RequestParam String email, HttpServletRequest request)
throws URISyntaxException {
if (StringUtils.hasText(email)) {
String username = email.substring(0, email.indexOf("@"));
if (StringUtils.hasText(username)) {
URI uri = new URI(request.getRequestURL().toString());
Email mail =
Email.builder()
.title("Your password reset link for challenge 7")
.contents(
String.format(
TEMPLATE,
uri.getScheme() + "://" + uri.getHost(),
new PasswordResetLink().createPasswordReset(username, "webgoat")))
.sender("password-reset@webgoat-cloud.net")
.recipient(username)
.time(LocalDateTime.now())
.build();
restTemplate.postForEntity(webWolfMailURL, mail, Object.class);
}
}
return success(this).feedback("email.send").feedbackArgs(email).build();
}
@GetMapping(value = "/challenge/7/.git", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@ResponseBody
public ClassPathResource git() {
return new ClassPathResource("challenge7/git.zip");
}
@GetMapping(value = "/challenge/7/.git", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@ResponseBody
public ClassPathResource git() {
return new ClassPathResource("challenge7/git.zip");
}
}

View File

@@ -11,13 +11,13 @@ import org.springframework.stereotype.Component;
@Component
public class Challenge7 extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.CHALLENGE;
}
@Override
public Category getDefaultCategory() {
return Category.CHALLENGE;
}
@Override
public String getTitle() {
return "challenge7.title";
}
@Override
public String getTitle() {
return "challenge7.title";
}
}

View File

@@ -10,34 +10,36 @@ import java.util.Random;
*/
public class PasswordResetLink {
public String createPasswordReset(String username, String key) {
Random random = new Random();
if (username.equalsIgnoreCase("admin")) {
//Admin has a fix reset link
random.setSeed(key.length());
}
return scramble(random, scramble(random, scramble(random, MD5.getHashString(username))));
public String createPasswordReset(String username, String key) {
Random random = new Random();
if (username.equalsIgnoreCase("admin")) {
// Admin has a fix reset link
random.setSeed(key.length());
}
return scramble(random, scramble(random, scramble(random, MD5.getHashString(username))));
}
public static String scramble(Random random, String inputString) {
char[] a = inputString.toCharArray();
for (int i = 0; i < a.length; i++) {
int j = random.nextInt(a.length);
char temp = a[i];
a[i] = a[j];
a[j] = temp;
}
return new String(a);
public static String scramble(Random random, String inputString) {
char[] a = inputString.toCharArray();
for (int i = 0; i < a.length; i++) {
int j = random.nextInt(a.length);
char temp = a[i];
a[i] = a[j];
a[j] = temp;
}
return new String(a);
}
public static void main(String[] args) {
if (args == null || args.length != 2) {
System.out.println("Need a username and key");
System.exit(1);
}
String username = args[0];
String key = args[1];
System.out.println("Generation password reset link for " + username);
System.out.println("Created password reset link: " + new PasswordResetLink().createPasswordReset(username, key));
public static void main(String[] args) {
if (args == null || args.length != 2) {
System.out.println("Need a username and key");
System.exit(1);
}
String username = args[0];
String key = args[1];
System.out.println("Generation password reset link for " + username);
System.out.println(
"Created password reset link: "
+ new PasswordResetLink().createPasswordReset(username, key));
}
}

View File

@@ -1,5 +1,9 @@
package org.owasp.webgoat.lessons.challenges.challenge8;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
@@ -11,11 +15,6 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author nbaars
* @since 4/8/17.
@@ -24,46 +23,54 @@ import java.util.stream.Collectors;
@Slf4j
public class Assignment8 extends AssignmentEndpoint {
private static final Map<Integer, Integer> votes = new HashMap<>();
private static final Map<Integer, Integer> votes = new HashMap<>();
static {
votes.put(1, 400);
votes.put(2, 120);
votes.put(3, 140);
votes.put(4, 150);
votes.put(5, 300);
}
static {
votes.put(1, 400);
votes.put(2, 120);
votes.put(3, 140);
votes.put(4, 150);
votes.put(5, 300);
}
@GetMapping(value = "/challenge/8/vote/{stars}", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<?> vote(@PathVariable(value = "stars") int nrOfStars, HttpServletRequest request) {
//Simple implementation of VERB Based Authentication
String msg = "";
if (request.getMethod().equals("GET")) {
var json = Map.of("error", true, "message", "Sorry but you need to login first in order to vote");
return ResponseEntity.status(200).body(json);
}
Integer allVotesForStar = votes.getOrDefault(nrOfStars, 0);
votes.put(nrOfStars, allVotesForStar + 1);
return ResponseEntity.ok().header("X-Flag", "Thanks for voting, your flag is: " + Flag.FLAGS.get(8)).build();
@GetMapping(value = "/challenge/8/vote/{stars}", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<?> vote(
@PathVariable(value = "stars") int nrOfStars, HttpServletRequest request) {
// Simple implementation of VERB Based Authentication
String msg = "";
if (request.getMethod().equals("GET")) {
var json =
Map.of("error", true, "message", "Sorry but you need to login first in order to vote");
return ResponseEntity.status(200).body(json);
}
Integer allVotesForStar = votes.getOrDefault(nrOfStars, 0);
votes.put(nrOfStars, allVotesForStar + 1);
return ResponseEntity.ok()
.header("X-Flag", "Thanks for voting, your flag is: " + Flag.FLAGS.get(8))
.build();
}
@GetMapping("/challenge/8/votes/")
public ResponseEntity<?> getVotes() {
return ResponseEntity.ok(votes.entrySet().stream().collect(Collectors.toMap(e -> "" + e.getKey(), e -> e.getValue())));
}
@GetMapping("/challenge/8/votes/")
public ResponseEntity<?> getVotes() {
return ResponseEntity.ok(
votes.entrySet().stream()
.collect(Collectors.toMap(e -> "" + e.getKey(), e -> e.getValue())));
}
@GetMapping("/challenge/8/votes/average")
public ResponseEntity<Map<String, Integer>> average() {
int totalNumberOfVotes = votes.values().stream().mapToInt(i -> i.intValue()).sum();
int categories = votes.entrySet().stream().mapToInt(e -> e.getKey() * e.getValue()).reduce(0, (a, b) -> a + b);
var json = Map.of("average", (int) Math.ceil((double) categories / totalNumberOfVotes));
return ResponseEntity.ok(json);
}
@GetMapping("/challenge/8/votes/average")
public ResponseEntity<Map<String, Integer>> average() {
int totalNumberOfVotes = votes.values().stream().mapToInt(i -> i.intValue()).sum();
int categories =
votes.entrySet().stream()
.mapToInt(e -> e.getKey() * e.getValue())
.reduce(0, (a, b) -> a + b);
var json = Map.of("average", (int) Math.ceil((double) categories / totalNumberOfVotes));
return ResponseEntity.ok(json);
}
@GetMapping("/challenge/8/notUsed")
public AttackResult notUsed() {
throw new IllegalStateException("Should never be called, challenge specific method");
}
@GetMapping("/challenge/8/notUsed")
public AttackResult notUsed() {
throw new IllegalStateException("Should never be called, challenge specific method");
}
}

View File

@@ -11,13 +11,13 @@ import org.springframework.stereotype.Component;
@Component
public class Challenge8 extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.CHALLENGE;
}
@Override
public Category getDefaultCategory() {
return Category.CHALLENGE;
}
@Override
public String getTitle() {
return "challenge8.title";
}
@Override
public String getTitle() {
return "challenge8.title";
}
}

View File

@@ -33,13 +33,13 @@ import org.springframework.stereotype.Component;
@Component
public class ChromeDevTools extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.GENERAL;
}
@Override
public Category getDefaultCategory() {
return Category.GENERAL;
}
@Override
public String getTitle() {
return "3.chrome-dev-tools.title";//3rd lesson in General
}
@Override
public String getTitle() {
return "3.chrome-dev-tools.title"; // 3rd lesson in General
}
}

View File

@@ -39,16 +39,16 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
public class NetworkDummy extends AssignmentEndpoint {
@PostMapping("/ChromeDevTools/dummy")
@ResponseBody
public AttackResult completed(@RequestParam String successMessage) {
UserSessionData userSessionData = getUserSessionData();
String answer = (String) userSessionData.getValue("randValue");
@PostMapping("/ChromeDevTools/dummy")
@ResponseBody
public AttackResult completed(@RequestParam String successMessage) {
UserSessionData userSessionData = getUserSessionData();
String answer = (String) userSessionData.getValue("randValue");
if (successMessage != null && successMessage.equals(answer)) {
return success(this).feedback("xss-dom-message-success").build();
} else {
return failed(this).feedback("xss-dom-message-failure").build();
}
if (successMessage != null && successMessage.equals(answer)) {
return success(this).feedback("xss-dom-message-success").build();
} else {
return failed(this).feedback("xss-dom-message-failure").build();
}
}
}

View File

@@ -32,8 +32,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* Assignment where the user has to look through an HTTP Request
* using the Developer Tools and find a specific number.
* Assignment where the user has to look through an HTTP Request using the Developer Tools and find
* a specific number.
*
* @author TMelzer
* @since 30.11.18
@@ -42,19 +42,21 @@ import org.springframework.web.bind.annotation.RestController;
@AssignmentHints({"networkHint1", "networkHint2"})
public class NetworkLesson extends AssignmentEndpoint {
@PostMapping(value = "/ChromeDevTools/network", params = {"network_num", "number"})
@ResponseBody
public AttackResult completed(@RequestParam String network_num, @RequestParam String number) {
if (network_num.equals(number)) {
return success(this).feedback("network.success").output("").build();
} else {
return failed(this).feedback("network.failed").build();
}
@PostMapping(
value = "/ChromeDevTools/network",
params = {"network_num", "number"})
@ResponseBody
public AttackResult completed(@RequestParam String network_num, @RequestParam String number) {
if (network_num.equals(number)) {
return success(this).feedback("network.success").output("").build();
} else {
return failed(this).feedback("network.failed").build();
}
}
@PostMapping(path = "/ChromeDevTools/network", params = "networkNum")
@ResponseBody
public ResponseEntity<?> ok(@RequestParam String networkNum) {
return ResponseEntity.ok().build();
}
@PostMapping(path = "/ChromeDevTools/network", params = "networkNum")
@ResponseBody
public ResponseEntity<?> ok(@RequestParam String networkNum) {
return ResponseEntity.ok().build();
}
}

View File

@@ -11,13 +11,13 @@ import org.springframework.stereotype.Component;
@Component
public class CIA extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.GENERAL;
}
@Override
public Category getDefaultCategory() {
return Category.GENERAL;
}
@Override
public String getTitle() {
return "4.cia.title";//4th lesson in general
}
@Override
public String getTitle() {
return "4.cia.title"; // 4th lesson in general
}
}

View File

@@ -11,38 +11,43 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
public class CIAQuiz extends AssignmentEndpoint {
String[] solutions = {"Solution 3", "Solution 1", "Solution 4", "Solution 2"};
boolean[] guesses = new boolean[solutions.length];
String[] solutions = {"Solution 3", "Solution 1", "Solution 4", "Solution 2"};
boolean[] guesses = new boolean[solutions.length];
@PostMapping("/cia/quiz")
@ResponseBody
public AttackResult completed(@RequestParam String[] question_0_solution, @RequestParam String[] question_1_solution, @RequestParam String[] question_2_solution, @RequestParam String[] question_3_solution) {
int correctAnswers = 0;
@PostMapping("/cia/quiz")
@ResponseBody
public AttackResult completed(
@RequestParam String[] question_0_solution,
@RequestParam String[] question_1_solution,
@RequestParam String[] question_2_solution,
@RequestParam String[] question_3_solution) {
int correctAnswers = 0;
String[] givenAnswers = {question_0_solution[0], question_1_solution[0], question_2_solution[0], question_3_solution[0]};
String[] givenAnswers = {
question_0_solution[0], question_1_solution[0], question_2_solution[0], question_3_solution[0]
};
for (int i = 0; i < solutions.length; i++) {
if (givenAnswers[i].contains(solutions[i])) {
// answer correct
correctAnswers++;
guesses[i] = true;
} else {
// answer incorrect
guesses[i] = false;
}
}
if (correctAnswers == solutions.length) {
return success(this).build();
} else {
return failed(this).build();
}
for (int i = 0; i < solutions.length; i++) {
if (givenAnswers[i].contains(solutions[i])) {
// answer correct
correctAnswers++;
guesses[i] = true;
} else {
// answer incorrect
guesses[i] = false;
}
}
@GetMapping("/cia/quiz")
@ResponseBody
public boolean[] getResults() {
return this.guesses;
if (correctAnswers == solutions.length) {
return success(this).build();
} else {
return failed(this).build();
}
}
@GetMapping("/cia/quiz")
@ResponseBody
public boolean[] getResults() {
return this.guesses;
}
}

View File

@@ -8,25 +8,26 @@ import org.springframework.stereotype.Component;
* ************************************************************************************************
* 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 - 2014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
*
* <p>Copyright (c) 2002 - 2014 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
*
* <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>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* <p>
*
* @author WebGoat
@@ -36,13 +37,13 @@ import org.springframework.stereotype.Component;
@Component
public class ClientSideFiltering extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.CLIENT_SIDE;
}
@Override
public Category getDefaultCategory() {
return Category.CLIENT_SIDE;
}
@Override
public String getTitle() {
return "client.side.filtering.title";
}
@Override
public String getTitle() {
return "client.side.filtering.title";
}
}

View File

@@ -31,14 +31,19 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@AssignmentHints({"ClientSideFilteringHint1", "ClientSideFilteringHint2", "ClientSideFilteringHint3", "ClientSideFilteringHint4"})
@AssignmentHints({
"ClientSideFilteringHint1",
"ClientSideFilteringHint2",
"ClientSideFilteringHint3",
"ClientSideFilteringHint4"
})
public class ClientSideFilteringAssignment extends AssignmentEndpoint {
@PostMapping("/clientSideFiltering/attack1")
@ResponseBody
public AttackResult completed(@RequestParam String answer) {
return "450000".equals(answer)
? success(this).feedback("assignment.solved").build() :
failed(this).feedback("ClientSideFiltering.incorrect").build();
}
@PostMapping("/clientSideFiltering/attack1")
@ResponseBody
public AttackResult completed(@RequestParam String answer) {
return "450000".equals(answer)
? success(this).feedback("assignment.solved").build()
: failed(this).feedback("ClientSideFiltering.incorrect").build();
}
}

View File

@@ -35,17 +35,21 @@ import org.springframework.web.bind.annotation.RestController;
* @since 4/6/17.
*/
@RestController
@AssignmentHints({"client.side.filtering.free.hint1", "client.side.filtering.free.hint2", "client.side.filtering.free.hint3"})
@AssignmentHints({
"client.side.filtering.free.hint1",
"client.side.filtering.free.hint2",
"client.side.filtering.free.hint3"
})
public class ClientSideFilteringFreeAssignment extends AssignmentEndpoint {
public static final String SUPER_COUPON_CODE = "get_it_for_free";
public static final String SUPER_COUPON_CODE = "get_it_for_free";
@PostMapping("/clientSideFiltering/getItForFree")
@ResponseBody
public AttackResult completed(@RequestParam String checkoutCode) {
if (SUPER_COUPON_CODE.equals(checkoutCode)) {
return success(this).build();
}
return failed(this).build();
@PostMapping("/clientSideFiltering/getItForFree")
@ResponseBody
public AttackResult completed(@RequestParam String checkoutCode) {
if (SUPER_COUPON_CODE.equals(checkoutCode)) {
return success(this).build();
}
return failed(this).build();
}
}

View File

@@ -22,6 +22,20 @@
package org.owasp.webgoat.lessons.clientsidefiltering;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
@@ -33,79 +47,66 @@ import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.annotation.PostConstruct;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@Slf4j
public class Salaries {
@Value("${webgoat.user.directory}")
private String webGoatHomeDirectory;
@Value("${webgoat.user.directory}")
private String webGoatHomeDirectory;
@PostConstruct
public void copyFiles() {
ClassPathResource classPathResource = new ClassPathResource("lessons/employees.xml");
File targetDirectory = new File(webGoatHomeDirectory, "/ClientSideFiltering");
if (!targetDirectory.exists()) {
targetDirectory.mkdir();
}
try {
FileCopyUtils.copy(classPathResource.getInputStream(), new FileOutputStream(new File(targetDirectory, "employees.xml")));
} catch (IOException e) {
throw new RuntimeException(e);
}
@PostConstruct
public void copyFiles() {
ClassPathResource classPathResource = new ClassPathResource("lessons/employees.xml");
File targetDirectory = new File(webGoatHomeDirectory, "/ClientSideFiltering");
if (!targetDirectory.exists()) {
targetDirectory.mkdir();
}
@GetMapping("clientSideFiltering/salaries")
@ResponseBody
public List<Map<String, Object>> invoke() {
NodeList nodes = null;
File d = new File(webGoatHomeDirectory, "ClientSideFiltering/employees.xml");
XPathFactory factory = XPathFactory.newInstance();
XPath path = factory.newXPath();
int columns = 5;
List<Map<String, Object>> json = new ArrayList<>();
java.util.Map<String, Object> employeeJson = new HashMap<>();
try (InputStream is = new FileInputStream(d)) {
InputSource inputSource = new InputSource(is);
StringBuilder sb = new StringBuilder();
sb.append("/Employees/Employee/UserID | ");
sb.append("/Employees/Employee/FirstName | ");
sb.append("/Employees/Employee/LastName | ");
sb.append("/Employees/Employee/SSN | ");
sb.append("/Employees/Employee/Salary ");
String expression = sb.toString();
nodes = (NodeList) path.evaluate(expression, inputSource, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
if (i % columns == 0) {
employeeJson = new HashMap<>();
json.add(employeeJson);
}
Node node = nodes.item(i);
employeeJson.put(node.getNodeName(), node.getTextContent());
}
} catch (XPathExpressionException e) {
log.error("Unable to parse xml", e);
} catch (IOException e) {
log.error("Unable to read employees.xml at location: '{}'", d);
}
return json;
try {
FileCopyUtils.copy(
classPathResource.getInputStream(),
new FileOutputStream(new File(targetDirectory, "employees.xml")));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@GetMapping("clientSideFiltering/salaries")
@ResponseBody
public List<Map<String, Object>> invoke() {
NodeList nodes = null;
File d = new File(webGoatHomeDirectory, "ClientSideFiltering/employees.xml");
XPathFactory factory = XPathFactory.newInstance();
XPath path = factory.newXPath();
int columns = 5;
List<Map<String, Object>> json = new ArrayList<>();
java.util.Map<String, Object> employeeJson = new HashMap<>();
try (InputStream is = new FileInputStream(d)) {
InputSource inputSource = new InputSource(is);
StringBuilder sb = new StringBuilder();
sb.append("/Employees/Employee/UserID | ");
sb.append("/Employees/Employee/FirstName | ");
sb.append("/Employees/Employee/LastName | ");
sb.append("/Employees/Employee/SSN | ");
sb.append("/Employees/Employee/Salary ");
String expression = sb.toString();
nodes = (NodeList) path.evaluate(expression, inputSource, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
if (i % columns == 0) {
employeeJson = new HashMap<>();
json.add(employeeJson);
}
Node node = nodes.item(i);
employeeJson.put(node.getNodeName(), node.getTextContent());
}
} catch (XPathExpressionException e) {
log.error("Unable to parse xml", e);
} catch (IOException e) {
log.error("Unable to read employees.xml at location: '{}'", d);
}
return json;
}
}

View File

@@ -23,18 +23,16 @@
package org.owasp.webgoat.lessons.clientsidefiltering;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.Optional;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Optional;
/**
* @author nbaars
* @since 4/6/17.
@@ -43,47 +41,46 @@ import java.util.Optional;
@RequestMapping("/clientSideFiltering/challenge-store")
public class ShopEndpoint {
@AllArgsConstructor
private class CheckoutCodes {
@AllArgsConstructor
private class CheckoutCodes {
@Getter
private List<CheckoutCode> codes;
@Getter private List<CheckoutCode> codes;
public Optional<CheckoutCode> get(String code) {
return codes.stream().filter(c -> c.getCode().equals(code)).findFirst();
}
public Optional<CheckoutCode> get(String code) {
return codes.stream().filter(c -> c.getCode().equals(code)).findFirst();
}
}
@AllArgsConstructor
@Getter
private class CheckoutCode {
private String code;
private int discount;
@AllArgsConstructor
@Getter
private class CheckoutCode {
private String code;
private int discount;
}
private CheckoutCodes checkoutCodes;
public ShopEndpoint() {
List<CheckoutCode> codes = Lists.newArrayList();
codes.add(new CheckoutCode("webgoat", 25));
codes.add(new CheckoutCode("owasp", 25));
codes.add(new CheckoutCode("owasp-webgoat", 50));
this.checkoutCodes = new CheckoutCodes(codes);
}
@GetMapping(value = "/coupons/{code}", produces = MediaType.APPLICATION_JSON_VALUE)
public CheckoutCode getDiscountCode(@PathVariable String code) {
if (ClientSideFilteringFreeAssignment.SUPER_COUPON_CODE.equals(code)) {
return new CheckoutCode(ClientSideFilteringFreeAssignment.SUPER_COUPON_CODE, 100);
}
return checkoutCodes.get(code).orElse(new CheckoutCode("no", 0));
}
private CheckoutCodes checkoutCodes;
public ShopEndpoint() {
List<CheckoutCode> codes = Lists.newArrayList();
codes.add(new CheckoutCode("webgoat", 25));
codes.add(new CheckoutCode("owasp", 25));
codes.add(new CheckoutCode("owasp-webgoat", 50));
this.checkoutCodes = new CheckoutCodes(codes);
}
@GetMapping(value = "/coupons/{code}", produces = MediaType.APPLICATION_JSON_VALUE)
public CheckoutCode getDiscountCode(@PathVariable String code) {
if (ClientSideFilteringFreeAssignment.SUPER_COUPON_CODE.equals(code)) {
return new CheckoutCode(ClientSideFilteringFreeAssignment.SUPER_COUPON_CODE, 100);
}
return checkoutCodes.get(code).orElse(new CheckoutCode("no", 0));
}
@GetMapping(value = "/coupons", produces = MediaType.APPLICATION_JSON_VALUE)
public CheckoutCodes all() {
List<CheckoutCode> all = Lists.newArrayList();
all.addAll(this.checkoutCodes.getCodes());
all.add(new CheckoutCode(ClientSideFilteringFreeAssignment.SUPER_COUPON_CODE, 100));
return new CheckoutCodes(all);
}
@GetMapping(value = "/coupons", produces = MediaType.APPLICATION_JSON_VALUE)
public CheckoutCodes all() {
List<CheckoutCode> all = Lists.newArrayList();
all.addAll(this.checkoutCodes.getCodes());
all.add(new CheckoutCode(ClientSideFilteringFreeAssignment.SUPER_COUPON_CODE, 100));
return new CheckoutCodes(all);
}
}

View File

@@ -1,8 +1,5 @@
package org.owasp.webgoat.lessons.cryptography;
import lombok.extern.slf4j.Slf4j;
import javax.xml.bind.DatatypeConverter;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
@@ -19,117 +16,128 @@ import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAKeyGenParameterSpec;
import java.util.Base64;
import javax.xml.bind.DatatypeConverter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class CryptoUtil {
private static final BigInteger[] FERMAT_PRIMES =
{ BigInteger.valueOf(3),
BigInteger.valueOf(5),
BigInteger.valueOf(17),
BigInteger.valueOf(257),
BigInteger.valueOf(65537) };
private static final BigInteger[] FERMAT_PRIMES = {
BigInteger.valueOf(3),
BigInteger.valueOf(5),
BigInteger.valueOf(17),
BigInteger.valueOf(257),
BigInteger.valueOf(65537)
};
public static KeyPair generateKeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
RSAKeyGenParameterSpec kpgSpec = new RSAKeyGenParameterSpec(2048, FERMAT_PRIMES[new SecureRandom().nextInt(FERMAT_PRIMES.length)]);
keyPairGenerator.initialize(kpgSpec);
//keyPairGenerator.initialize(2048);
return keyPairGenerator.generateKeyPair();
public static KeyPair generateKeyPair()
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
RSAKeyGenParameterSpec kpgSpec =
new RSAKeyGenParameterSpec(
2048, FERMAT_PRIMES[new SecureRandom().nextInt(FERMAT_PRIMES.length)]);
keyPairGenerator.initialize(kpgSpec);
// keyPairGenerator.initialize(2048);
return keyPairGenerator.generateKeyPair();
}
public static String getPrivateKeyInPEM(KeyPair keyPair) {
String encodedString = "-----BEGIN PRIVATE KEY-----\n";
encodedString =
encodedString
+ new String(
Base64.getEncoder().encode(keyPair.getPrivate().getEncoded()),
Charset.forName("UTF-8"))
+ "\n";
encodedString = encodedString + "-----END PRIVATE KEY-----\n";
return encodedString;
}
public static String signMessage(String message, PrivateKey privateKey) {
log.debug("start signMessage");
String signature = null;
try {
// Initiate signature verification
Signature instance = Signature.getInstance("SHA256withRSA");
instance.initSign(privateKey);
instance.update(message.getBytes("UTF-8"));
// actual verification against signature
signature = new String(Base64.getEncoder().encode(instance.sign()), Charset.forName("UTF-8"));
log.info("signe the signature with result: {}", signature);
} catch (Exception e) {
log.error("Signature signing failed", e);
}
public static String getPrivateKeyInPEM(KeyPair keyPair) {
String encodedString = "-----BEGIN PRIVATE KEY-----\n";
encodedString = encodedString+new String(Base64.getEncoder().encode(keyPair.getPrivate().getEncoded()),Charset.forName("UTF-8"))+"\n";
encodedString = encodedString+"-----END PRIVATE KEY-----\n";
return encodedString;
log.debug("end signMessage");
return signature;
}
public static boolean verifyMessage(
String message, String base64EncSignature, PublicKey publicKey) {
log.debug("start verifyMessage");
boolean result = false;
try {
base64EncSignature = base64EncSignature.replace("\r", "").replace("\n", "").replace(" ", "");
// get raw signature from base64 encrypted string in header
byte[] decodedSignature = Base64.getDecoder().decode(base64EncSignature);
// Initiate signature verification
Signature instance = Signature.getInstance("SHA256withRSA");
instance.initVerify(publicKey);
instance.update(message.getBytes("UTF-8"));
// actual verification against signature
result = instance.verify(decodedSignature);
log.info("Verified the signature with result: {}", result);
} catch (Exception e) {
log.error("Signature verification failed", e);
}
public static String signMessage(String message, PrivateKey privateKey) {
log.debug("end verifyMessage");
return result;
}
log.debug("start signMessage");
String signature = null;
try {
//Initiate signature verification
Signature instance = Signature.getInstance("SHA256withRSA");
instance.initSign(privateKey);
instance.update(message.getBytes("UTF-8"));
//actual verification against signature
signature = new String(Base64.getEncoder().encode(instance.sign()), Charset.forName("UTF-8"));
public static boolean verifyAssignment(String modulus, String signature, PublicKey publicKey) {
log.info("signe the signature with result: {}", signature);
} catch (Exception e) {
log.error("Signature signing failed", e);
}
/* first check if the signature is correct, i.e. right private key and right hash */
boolean result = false;
log.debug("end signMessage");
return signature;
}
public static boolean verifyMessage(String message, String base64EncSignature,
PublicKey publicKey) {
if (modulus != null && signature != null) {
result = verifyMessage(modulus, signature, publicKey);
log.debug("start verifyMessage");
boolean result = false;
try {
base64EncSignature = base64EncSignature.replace("\r", "").replace("\n", "")
.replace(" ", "");
//get raw signature from base64 encrypted string in header
byte[] decodedSignature = Base64.getDecoder().decode(base64EncSignature);
//Initiate signature verification
Signature instance = Signature.getInstance("SHA256withRSA");
instance.initVerify(publicKey);
instance.update(message.getBytes("UTF-8"));
//actual verification against signature
result = instance.verify(decodedSignature);
/*
* next check if the submitted modulus is the correct modulus of the public key
*/
RSAPublicKey rsaPubKey = (RSAPublicKey) publicKey;
if (modulus.length() == 512) {
modulus = "00".concat(modulus);
}
result =
result
&& (DatatypeConverter.printHexBinary(rsaPubKey.getModulus().toByteArray())
.equals(modulus.toUpperCase()));
}
return result;
}
log.info("Verified the signature with result: {}", result);
} catch (Exception e) {
log.error("Signature verification failed", e);
}
public static PrivateKey getPrivateKeyFromPEM(String privateKeyPem)
throws NoSuchAlgorithmException, InvalidKeySpecException {
privateKeyPem = privateKeyPem.replace("-----BEGIN PRIVATE KEY-----", "");
privateKeyPem = privateKeyPem.replace("-----END PRIVATE KEY-----", "");
privateKeyPem = privateKeyPem.replace("\n", "").replace("\r", "");
log.debug("end verifyMessage");
return result;
}
public static boolean verifyAssignment(String modulus, String signature, PublicKey publicKey) {
/* first check if the signature is correct, i.e. right private key and right hash */
boolean result = false;
if (modulus != null && signature != null) {
result = verifyMessage(modulus, signature, publicKey);
/*
* next check if the submitted modulus is the correct modulus of the public key
*/
RSAPublicKey rsaPubKey = (RSAPublicKey) publicKey;
if (modulus.length()==512) {
modulus = "00".concat(modulus);
}
result = result && (DatatypeConverter.printHexBinary(rsaPubKey.getModulus().toByteArray()).equals(modulus.toUpperCase()));
}
return result;
}
public static PrivateKey getPrivateKeyFromPEM(String privateKeyPem) throws NoSuchAlgorithmException, InvalidKeySpecException {
privateKeyPem = privateKeyPem.replace("-----BEGIN PRIVATE KEY-----", "");
privateKeyPem = privateKeyPem.replace("-----END PRIVATE KEY-----", "");
privateKeyPem = privateKeyPem.replace("\n", "").replace("\r", "");
byte [] decoded = Base64.getDecoder().decode(privateKeyPem);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}
byte[] decoded = Base64.getDecoder().decode(privateKeyPem);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}
}

View File

@@ -28,14 +28,13 @@ import org.springframework.stereotype.Component;
@Component
public class Cryptography extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.A2;
}
@Override
public String getTitle() {
return "6.crypto.title";//first lesson in general
}
@Override
public Category getDefaultCategory() {
return Category.A2;
}
@Override
public String getTitle() {
return "6.crypto.title"; // first lesson in general
}
}

View File

@@ -22,6 +22,9 @@
package org.owasp.webgoat.lessons.cryptography;
import java.util.Base64;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.springframework.http.MediaType;
@@ -31,43 +34,42 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.Base64;
import java.util.Random;
@RestController
public class EncodingAssignment extends AssignmentEndpoint {
public static String getBasicAuth(String username, String password) {
return Base64.getEncoder().encodeToString(username.concat(":").concat(password).getBytes());
public static String getBasicAuth(String username, String password) {
return Base64.getEncoder().encodeToString(username.concat(":").concat(password).getBytes());
}
@GetMapping(path = "/crypto/encoding/basic", produces = MediaType.TEXT_HTML_VALUE)
@ResponseBody
public String getBasicAuth(HttpServletRequest request) {
String basicAuth = (String) request.getSession().getAttribute("basicAuth");
String username = request.getUserPrincipal().getName();
if (basicAuth == null) {
String password =
HashingAssignment.SECRETS[new Random().nextInt(HashingAssignment.SECRETS.length)];
basicAuth = getBasicAuth(username, password);
request.getSession().setAttribute("basicAuth", basicAuth);
}
@GetMapping(path="/crypto/encoding/basic",produces=MediaType.TEXT_HTML_VALUE)
@ResponseBody
public String getBasicAuth(HttpServletRequest request) {
String basicAuth = (String) request.getSession().getAttribute("basicAuth");
String username = request.getUserPrincipal().getName();
if (basicAuth == null) {
String password = HashingAssignment.SECRETS[new Random().nextInt(HashingAssignment.SECRETS.length)];
basicAuth = getBasicAuth(username, password);
request.getSession().setAttribute("basicAuth", basicAuth);
}
return "Authorization: Basic ".concat(basicAuth);
}
@PostMapping("/crypto/encoding/basic-auth")
@ResponseBody
public AttackResult completed(HttpServletRequest request, @RequestParam String answer_user, @RequestParam String answer_pwd) {
String basicAuth = (String) request.getSession().getAttribute("basicAuth");
if (basicAuth !=null && answer_user!=null && answer_pwd !=null
&& basicAuth.equals(getBasicAuth(answer_user,answer_pwd)))
{
return success(this)
.feedback("crypto-encoding.success")
.build();
} else {
return failed(this).feedback("crypto-encoding.empty").build();
}
return "Authorization: Basic ".concat(basicAuth);
}
@PostMapping("/crypto/encoding/basic-auth")
@ResponseBody
public AttackResult completed(
HttpServletRequest request,
@RequestParam String answer_user,
@RequestParam String answer_pwd) {
String basicAuth = (String) request.getSession().getAttribute("basicAuth");
if (basicAuth != null
&& answer_user != null
&& answer_pwd != null
&& basicAuth.equals(getBasicAuth(answer_user, answer_pwd))) {
return success(this).feedback("crypto-encoding.success").build();
} else {
return failed(this).feedback("crypto-encoding.empty").build();
}
}
}

View File

@@ -22,6 +22,11 @@
package org.owasp.webgoat.lessons.cryptography;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
@@ -32,79 +37,69 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
@RestController
@AssignmentHints({"crypto-hashing.hints.1","crypto-hashing.hints.2"})
@AssignmentHints({"crypto-hashing.hints.1", "crypto-hashing.hints.2"})
public class HashingAssignment extends AssignmentEndpoint {
public static final String[] SECRETS = {"secret","admin","password", "123456", "passw0rd"};
@RequestMapping(path="/crypto/hashing/md5",produces=MediaType.TEXT_HTML_VALUE)
@ResponseBody
public String getMd5(HttpServletRequest request) throws NoSuchAlgorithmException {
String md5Hash = (String) request.getSession().getAttribute("md5Hash");
if (md5Hash == null) {
String secret = SECRETS[new Random().nextInt(SECRETS.length)];
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(secret.getBytes());
byte[] digest = md.digest();
md5Hash = DatatypeConverter
.printHexBinary(digest).toUpperCase();
request.getSession().setAttribute("md5Hash", md5Hash);
request.getSession().setAttribute("md5Secret", secret);
}
return md5Hash;
public static final String[] SECRETS = {"secret", "admin", "password", "123456", "passw0rd"};
@RequestMapping(path = "/crypto/hashing/md5", produces = MediaType.TEXT_HTML_VALUE)
@ResponseBody
public String getMd5(HttpServletRequest request) throws NoSuchAlgorithmException {
String md5Hash = (String) request.getSession().getAttribute("md5Hash");
if (md5Hash == null) {
String secret = SECRETS[new Random().nextInt(SECRETS.length)];
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(secret.getBytes());
byte[] digest = md.digest();
md5Hash = DatatypeConverter.printHexBinary(digest).toUpperCase();
request.getSession().setAttribute("md5Hash", md5Hash);
request.getSession().setAttribute("md5Secret", secret);
}
@RequestMapping(path="/crypto/hashing/sha256",produces=MediaType.TEXT_HTML_VALUE)
@ResponseBody
public String getSha256(HttpServletRequest request) throws NoSuchAlgorithmException {
String sha256 = (String) request.getSession().getAttribute("sha256");
if (sha256 == null) {
String secret = SECRETS[new Random().nextInt(SECRETS.length)];
sha256 = getHash(secret, "SHA-256");
request.getSession().setAttribute("sha256Hash", sha256);
request.getSession().setAttribute("sha256Secret", secret);
}
return sha256;
return md5Hash;
}
@RequestMapping(path = "/crypto/hashing/sha256", produces = MediaType.TEXT_HTML_VALUE)
@ResponseBody
public String getSha256(HttpServletRequest request) throws NoSuchAlgorithmException {
String sha256 = (String) request.getSession().getAttribute("sha256");
if (sha256 == null) {
String secret = SECRETS[new Random().nextInt(SECRETS.length)];
sha256 = getHash(secret, "SHA-256");
request.getSession().setAttribute("sha256Hash", sha256);
request.getSession().setAttribute("sha256Secret", secret);
}
@PostMapping("/crypto/hashing")
@ResponseBody
public AttackResult completed(HttpServletRequest request, @RequestParam String answer_pwd1, @RequestParam String answer_pwd2) {
String md5Secret = (String) request.getSession().getAttribute("md5Secret");
String sha256Secret = (String) request.getSession().getAttribute("sha256Secret");
if (answer_pwd1!=null && answer_pwd2 !=null) {
if (answer_pwd1.equals(md5Secret)
&& answer_pwd2.equals(sha256Secret)) {
return success(this)
.feedback("crypto-hashing.success")
.build();
} else if (answer_pwd1.equals(md5Secret)
|| answer_pwd2.equals(sha256Secret)) {
return failed(this).feedback("crypto-hashing.oneok").build();
}
}
return failed(this).feedback("crypto-hashing.empty").build();
return sha256;
}
@PostMapping("/crypto/hashing")
@ResponseBody
public AttackResult completed(
HttpServletRequest request,
@RequestParam String answer_pwd1,
@RequestParam String answer_pwd2) {
String md5Secret = (String) request.getSession().getAttribute("md5Secret");
String sha256Secret = (String) request.getSession().getAttribute("sha256Secret");
if (answer_pwd1 != null && answer_pwd2 != null) {
if (answer_pwd1.equals(md5Secret) && answer_pwd2.equals(sha256Secret)) {
return success(this).feedback("crypto-hashing.success").build();
} else if (answer_pwd1.equals(md5Secret) || answer_pwd2.equals(sha256Secret)) {
return failed(this).feedback("crypto-hashing.oneok").build();
}
}
public static String getHash(String secret, String algorithm) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance(algorithm);
md.update(secret.getBytes());
byte[] digest = md.digest();
return DatatypeConverter
.printHexBinary(digest).toUpperCase();
}
return failed(this).feedback("crypto-hashing.empty").build();
}
public static String getHash(String secret, String algorithm) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance(algorithm);
md.update(secret.getBytes());
byte[] digest = md.digest();
return DatatypeConverter.printHexBinary(digest).toUpperCase();
}
}

View File

@@ -22,6 +22,7 @@
package org.owasp.webgoat.lessons.cryptography;
import java.security.NoSuchAlgorithmException;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
@@ -30,24 +31,29 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.security.NoSuchAlgorithmException;
@RestController
@AssignmentHints({"crypto-secure-defaults.hints.1", "crypto-secure-defaults.hints.2", "crypto-secure-defaults.hints.3"})
@AssignmentHints({
"crypto-secure-defaults.hints.1",
"crypto-secure-defaults.hints.2",
"crypto-secure-defaults.hints.3"
})
public class SecureDefaultsAssignment extends AssignmentEndpoint {
@PostMapping("/crypto/secure/defaults")
@ResponseBody
public AttackResult completed(@RequestParam String secretFileName, @RequestParam String secretText) throws NoSuchAlgorithmException {
if (secretFileName!=null && secretFileName.equals("default_secret")) {
if (secretText!=null && HashingAssignment.getHash(secretText, "SHA-256").equalsIgnoreCase("34de66e5caf2cb69ff2bebdc1f3091ecf6296852446c718e38ebfa60e4aa75d2")) {
return success(this)
.feedback("crypto-secure-defaults.success")
.build();
} else {
return failed(this).feedback("crypto-secure-defaults.messagenotok").build();
}
}
return failed(this).feedback("crypto-secure-defaults.notok").build();
@PostMapping("/crypto/secure/defaults")
@ResponseBody
public AttackResult completed(
@RequestParam String secretFileName, @RequestParam String secretText)
throws NoSuchAlgorithmException {
if (secretFileName != null && secretFileName.equals("default_secret")) {
if (secretText != null
&& HashingAssignment.getHash(secretText, "SHA-256")
.equalsIgnoreCase(
"34de66e5caf2cb69ff2bebdc1f3091ecf6296852446c718e38ebfa60e4aa75d2")) {
return success(this).feedback("crypto-secure-defaults.success").build();
} else {
return failed(this).feedback("crypto-secure-defaults.messagenotok").build();
}
}
return failed(this).feedback("crypto-secure-defaults.notok").build();
}
}

View File

@@ -22,6 +22,12 @@
package org.owasp.webgoat.lessons.cryptography;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
@@ -33,54 +39,54 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
@RestController
@AssignmentHints({"crypto-signing.hints.1","crypto-signing.hints.2", "crypto-signing.hints.3", "crypto-signing.hints.4"})
@AssignmentHints({
"crypto-signing.hints.1",
"crypto-signing.hints.2",
"crypto-signing.hints.3",
"crypto-signing.hints.4"
})
@Slf4j
public class SigningAssignment extends AssignmentEndpoint {
@RequestMapping(path="/crypto/signing/getprivate",produces=MediaType.TEXT_HTML_VALUE)
@ResponseBody
public String getPrivateKey(HttpServletRequest request) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
String privateKey = (String) request.getSession().getAttribute("privateKeyString");
if (privateKey == null) {
KeyPair keyPair = CryptoUtil.generateKeyPair();
privateKey = CryptoUtil.getPrivateKeyInPEM(keyPair);
request.getSession().setAttribute("privateKeyString", privateKey);
request.getSession().setAttribute("keyPair", keyPair);
}
return privateKey;
@RequestMapping(path = "/crypto/signing/getprivate", produces = MediaType.TEXT_HTML_VALUE)
@ResponseBody
public String getPrivateKey(HttpServletRequest request)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
String privateKey = (String) request.getSession().getAttribute("privateKeyString");
if (privateKey == null) {
KeyPair keyPair = CryptoUtil.generateKeyPair();
privateKey = CryptoUtil.getPrivateKeyInPEM(keyPair);
request.getSession().setAttribute("privateKeyString", privateKey);
request.getSession().setAttribute("keyPair", keyPair);
}
@PostMapping("/crypto/signing/verify")
@ResponseBody
public AttackResult completed(HttpServletRequest request, @RequestParam String modulus, @RequestParam String signature) {
String tempModulus = modulus;/* used to validate the modulus of the public key but might need to be corrected */
KeyPair keyPair = (KeyPair) request.getSession().getAttribute("keyPair");
RSAPublicKey rsaPubKey = (RSAPublicKey) keyPair.getPublic();
if (tempModulus.length() == 512) {
tempModulus = "00".concat(tempModulus);
}
if (!DatatypeConverter.printHexBinary(rsaPubKey.getModulus().toByteArray()).equals(tempModulus.toUpperCase())) {
log.warn("modulus {} incorrect", modulus);
return failed(this).feedback("crypto-signing.modulusnotok").build();
}
/* orginal modulus must be used otherwise the signature would be invalid */
if (CryptoUtil.verifyMessage(modulus, signature, keyPair.getPublic())) {
return success(this).feedback("crypto-signing.success").build();
} else {
log.warn("signature incorrect");
return failed(this).feedback("crypto-signing.notok").build();
}
return privateKey;
}
@PostMapping("/crypto/signing/verify")
@ResponseBody
public AttackResult completed(
HttpServletRequest request, @RequestParam String modulus, @RequestParam String signature) {
String tempModulus =
modulus; /* used to validate the modulus of the public key but might need to be corrected */
KeyPair keyPair = (KeyPair) request.getSession().getAttribute("keyPair");
RSAPublicKey rsaPubKey = (RSAPublicKey) keyPair.getPublic();
if (tempModulus.length() == 512) {
tempModulus = "00".concat(tempModulus);
}
if (!DatatypeConverter.printHexBinary(rsaPubKey.getModulus().toByteArray())
.equals(tempModulus.toUpperCase())) {
log.warn("modulus {} incorrect", modulus);
return failed(this).feedback("crypto-signing.modulusnotok").build();
}
/* orginal modulus must be used otherwise the signature would be invalid */
if (CryptoUtil.verifyMessage(modulus, signature, keyPair.getPublic())) {
return success(this).feedback("crypto-signing.success").build();
} else {
log.warn("signature incorrect");
return failed(this).feedback("crypto-signing.notok").build();
}
}
}

View File

@@ -34,14 +34,12 @@ import org.springframework.web.bind.annotation.RestController;
@AssignmentHints({"crypto-encoding-xor.hints.1"})
public class XOREncodingAssignment extends AssignmentEndpoint {
@PostMapping("/crypto/encoding/xor")
@ResponseBody
public AttackResult completed(@RequestParam String answer_pwd1) {
if (answer_pwd1!=null && answer_pwd1.equals("databasepassword")) {
return success(this)
.feedback("crypto-encoding-xor.success")
.build();
}
return failed(this).feedback("crypto-encoding-xor.empty").build();
@PostMapping("/crypto/encoding/xor")
@ResponseBody
public AttackResult completed(@RequestParam String answer_pwd1) {
if (answer_pwd1 != null && answer_pwd1.equals("databasepassword")) {
return success(this).feedback("crypto-encoding-xor.success").build();
}
return failed(this).feedback("crypto-encoding-xor.empty").build();
}
}

View File

@@ -26,16 +26,16 @@ import org.owasp.webgoat.container.lessons.Category;
import org.owasp.webgoat.container.lessons.Lesson;
import org.springframework.stereotype.Component;
/**
* Created by jason on 9/29/17.
*/
/** Created by jason on 9/29/17. */
@Component
public class CSRF extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.A10;
}
@Override
public Category getDefaultCategory() {
return Category.A10;
}
@Override
public String getTitle() { return "csrf.title"; }
@Override
public String getTitle() {
return "csrf.title";
}
}

View File

@@ -31,28 +31,26 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by jason on 9/29/17.
*/
/** Created by jason on 9/29/17. */
@RestController
@AssignmentHints({"csrf-get.hint1", "csrf-get.hint2", "csrf-get.hint3", "csrf-get.hint4"})
public class CSRFConfirmFlag1 extends AssignmentEndpoint {
@Autowired
UserSessionData userSessionData;
@Autowired UserSessionData userSessionData;
@PostMapping(path = "/csrf/confirm-flag-1", produces = {"application/json"})
@ResponseBody
public AttackResult completed(String confirmFlagVal) {
Object userSessionDataStr = userSessionData.getValue("csrf-get-success");
if (userSessionDataStr != null && confirmFlagVal.equals(userSessionDataStr.toString())) {
return success(this)
.feedback("csrf-get-null-referer.success")
.output("Correct, the flag was " + userSessionData.getValue("csrf-get-success"))
.build();
}
return failed(this).build();
@PostMapping(
path = "/csrf/confirm-flag-1",
produces = {"application/json"})
@ResponseBody
public AttackResult completed(String confirmFlagVal) {
Object userSessionDataStr = userSessionData.getValue("csrf-get-success");
if (userSessionDataStr != null && confirmFlagVal.equals(userSessionDataStr.toString())) {
return success(this)
.feedback("csrf-get-null-referer.success")
.output("Correct, the flag was " + userSessionData.getValue("csrf-get-success"))
.build();
}
return failed(this).build();
}
}

View File

@@ -24,6 +24,11 @@ package org.owasp.webgoat.lessons.csrf;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
@@ -37,12 +42,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;
/**
* @author nbaars
* @since 11/17/17.
@@ -51,70 +50,72 @@ import java.util.UUID;
@AssignmentHints({"csrf-feedback-hint1", "csrf-feedback-hint2", "csrf-feedback-hint3"})
public class CSRFFeedback extends AssignmentEndpoint {
@Autowired
private UserSessionData userSessionData;
@Autowired
private ObjectMapper objectMapper;
@Autowired private UserSessionData userSessionData;
@Autowired private ObjectMapper objectMapper;
@PostMapping(value = "/csrf/feedback/message", produces = {"application/json"})
@ResponseBody
public AttackResult completed(HttpServletRequest request, @RequestBody String feedback) {
try {
objectMapper.enable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
objectMapper.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
objectMapper.enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS);
objectMapper.enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY);
objectMapper.enable(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES);
objectMapper.enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
objectMapper.readValue(feedback.getBytes(), Map.class);
} catch (IOException e) {
return failed(this).feedback(ExceptionUtils.getStackTrace(e)).build();
}
boolean correctCSRF = requestContainsWebGoatCookie(request.getCookies()) && request.getContentType().contains(MediaType.TEXT_PLAIN_VALUE);
correctCSRF &= hostOrRefererDifferentHost(request);
if (correctCSRF) {
String flag = UUID.randomUUID().toString();
userSessionData.setValue("csrf-feedback", flag);
return success(this).feedback("csrf-feedback-success").feedbackArgs(flag).build();
}
return failed(this).build();
@PostMapping(
value = "/csrf/feedback/message",
produces = {"application/json"})
@ResponseBody
public AttackResult completed(HttpServletRequest request, @RequestBody String feedback) {
try {
objectMapper.enable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
objectMapper.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
objectMapper.enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS);
objectMapper.enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY);
objectMapper.enable(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES);
objectMapper.enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
objectMapper.readValue(feedback.getBytes(), Map.class);
} catch (IOException e) {
return failed(this).feedback(ExceptionUtils.getStackTrace(e)).build();
}
@PostMapping(path = "/csrf/feedback", produces = "application/json")
@ResponseBody
public AttackResult flag(@RequestParam("confirmFlagVal") String flag) {
if (flag.equals(userSessionData.getValue("csrf-feedback"))) {
return success(this).build();
} else {
return failed(this).build();
}
boolean correctCSRF =
requestContainsWebGoatCookie(request.getCookies())
&& request.getContentType().contains(MediaType.TEXT_PLAIN_VALUE);
correctCSRF &= hostOrRefererDifferentHost(request);
if (correctCSRF) {
String flag = UUID.randomUUID().toString();
userSessionData.setValue("csrf-feedback", flag);
return success(this).feedback("csrf-feedback-success").feedbackArgs(flag).build();
}
return failed(this).build();
}
private boolean hostOrRefererDifferentHost(HttpServletRequest request) {
String referer = request.getHeader("Referer");
String host = request.getHeader("Host");
if (referer != null) {
return !referer.contains(host);
} else {
return true;
}
@PostMapping(path = "/csrf/feedback", produces = "application/json")
@ResponseBody
public AttackResult flag(@RequestParam("confirmFlagVal") String flag) {
if (flag.equals(userSessionData.getValue("csrf-feedback"))) {
return success(this).build();
} else {
return failed(this).build();
}
}
private boolean requestContainsWebGoatCookie(Cookie[] cookies) {
if (cookies != null) {
for (Cookie c : cookies) {
if (c.getName().equals("JSESSIONID")) {
return true;
}
}
}
return false;
private boolean hostOrRefererDifferentHost(HttpServletRequest request) {
String referer = request.getHeader("Referer");
String host = request.getHeader("Host");
if (referer != null) {
return !referer.contains(host);
} else {
return true;
}
}
/** Solution
<form name="attack" enctype="text/plain" action="http://localhost:8080/WebGoat/csrf/feedback/message" METHOD="POST">
<input type="hidden" name='{"name": "Test", "email": "test1233@dfssdf.de", "subject": "service", "message":"dsaffd"}'>
</form>
<script>document.attack.submit();</script>
*/
private boolean requestContainsWebGoatCookie(Cookie[] cookies) {
if (cookies != null) {
for (Cookie c : cookies) {
if (c.getName().equals("JSESSIONID")) {
return true;
}
}
}
return false;
}
/**
* Solution <form name="attack" enctype="text/plain"
* action="http://localhost:8080/WebGoat/csrf/feedback/message" METHOD="POST"> <input
* type="hidden" name='{"name": "Test", "email": "test1233@dfssdf.de", "subject": "service",
* "message":"dsaffd"}'> </form> <script>document.attack.submit();</script>
*/
}

View File

@@ -22,6 +22,10 @@
package org.owasp.webgoat.lessons.csrf;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import org.owasp.webgoat.container.i18n.PluginMessages;
import org.owasp.webgoat.container.session.UserSessionData;
import org.springframework.beans.factory.annotation.Autowired;
@@ -30,60 +34,52 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
/**
* Created by jason on 9/30/17.
*/
/** Created by jason on 9/30/17. */
@RestController
public class CSRFGetFlag {
@Autowired
UserSessionData userSessionData;
@Autowired
private PluginMessages pluginMessages;
@Autowired UserSessionData userSessionData;
@Autowired private PluginMessages pluginMessages;
@RequestMapping(path = "/csrf/basic-get-flag", produces = {"application/json"}, method = RequestMethod.POST)
@ResponseBody
public Map<String, Object> invoke(HttpServletRequest req) {
@RequestMapping(
path = "/csrf/basic-get-flag",
produces = {"application/json"},
method = RequestMethod.POST)
@ResponseBody
public Map<String, Object> invoke(HttpServletRequest req) {
Map<String, Object> response = new HashMap<>();
Map<String, Object> response = new HashMap<>();
String host = (req.getHeader("host") == null) ? "NULL" : req.getHeader("host");
String referer = (req.getHeader("referer") == null) ? "NULL" : req.getHeader("referer");
String[] refererArr = referer.split("/");
if (referer.equals("NULL")) {
if ("true".equals(req.getParameter("csrf"))) {
Random random = new Random();
userSessionData.setValue("csrf-get-success", random.nextInt(65536));
response.put("success", true);
response.put("message", pluginMessages.getMessage("csrf-get-null-referer.success"));
response.put("flag", userSessionData.getValue("csrf-get-success"));
} else {
Random random = new Random();
userSessionData.setValue("csrf-get-success", random.nextInt(65536));
response.put("success", true);
response.put("message", pluginMessages.getMessage("csrf-get-other-referer.success"));
response.put("flag", userSessionData.getValue("csrf-get-success"));
}
} else if (refererArr[2].equals(host)) {
response.put("success", false);
response.put("message", "Appears the request came from the original host");
response.put("flag", null);
} else {
Random random = new Random();
userSessionData.setValue("csrf-get-success", random.nextInt(65536));
response.put("success", true);
response.put("message", pluginMessages.getMessage("csrf-get-other-referer.success"));
response.put("flag", userSessionData.getValue("csrf-get-success"));
}
return response;
String host = (req.getHeader("host") == null) ? "NULL" : req.getHeader("host");
String referer = (req.getHeader("referer") == null) ? "NULL" : req.getHeader("referer");
String[] refererArr = referer.split("/");
if (referer.equals("NULL")) {
if ("true".equals(req.getParameter("csrf"))) {
Random random = new Random();
userSessionData.setValue("csrf-get-success", random.nextInt(65536));
response.put("success", true);
response.put("message", pluginMessages.getMessage("csrf-get-null-referer.success"));
response.put("flag", userSessionData.getValue("csrf-get-success"));
} else {
Random random = new Random();
userSessionData.setValue("csrf-get-success", random.nextInt(65536));
response.put("success", true);
response.put("message", pluginMessages.getMessage("csrf-get-other-referer.success"));
response.put("flag", userSessionData.getValue("csrf-get-success"));
}
} else if (refererArr[2].equals(host)) {
response.put("success", false);
response.put("message", "Appears the request came from the original host");
response.put("flag", null);
} else {
Random random = new Random();
userSessionData.setValue("csrf-get-success", random.nextInt(65536));
response.put("success", true);
response.put("message", pluginMessages.getMessage("csrf-get-other-referer.success"));
response.put("flag", userSessionData.getValue("csrf-get-success"));
}
return response;
}
}

View File

@@ -22,6 +22,7 @@
package org.owasp.webgoat.lessons.csrf;
import javax.servlet.http.HttpServletRequest;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
@@ -31,8 +32,6 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* @author nbaars
* @since 11/17/17.
@@ -41,26 +40,29 @@ import javax.servlet.http.HttpServletRequest;
@AssignmentHints({"csrf-login-hint1", "csrf-login-hint2", "csrf-login-hint3"})
public class CSRFLogin extends AssignmentEndpoint {
private final UserTrackerRepository userTrackerRepository;
private final UserTrackerRepository userTrackerRepository;
public CSRFLogin(UserTrackerRepository userTrackerRepository) {
this.userTrackerRepository = userTrackerRepository;
}
public CSRFLogin(UserTrackerRepository userTrackerRepository) {
this.userTrackerRepository = userTrackerRepository;
}
@PostMapping(path = "/csrf/login", produces = {"application/json"})
@ResponseBody
public AttackResult completed(HttpServletRequest request) {
String userName = request.getUserPrincipal().getName();
if (userName.startsWith("csrf")) {
markAssignmentSolvedWithRealUser(userName.substring("csrf-".length()));
return success(this).feedback("csrf-login-success").build();
}
return failed(this).feedback("csrf-login-failed").feedbackArgs(userName).build();
@PostMapping(
path = "/csrf/login",
produces = {"application/json"})
@ResponseBody
public AttackResult completed(HttpServletRequest request) {
String userName = request.getUserPrincipal().getName();
if (userName.startsWith("csrf")) {
markAssignmentSolvedWithRealUser(userName.substring("csrf-".length()));
return success(this).feedback("csrf-login-success").build();
}
return failed(this).feedback("csrf-login-failed").feedbackArgs(userName).build();
}
private void markAssignmentSolvedWithRealUser(String username) {
UserTracker userTracker = userTrackerRepository.findByUser(username);
userTracker.assignmentSolved(getWebSession().getCurrentLesson(), this.getClass().getSimpleName());
userTrackerRepository.save(userTracker);
}
private void markAssignmentSolvedWithRealUser(String username) {
UserTracker userTracker = userTrackerRepository.findByUser(username);
userTracker.assignmentSolved(
getWebSession().getCurrentLesson(), this.getClass().getSimpleName());
userTrackerRepository.save(userTracker);
}
}

View File

@@ -22,7 +22,17 @@
package org.owasp.webgoat.lessons.csrf;
import static org.springframework.http.MediaType.ALL_VALUE;
import com.google.common.collect.Lists;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
@@ -34,80 +44,75 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.springframework.http.MediaType.ALL_VALUE;
@RestController
@AssignmentHints({"csrf-review-hint1", "csrf-review-hint2", "csrf-review-hint3"})
public class ForgedReviews extends AssignmentEndpoint {
@Autowired
private WebSession webSession;
private static DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd, HH:mm:ss");
@Autowired private WebSession webSession;
private static DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd, HH:mm:ss");
private static final Map<String, List<Review>> userReviews = new HashMap<>();
private static final List<Review> REVIEWS = new ArrayList<>();
private static final String weakAntiCSRF = "2aa14227b9a13d0bede0388a7fba9aa9";
private static final Map<String, List<Review>> userReviews = new HashMap<>();
private static final List<Review> REVIEWS = new ArrayList<>();
private static final String weakAntiCSRF = "2aa14227b9a13d0bede0388a7fba9aa9";
static {
REVIEWS.add(
new Review("secUriTy", LocalDateTime.now().format(fmt), "This is like swiss cheese", 0));
REVIEWS.add(new Review("webgoat", LocalDateTime.now().format(fmt), "It works, sorta", 2));
REVIEWS.add(new Review("guest", LocalDateTime.now().format(fmt), "Best, App, Ever", 5));
REVIEWS.add(
new Review(
"guest",
LocalDateTime.now().format(fmt),
"This app is so insecure, I didn't even post this review, can you pull that off too?",
1));
}
static {
REVIEWS.add(new Review("secUriTy", LocalDateTime.now().format(fmt), "This is like swiss cheese", 0));
REVIEWS.add(new Review("webgoat", LocalDateTime.now().format(fmt), "It works, sorta", 2));
REVIEWS.add(new Review("guest", LocalDateTime.now().format(fmt), "Best, App, Ever", 5));
REVIEWS.add(new Review("guest", LocalDateTime.now().format(fmt), "This app is so insecure, I didn't even post this review, can you pull that off too?", 1));
@GetMapping(
path = "/csrf/review",
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = ALL_VALUE)
@ResponseBody
public Collection<Review> retrieveReviews() {
Collection<Review> allReviews = Lists.newArrayList();
Collection<Review> newReviews = userReviews.get(webSession.getUserName());
if (newReviews != null) {
allReviews.addAll(newReviews);
}
@GetMapping(path = "/csrf/review", produces = MediaType.APPLICATION_JSON_VALUE, consumes = ALL_VALUE)
@ResponseBody
public Collection<Review> retrieveReviews() {
Collection<Review> allReviews = Lists.newArrayList();
Collection<Review> newReviews = userReviews.get(webSession.getUserName());
if (newReviews != null) {
allReviews.addAll(newReviews);
}
allReviews.addAll(REVIEWS);
allReviews.addAll(REVIEWS);
return allReviews;
}
return allReviews;
@PostMapping("/csrf/review")
@ResponseBody
public AttackResult createNewReview(
String reviewText, Integer stars, String validateReq, HttpServletRequest request) {
final String host = (request.getHeader("host") == null) ? "NULL" : request.getHeader("host");
final String referer =
(request.getHeader("referer") == null) ? "NULL" : request.getHeader("referer");
final String[] refererArr = referer.split("/");
Review review = new Review();
review.setText(reviewText);
review.setDateTime(LocalDateTime.now().format(fmt));
review.setUser(webSession.getUserName());
review.setStars(stars);
var reviews = userReviews.getOrDefault(webSession.getUserName(), new ArrayList<>());
reviews.add(review);
userReviews.put(webSession.getUserName(), reviews);
// short-circuit
if (validateReq == null || !validateReq.equals(weakAntiCSRF)) {
return failed(this).feedback("csrf-you-forgot-something").build();
}
@PostMapping("/csrf/review")
@ResponseBody
public AttackResult createNewReview(String reviewText, Integer stars, String validateReq, HttpServletRequest request) {
final String host = (request.getHeader("host") == null) ? "NULL" : request.getHeader("host");
final String referer = (request.getHeader("referer") == null) ? "NULL" : request.getHeader("referer");
final String[] refererArr = referer.split("/");
Review review = new Review();
review.setText(reviewText);
review.setDateTime(LocalDateTime.now().format(fmt));
review.setUser(webSession.getUserName());
review.setStars(stars);
var reviews = userReviews.getOrDefault(webSession.getUserName(), new ArrayList<>());
reviews.add(review);
userReviews.put(webSession.getUserName(), reviews);
//short-circuit
if (validateReq == null || !validateReq.equals(weakAntiCSRF)) {
return failed(this).feedback("csrf-you-forgot-something").build();
}
//we have the spoofed files
if (referer != "NULL" && refererArr[2].equals(host)) {
return failed(this).feedback("csrf-same-host").build();
} else {
return success(this).feedback("csrf-review.success").build(); //feedback("xss-stored-comment-failure")
}
// we have the spoofed files
if (referer != "NULL" && refererArr[2].equals(host)) {
return failed(this).feedback("csrf-same-host").build();
} else {
return success(this)
.feedback("csrf-review.success")
.build(); // feedback("xss-stored-comment-failure")
}
}
}

View File

@@ -22,13 +22,12 @@
package org.owasp.webgoat.lessons.csrf;
import javax.xml.bind.annotation.XmlRootElement;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.xml.bind.annotation.XmlRootElement;
/**
* @author nbaars
* @since 4/8/17.
@@ -39,9 +38,8 @@ import javax.xml.bind.annotation.XmlRootElement;
@NoArgsConstructor
@XmlRootElement
public class Review {
private String user;
private String dateTime;
private String text;
private Integer stars;
private String user;
private String dateTime;
private String text;
private Integer stars;
}

View File

@@ -8,25 +8,26 @@ import org.springframework.stereotype.Component;
* ************************************************************************************************
* 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 - 2014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
*
* <p>Copyright (c) 2002 - 2014 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
*
* <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>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* <p>
*
* @author WebGoat
@@ -35,13 +36,13 @@ import org.springframework.stereotype.Component;
*/
@Component
public class InsecureDeserialization extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.A8;
}
@Override
public Category getDefaultCategory() {
return Category.A8;
}
@Override
public String getTitle() {
return "insecure-deserialization.title";
}
@Override
public String getTitle() {
return "insecure-deserialization.title";
}
}

View File

@@ -22,6 +22,11 @@
package org.owasp.webgoat.lessons.deserialization;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.util.Base64;
import org.dummy.insecure.framework.VulnerableTaskHolder;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
@@ -31,51 +36,50 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.util.Base64;
@RestController
@AssignmentHints({"insecure-deserialization.hints.1", "insecure-deserialization.hints.2", "insecure-deserialization.hints.3"})
@AssignmentHints({
"insecure-deserialization.hints.1",
"insecure-deserialization.hints.2",
"insecure-deserialization.hints.3"
})
public class InsecureDeserializationTask extends AssignmentEndpoint {
@PostMapping("/InsecureDeserialization/task")
@ResponseBody
public AttackResult completed(@RequestParam String token) throws IOException {
String b64token;
long before;
long after;
int delay;
@PostMapping("/InsecureDeserialization/task")
@ResponseBody
public AttackResult completed(@RequestParam String token) throws IOException {
String b64token;
long before;
long after;
int delay;
b64token = token.replace('-', '+').replace('_', '/');
b64token = token.replace('-', '+').replace('_', '/');
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(b64token)))) {
before = System.currentTimeMillis();
Object o = ois.readObject();
if (!(o instanceof VulnerableTaskHolder)) {
if (o instanceof String) {
return failed(this).feedback("insecure-deserialization.stringobject").build();
}
return failed(this).feedback("insecure-deserialization.wrongobject").build();
}
after = System.currentTimeMillis();
} catch (InvalidClassException e) {
return failed(this).feedback("insecure-deserialization.invalidversion").build();
} catch (IllegalArgumentException e) {
return failed(this).feedback("insecure-deserialization.expired").build();
} catch (Exception e) {
return failed(this).feedback("insecure-deserialization.invalidversion").build();
try (ObjectInputStream ois =
new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(b64token)))) {
before = System.currentTimeMillis();
Object o = ois.readObject();
if (!(o instanceof VulnerableTaskHolder)) {
if (o instanceof String) {
return failed(this).feedback("insecure-deserialization.stringobject").build();
}
delay = (int) (after - before);
if (delay > 7000) {
return failed(this).build();
}
if (delay < 3000) {
return failed(this).build();
}
return success(this).build();
return failed(this).feedback("insecure-deserialization.wrongobject").build();
}
after = System.currentTimeMillis();
} catch (InvalidClassException e) {
return failed(this).feedback("insecure-deserialization.invalidversion").build();
} catch (IllegalArgumentException e) {
return failed(this).feedback("insecure-deserialization.expired").build();
} catch (Exception e) {
return failed(this).feedback("insecure-deserialization.invalidversion").build();
}
delay = (int) (after - before);
if (delay > 7000) {
return failed(this).build();
}
if (delay < 3000) {
return failed(this).build();
}
return success(this).build();
}
}

View File

@@ -11,44 +11,41 @@ import java.util.Base64;
public class SerializationHelper {
private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
public static Object fromString(String s) throws IOException,
ClassNotFoundException {
byte[] data = Base64.getDecoder().decode(s);
ObjectInputStream ois = new ObjectInputStream(
new ByteArrayInputStream(data));
Object o = ois.readObject();
ois.close();
return o;
public static Object fromString(String s) throws IOException, ClassNotFoundException {
byte[] data = Base64.getDecoder().decode(s);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
Object o = ois.readObject();
ois.close();
return o;
}
public static String toString(Serializable o) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
return Base64.getEncoder().encodeToString(baos.toByteArray());
}
public static String show() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeLong(-8699352886133051976L);
dos.close();
byte[] longBytes = baos.toByteArray();
return bytesToHex(longBytes);
}
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
public static String toString(Serializable o) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
return Base64.getEncoder().encodeToString(baos.toByteArray());
}
public static String show() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeLong(-8699352886133051976L);
dos.close();
byte[] longBytes = baos.toByteArray();
return bytesToHex(longBytes);
}
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
return new String(hexChars);
}
}

View File

@@ -36,13 +36,13 @@ import org.springframework.stereotype.Component;
@Component
public class HijackSession extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.A1;
}
@Override
public Category getDefaultCategory() {
return Category.A1;
}
@Override
public String getTitle() {
return "hijacksession.title";
}
@Override
public String getTitle() {
return "hijacksession.title";
}
}

View File

@@ -24,7 +24,6 @@ package org.owasp.webgoat.lessons.hijacksession;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
@@ -46,47 +45,47 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@AssignmentHints({
"hijacksession.hints.1",
"hijacksession.hints.2",
"hijacksession.hints.3",
"hijacksession.hints.4",
"hijacksession.hints.5"
"hijacksession.hints.1",
"hijacksession.hints.2",
"hijacksession.hints.3",
"hijacksession.hints.4",
"hijacksession.hints.5"
})
public class HijackSessionAssignment extends AssignmentEndpoint {
private static final String COOKIE_NAME = "hijack_cookie";
private static final String COOKIE_NAME = "hijack_cookie";
@Autowired
HijackSessionAuthenticationProvider provider;
@Autowired HijackSessionAuthenticationProvider provider;
@PostMapping(path = "/HijackSession/login")
@ResponseBody
public AttackResult login(
@RequestParam String username,
@RequestParam String password,
@CookieValue(value = COOKIE_NAME, required = false) String cookieValue,
HttpServletResponse response) {
@PostMapping(path = "/HijackSession/login")
@ResponseBody
public AttackResult login(
@RequestParam String username,
@RequestParam String password,
@CookieValue(value = COOKIE_NAME, required = false) String cookieValue,
HttpServletResponse response) {
Authentication authentication;
if (StringUtils.isEmpty(cookieValue)) {
authentication = provider.authenticate(Authentication.builder().name(username).credentials(password).build());
setCookie(response, authentication.getId());
} else {
authentication = provider.authenticate(Authentication.builder().id(cookieValue).build());
}
if (authentication.isAuthenticated()) {
return success(this).build();
}
return failed(this).build();
Authentication authentication;
if (StringUtils.isEmpty(cookieValue)) {
authentication =
provider.authenticate(
Authentication.builder().name(username).credentials(password).build());
setCookie(response, authentication.getId());
} else {
authentication = provider.authenticate(Authentication.builder().id(cookieValue).build());
}
private void setCookie(HttpServletResponse response, String cookieValue) {
Cookie cookie = new Cookie(COOKIE_NAME, cookieValue);
cookie.setPath("/WebGoat");
cookie.setSecure(true);
response.addCookie(cookie);
if (authentication.isAuthenticated()) {
return success(this).build();
}
return failed(this).build();
}
private void setCookie(HttpServletResponse response, String cookieValue) {
Cookie cookie = new Cookie(COOKIE_NAME, cookieValue);
cookie.setPath("/WebGoat");
cookie.setSecure(true);
response.addCookie(cookie);
}
}

View File

@@ -24,44 +24,39 @@
package org.owasp.webgoat.lessons.hijacksession.cas;
import java.security.Principal;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
/**
*
* @author Angel Olle Blazquez
*
*/
@Getter
@ToString
public class Authentication implements Principal {
private boolean authenticated = false;
private String name;
private Object credentials;
private String id;
private boolean authenticated = false;
private String name;
private Object credentials;
private String id;
@Builder
public Authentication(String name, Object credentials, String id) {
this.name = name;
this.credentials = credentials;
this.id = id;
}
@Builder
public Authentication(String name, Object credentials, String id) {
this.name = name;
this.credentials = credentials;
this.id = id;
}
@Override
public String getName() {
return name;
}
@Override
public String getName() {
return name;
}
protected void setAuthenticated(boolean authenticated) {
this.authenticated = authenticated;
}
protected void setId(String id) {
this.id = id;
}
protected void setAuthenticated(boolean authenticated) {
this.authenticated = authenticated;
}
protected void setId(String id) {
this.id = id;
}
}

View File

@@ -26,14 +26,10 @@ package org.owasp.webgoat.lessons.hijacksession.cas;
import java.security.Principal;
/**
*
* @author Angel Olle Blazquez
*
*/
@FunctionalInterface
public interface AuthenticationProvider<T extends Principal> {
T authenticate(T t);
T authenticate(T t);
}

View File

@@ -30,15 +30,12 @@ import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.DoublePredicate;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.ApplicationScope;
/**
*
* @author Angel Olle Blazquez
*
*/
// weak id value and mechanism
@@ -47,54 +44,53 @@ import org.springframework.web.context.annotation.ApplicationScope;
@Component
public class HijackSessionAuthenticationProvider implements AuthenticationProvider<Authentication> {
private Queue<String> sessions = new LinkedList<>();
private static long id = new Random().nextLong() & Long.MAX_VALUE;
protected static final int MAX_SESSIONS = 50;
private Queue<String> sessions = new LinkedList<>();
private static long id = new Random().nextLong() & Long.MAX_VALUE;
protected static final int MAX_SESSIONS = 50;
private static final DoublePredicate PROBABILITY_DOUBLE_PREDICATE = pr -> pr < 0.75;
private static final Supplier<String> GENERATE_SESSION_ID = () -> ++id + "-" + Instant.now().toEpochMilli();
public static final Supplier<Authentication> AUTHENTICATION_SUPPLIER = () -> Authentication
.builder()
.id(GENERATE_SESSION_ID.get())
.build();
private static final DoublePredicate PROBABILITY_DOUBLE_PREDICATE = pr -> pr < 0.75;
private static final Supplier<String> GENERATE_SESSION_ID =
() -> ++id + "-" + Instant.now().toEpochMilli();
public static final Supplier<Authentication> AUTHENTICATION_SUPPLIER =
() -> Authentication.builder().id(GENERATE_SESSION_ID.get()).build();
@Override
public Authentication authenticate(Authentication authentication) {
if (authentication == null) {
return AUTHENTICATION_SUPPLIER.get();
}
if (StringUtils.isNotEmpty(authentication.getId()) && sessions.contains(authentication.getId())) {
authentication.setAuthenticated(true);
return authentication;
}
if (StringUtils.isEmpty(authentication.getId())) {
authentication.setId(GENERATE_SESSION_ID.get());
}
authorizedUserAutoLogin();
return authentication;
@Override
public Authentication authenticate(Authentication authentication) {
if (authentication == null) {
return AUTHENTICATION_SUPPLIER.get();
}
protected void authorizedUserAutoLogin() {
if (!PROBABILITY_DOUBLE_PREDICATE.test(ThreadLocalRandom.current().nextDouble())) {
Authentication authentication = AUTHENTICATION_SUPPLIER.get();
authentication.setAuthenticated(true);
addSession(authentication.getId());
}
if (StringUtils.isNotEmpty(authentication.getId())
&& sessions.contains(authentication.getId())) {
authentication.setAuthenticated(true);
return authentication;
}
protected boolean addSession(String sessionId) {
if (sessions.size() >= MAX_SESSIONS) {
sessions.remove();
}
return sessions.add(sessionId);
if (StringUtils.isEmpty(authentication.getId())) {
authentication.setId(GENERATE_SESSION_ID.get());
}
protected int getSessionsSize() {
return sessions.size();
}
authorizedUserAutoLogin();
return authentication;
}
protected void authorizedUserAutoLogin() {
if (!PROBABILITY_DOUBLE_PREDICATE.test(ThreadLocalRandom.current().nextDouble())) {
Authentication authentication = AUTHENTICATION_SUPPLIER.get();
authentication.setAuthenticated(true);
addSession(authentication.getId());
}
}
protected boolean addSession(String sessionId) {
if (sessions.size() >= MAX_SESSIONS) {
sessions.remove();
}
return sessions.add(sessionId);
}
protected int getSessionsSize() {
return sessions.size();
}
}

View File

@@ -8,25 +8,26 @@ import org.springframework.stereotype.Component;
* ************************************************************************************************
* 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 - 2014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
*
* <p>Copyright (c) 2002 - 2014 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
*
* <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>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* <p>
*
* @author WebGoat
@@ -35,13 +36,13 @@ import org.springframework.stereotype.Component;
*/
@Component
public class HtmlTampering extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.CLIENT_SIDE;
}
@Override
public Category getDefaultCategory() {
return Category.CLIENT_SIDE;
}
@Override
public String getTitle() {
return "html-tampering.title";
}
@Override
public String getTitle() {
return "html-tampering.title";
}
}

View File

@@ -34,12 +34,12 @@ import org.springframework.web.bind.annotation.RestController;
@AssignmentHints({"hint1", "hint2", "hint3"})
public class HtmlTamperingTask extends AssignmentEndpoint {
@PostMapping("/HtmlTampering/task")
@ResponseBody
public AttackResult completed(@RequestParam String QTY, @RequestParam String Total) {
if (Float.parseFloat(QTY) * 2999.99 > Float.parseFloat(Total) + 1) {
return success(this).feedback("html-tampering.tamper.success").build();
}
return failed(this).feedback("html-tampering.tamper.failure").build();
@PostMapping("/HtmlTampering/task")
@ResponseBody
public AttackResult completed(@RequestParam String QTY, @RequestParam String Total) {
if (Float.parseFloat(QTY) * 2999.99 > Float.parseFloat(Total) + 1) {
return success(this).feedback("html-tampering.tamper.success").build();
}
return failed(this).feedback("html-tampering.tamper.failure").build();
}
}

View File

@@ -28,13 +28,13 @@ import org.springframework.stereotype.Component;
@Component
public class HttpBasics extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.GENERAL;
}
@Override
public Category getDefaultCategory() {
return Category.GENERAL;
}
@Override
public String getTitle() {
return "1.http-basics.title";//first lesson in general
}
@Override
public String getTitle() {
return "1.http-basics.title"; // first lesson in general
}
}

View File

@@ -34,16 +34,16 @@ import org.springframework.web.bind.annotation.RestController;
@AssignmentHints({"http-basics.hints.http_basics_lesson.1"})
public class HttpBasicsLesson extends AssignmentEndpoint {
@PostMapping("/HttpBasics/attack1")
@ResponseBody
public AttackResult completed(@RequestParam String person) {
if (!person.isBlank()) {
return success(this)
.feedback("http-basics.reversed")
.feedbackArgs(new StringBuilder(person).reverse().toString())
.build();
} else {
return failed(this).feedback("http-basics.empty").build();
}
@PostMapping("/HttpBasics/attack1")
@ResponseBody
public AttackResult completed(@RequestParam String person) {
if (!person.isBlank()) {
return success(this)
.feedback("http-basics.reversed")
.feedbackArgs(new StringBuilder(person).reverse().toString())
.build();
} else {
return failed(this).feedback("http-basics.empty").build();
}
}
}

View File

@@ -36,19 +36,22 @@ import org.springframework.web.bind.annotation.RestController;
@AssignmentPath("HttpBasics/attack2")
public class HttpBasicsQuiz extends AssignmentEndpoint {
@PostMapping("/HttpBasics/attack2")
@ResponseBody
public AttackResult completed(@RequestParam String answer, @RequestParam String magic_answer, @RequestParam String magic_num) {
if ("POST".equalsIgnoreCase(answer) && magic_answer.equals(magic_num)) {
return success(this).build();
} else {
if (!"POST".equalsIgnoreCase(answer)) {
return failed(this).feedback("http-basics.incorrect").build();
}
if (!magic_answer.equals(magic_num)) {
return failed(this).feedback("http-basics.magic").build();
}
}
return failed(this).build();
@PostMapping("/HttpBasics/attack2")
@ResponseBody
public AttackResult completed(
@RequestParam String answer,
@RequestParam String magic_answer,
@RequestParam String magic_num) {
if ("POST".equalsIgnoreCase(answer) && magic_answer.equals(magic_num)) {
return success(this).build();
} else {
if (!"POST".equalsIgnoreCase(answer)) {
return failed(this).feedback("http-basics.incorrect").build();
}
if (!magic_answer.equals(magic_num)) {
return failed(this).feedback("http-basics.magic").build();
}
}
return failed(this).build();
}
}

View File

@@ -22,6 +22,7 @@
package org.owasp.webgoat.lessons.httpproxies;
import javax.servlet.http.HttpServletRequest;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.springframework.http.HttpMethod;
@@ -32,22 +33,27 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
public class HttpBasicsInterceptRequest extends AssignmentEndpoint {
@RequestMapping(path = "/HttpProxies/intercept-request", method = {RequestMethod.POST, RequestMethod.GET})
@ResponseBody
public AttackResult completed(@RequestHeader(value = "x-request-intercepted", required = false) Boolean headerValue,
@RequestParam(value = "changeMe", required = false) String paramValue, HttpServletRequest request) {
if (HttpMethod.POST.matches(request.getMethod())) {
return failed(this).feedback("http-proxies.intercept.failure").build();
}
if (headerValue != null && paramValue != null && headerValue && "Requests are tampered easily".equalsIgnoreCase(paramValue)) {
return success(this).feedback("http-proxies.intercept.success").build();
} else {
return failed(this).feedback("http-proxies.intercept.failure").build();
}
@RequestMapping(
path = "/HttpProxies/intercept-request",
method = {RequestMethod.POST, RequestMethod.GET})
@ResponseBody
public AttackResult completed(
@RequestHeader(value = "x-request-intercepted", required = false) Boolean headerValue,
@RequestParam(value = "changeMe", required = false) String paramValue,
HttpServletRequest request) {
if (HttpMethod.POST.matches(request.getMethod())) {
return failed(this).feedback("http-proxies.intercept.failure").build();
}
if (headerValue != null
&& paramValue != null
&& headerValue
&& "Requests are tampered easily".equalsIgnoreCase(paramValue)) {
return success(this).feedback("http-proxies.intercept.success").build();
} else {
return failed(this).feedback("http-proxies.intercept.failure").build();
}
}
}

View File

@@ -8,25 +8,26 @@ import org.springframework.stereotype.Component;
* ************************************************************************************************
* 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 - 2014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
*
* <p>Copyright (c) 2002 - 2014 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
*
* <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>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* <p>
*
* @author WebGoat
@@ -35,13 +36,13 @@ import org.springframework.stereotype.Component;
*/
@Component
public class HttpProxies extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.GENERAL;
}
@Override
public Category getDefaultCategory() {
return Category.GENERAL;
}
@Override
public String getTitle() {
return "2.http-proxies.title";//second lesson in GENERAL
}
@Override
public String getTitle() {
return "2.http-proxies.title"; // second lesson in GENERAL
}
}

View File

@@ -8,25 +8,26 @@ import org.springframework.stereotype.Component;
* ************************************************************************************************
* 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 - 2014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
*
* <p>Copyright (c) 2002 - 2014 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
*
* <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>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* <p>
*
* @author misfir3
@@ -36,13 +37,13 @@ import org.springframework.stereotype.Component;
@Component
public class IDOR extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.A1;
}
@Override
public Category getDefaultCategory() {
return Category.A1;
}
@Override
public String getTitle() {
return "idor.title";
}
@Override
public String getTitle() {
return "idor.title";
}
}

View File

@@ -31,22 +31,28 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@AssignmentHints({"idor.hints.idorDiffAttributes1", "idor.hints.idorDiffAttributes2", "idor.hints.idorDiffAttributes3"})
@AssignmentHints({
"idor.hints.idorDiffAttributes1",
"idor.hints.idorDiffAttributes2",
"idor.hints.idorDiffAttributes3"
})
public class IDORDiffAttributes extends AssignmentEndpoint {
@PostMapping("/IDOR/diff-attributes")
@ResponseBody
public AttackResult completed(@RequestParam String attributes) {
attributes = attributes.trim();
String[] diffAttribs = attributes.split(",");
if (diffAttribs.length < 2) {
return failed(this).feedback("idor.diff.attributes.missing").build();
}
if (diffAttribs[0].toLowerCase().trim().equals("userid") && diffAttribs[1].toLowerCase().trim().equals("role")
|| diffAttribs[1].toLowerCase().trim().equals("userid") && diffAttribs[0].toLowerCase().trim().equals("role")) {
return success(this).feedback("idor.diff.success").build();
} else {
return failed(this).feedback("idor.diff.failure").build();
}
@PostMapping("/IDOR/diff-attributes")
@ResponseBody
public AttackResult completed(@RequestParam String attributes) {
attributes = attributes.trim();
String[] diffAttribs = attributes.split(",");
if (diffAttribs.length < 2) {
return failed(this).feedback("idor.diff.attributes.missing").build();
}
if (diffAttribs[0].toLowerCase().trim().equals("userid")
&& diffAttribs[1].toLowerCase().trim().equals("role")
|| diffAttribs[1].toLowerCase().trim().equals("userid")
&& diffAttribs[0].toLowerCase().trim().equals("role")) {
return success(this).feedback("idor.diff.success").build();
} else {
return failed(this).feedback("idor.diff.failure").build();
}
}
}

View File

@@ -34,66 +34,80 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@AssignmentHints({"idor.hints.otherProfile1", "idor.hints.otherProfile2", "idor.hints.otherProfile3", "idor.hints.otherProfile4", "idor.hints.otherProfile5", "idor.hints.otherProfile6", "idor.hints.otherProfile7", "idor.hints.otherProfile8", "idor.hints.otherProfile9"})
@AssignmentHints({
"idor.hints.otherProfile1",
"idor.hints.otherProfile2",
"idor.hints.otherProfile3",
"idor.hints.otherProfile4",
"idor.hints.otherProfile5",
"idor.hints.otherProfile6",
"idor.hints.otherProfile7",
"idor.hints.otherProfile8",
"idor.hints.otherProfile9"
})
public class IDOREditOtherProfiile extends AssignmentEndpoint {
@Autowired
private UserSessionData userSessionData;
@Autowired private UserSessionData userSessionData;
@PutMapping(path = "/IDOR/profile/{userId}", consumes = "application/json")
@ResponseBody
public AttackResult completed(@PathVariable("userId") String userId, @RequestBody UserProfile userSubmittedProfile) {
@PutMapping(path = "/IDOR/profile/{userId}", consumes = "application/json")
@ResponseBody
public AttackResult completed(
@PathVariable("userId") String userId, @RequestBody UserProfile userSubmittedProfile) {
String authUserId = (String) userSessionData.getValue("idor-authenticated-user-id");
// this is where it starts ... accepting the user submitted ID and assuming it will be the same as the logged in userId and not checking for proper authorization
// Certain roles can sometimes edit others' profiles, but we shouldn't just assume that and let everyone, right?
// Except that this is a vulnerable app ... so we will
UserProfile currentUserProfile = new UserProfile(userId);
if (userSubmittedProfile.getUserId() != null && !userSubmittedProfile.getUserId().equals(authUserId)) {
// let's get this started ...
currentUserProfile.setColor(userSubmittedProfile.getColor());
currentUserProfile.setRole(userSubmittedProfile.getRole());
// we will persist in the session object for now in case we want to refer back or use it later
userSessionData.setValue("idor-updated-other-profile", currentUserProfile);
if (currentUserProfile.getRole() <= 1 && currentUserProfile.getColor().toLowerCase().equals("red")) {
return success(this)
.feedback("idor.edit.profile.success1")
.output(currentUserProfile.profileToMap().toString())
.build();
}
String authUserId = (String) userSessionData.getValue("idor-authenticated-user-id");
// this is where it starts ... accepting the user submitted ID and assuming it will be the same
// as the logged in userId and not checking for proper authorization
// Certain roles can sometimes edit others' profiles, but we shouldn't just assume that and let
// everyone, right?
// Except that this is a vulnerable app ... so we will
UserProfile currentUserProfile = new UserProfile(userId);
if (userSubmittedProfile.getUserId() != null
&& !userSubmittedProfile.getUserId().equals(authUserId)) {
// let's get this started ...
currentUserProfile.setColor(userSubmittedProfile.getColor());
currentUserProfile.setRole(userSubmittedProfile.getRole());
// we will persist in the session object for now in case we want to refer back or use it later
userSessionData.setValue("idor-updated-other-profile", currentUserProfile);
if (currentUserProfile.getRole() <= 1
&& currentUserProfile.getColor().toLowerCase().equals("red")) {
return success(this)
.feedback("idor.edit.profile.success1")
.output(currentUserProfile.profileToMap().toString())
.build();
}
if (currentUserProfile.getRole() > 1 && currentUserProfile.getColor().toLowerCase().equals("red")) {
return success(this)
.feedback("idor.edit.profile.failure1")
.output(currentUserProfile.profileToMap().toString())
.build();
}
if (currentUserProfile.getRole() > 1
&& currentUserProfile.getColor().toLowerCase().equals("red")) {
return success(this)
.feedback("idor.edit.profile.failure1")
.output(currentUserProfile.profileToMap().toString())
.build();
}
if (currentUserProfile.getRole() <= 1 && !currentUserProfile.getColor().toLowerCase().equals("red")) {
return success(this)
.feedback("idor.edit.profile.failure2")
.output(currentUserProfile.profileToMap().toString())
.build();
}
// else
return failed(this)
.feedback("idor.edit.profile.failure3")
.output(currentUserProfile.profileToMap().toString())
.build();
} else if (userSubmittedProfile.getUserId().equals(authUserId)) {
return failed(this).feedback("idor.edit.profile.failure4").build();
}
if (currentUserProfile.getColor().equals("black") && currentUserProfile.getRole() <= 1) {
return success(this)
.feedback("idor.edit.profile.success2")
.output(userSessionData.getValue("idor-updated-own-profile").toString())
.build();
} else {
return failed(this).feedback("idor.edit.profile.failure3").build();
}
if (currentUserProfile.getRole() <= 1
&& !currentUserProfile.getColor().toLowerCase().equals("red")) {
return success(this)
.feedback("idor.edit.profile.failure2")
.output(currentUserProfile.profileToMap().toString())
.build();
}
// else
return failed(this)
.feedback("idor.edit.profile.failure3")
.output(currentUserProfile.profileToMap().toString())
.build();
} else if (userSubmittedProfile.getUserId().equals(authUserId)) {
return failed(this).feedback("idor.edit.profile.failure4").build();
}
if (currentUserProfile.getColor().equals("black") && currentUserProfile.getRole() <= 1) {
return success(this)
.feedback("idor.edit.profile.success2")
.output(userSessionData.getValue("idor-updated-own-profile").toString())
.build();
} else {
return failed(this).feedback("idor.edit.profile.failure3").build();
}
}
}

View File

@@ -22,6 +22,8 @@
package org.owasp.webgoat.lessons.idor;
import java.util.HashMap;
import java.util.Map;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
@@ -31,47 +33,44 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@AssignmentHints({"idor.hints.idor_login"})
public class IDORLogin extends AssignmentEndpoint {
private Map<String, Map<String, String>> idorUserInfo = new HashMap<>();
private Map<String, Map<String, String>> idorUserInfo = new HashMap<>();
public void initIDORInfo() {
public void initIDORInfo() {
idorUserInfo.put("tom", new HashMap<String, String>());
idorUserInfo.get("tom").put("password", "cat");
idorUserInfo.get("tom").put("id", "2342384");
idorUserInfo.get("tom").put("color", "yellow");
idorUserInfo.get("tom").put("size", "small");
idorUserInfo.put("tom", new HashMap<String, String>());
idorUserInfo.get("tom").put("password", "cat");
idorUserInfo.get("tom").put("id", "2342384");
idorUserInfo.get("tom").put("color", "yellow");
idorUserInfo.get("tom").put("size", "small");
idorUserInfo.put("bill", new HashMap<String, String>());
idorUserInfo.get("bill").put("password", "buffalo");
idorUserInfo.get("bill").put("id", "2342388");
idorUserInfo.get("bill").put("color", "brown");
idorUserInfo.get("bill").put("size", "large");
idorUserInfo.put("bill", new HashMap<String, String>());
idorUserInfo.get("bill").put("password", "buffalo");
idorUserInfo.get("bill").put("id", "2342388");
idorUserInfo.get("bill").put("color", "brown");
idorUserInfo.get("bill").put("size", "large");
}
@PostMapping("/IDOR/login")
@ResponseBody
public AttackResult completed(@RequestParam String username, @RequestParam String password) {
initIDORInfo();
UserSessionData userSessionData = getUserSessionData();
if (idorUserInfo.containsKey(username)) {
if ("tom".equals(username) && idorUserInfo.get("tom").get("password").equals(password)) {
userSessionData.setValue("idor-authenticated-as", username);
userSessionData.setValue(
"idor-authenticated-user-id", idorUserInfo.get(username).get("id"));
return success(this).feedback("idor.login.success").feedbackArgs(username).build();
} else {
return failed(this).feedback("idor.login.failure").build();
}
} else {
return failed(this).feedback("idor.login.failure").build();
}
@PostMapping("/IDOR/login")
@ResponseBody
public AttackResult completed(@RequestParam String username, @RequestParam String password) {
initIDORInfo();
UserSessionData userSessionData = getUserSessionData();
if (idorUserInfo.containsKey(username)) {
if ("tom".equals(username) && idorUserInfo.get("tom").get("password").equals(password)) {
userSessionData.setValue("idor-authenticated-as", username);
userSessionData.setValue("idor-authenticated-user-id", idorUserInfo.get(username).get("id"));
return success(this).feedback("idor.login.success").feedbackArgs(username).build();
} else {
return failed(this).feedback("idor.login.failure").build();
}
} else {
return failed(this).feedback("idor.login.failure").build();
}
}
}
}

View File

@@ -22,6 +22,9 @@
package org.owasp.webgoat.lessons.idor;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
@@ -32,38 +35,49 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@RestController
@AssignmentHints({"idor.hints.otherProfile1", "idor.hints.otherProfile2", "idor.hints.otherProfile3", "idor.hints.otherProfile4", "idor.hints.otherProfile5", "idor.hints.otherProfile6", "idor.hints.otherProfile7", "idor.hints.otherProfile8", "idor.hints.otherProfile9"})
@AssignmentHints({
"idor.hints.otherProfile1",
"idor.hints.otherProfile2",
"idor.hints.otherProfile3",
"idor.hints.otherProfile4",
"idor.hints.otherProfile5",
"idor.hints.otherProfile6",
"idor.hints.otherProfile7",
"idor.hints.otherProfile8",
"idor.hints.otherProfile9"
})
public class IDORViewOtherProfile extends AssignmentEndpoint {
@Autowired
UserSessionData userSessionData;
@Autowired UserSessionData userSessionData;
@GetMapping(path = "/IDOR/profile/{userId}", produces = {"application/json"})
@ResponseBody
public AttackResult completed(@PathVariable("userId") String userId, HttpServletResponse resp) {
Map<String, Object> details = new HashMap<>();
@GetMapping(
path = "/IDOR/profile/{userId}",
produces = {"application/json"})
@ResponseBody
public AttackResult completed(@PathVariable("userId") String userId, HttpServletResponse resp) {
Map<String, Object> details = new HashMap<>();
if (userSessionData.getValue("idor-authenticated-as").equals("tom")) {
//going to use session auth to view this one
String authUserId = (String) userSessionData.getValue("idor-authenticated-user-id");
if (userId != null && !userId.equals(authUserId)) {
//on the right track
UserProfile requestedProfile = new UserProfile(userId);
// secure code would ensure there was a horizontal access control check prior to dishing up the requested profile
if (requestedProfile.getUserId().equals("2342388")) {
return success(this).feedback("idor.view.profile.success").output(requestedProfile.profileToMap().toString()).build();
} else {
return failed(this).feedback("idor.view.profile.close1").build();
}
} else {
return failed(this).feedback("idor.view.profile.close2").build();
}
if (userSessionData.getValue("idor-authenticated-as").equals("tom")) {
// going to use session auth to view this one
String authUserId = (String) userSessionData.getValue("idor-authenticated-user-id");
if (userId != null && !userId.equals(authUserId)) {
// on the right track
UserProfile requestedProfile = new UserProfile(userId);
// secure code would ensure there was a horizontal access control check prior to dishing up
// the requested profile
if (requestedProfile.getUserId().equals("2342388")) {
return success(this)
.feedback("idor.view.profile.success")
.output(requestedProfile.profileToMap().toString())
.build();
} else {
return failed(this).feedback("idor.view.profile.close1").build();
}
return failed(this).build();
} else {
return failed(this).feedback("idor.view.profile.close2").build();
}
}
return failed(this).build();
}
}

View File

@@ -22,7 +22,8 @@
package org.owasp.webgoat.lessons.idor;
import java.util.HashMap;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.container.session.UserSessionData;
import org.springframework.beans.factory.annotation.Autowired;
@@ -30,36 +31,36 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@Slf4j
public class IDORViewOwnProfile {
@Autowired
UserSessionData userSessionData;
@Autowired UserSessionData userSessionData;
@GetMapping(path = {"/IDOR/own", "/IDOR/profile"}, produces = {"application/json"})
@ResponseBody
public Map<String, Object> invoke() {
Map<String,Object> details = new HashMap<>();
try {
if (userSessionData.getValue("idor-authenticated-as").equals("tom")) {
//going to use session auth to view this one
String authUserId = (String)userSessionData.getValue("idor-authenticated-user-id");
UserProfile userProfile = new UserProfile(authUserId);
details.put("userId",userProfile.getUserId());
details.put("name",userProfile.getName());
details.put("color",userProfile.getColor());
details.put("size",userProfile.getSize());
details.put("role",userProfile.getRole());
} else {
details.put("error","You do not have privileges to view the profile. Authenticate as tom first please.");
}
}catch (Exception ex) {
log.error("something went wrong", ex.getMessage());
}
return details;
@GetMapping(
path = {"/IDOR/own", "/IDOR/profile"},
produces = {"application/json"})
@ResponseBody
public Map<String, Object> invoke() {
Map<String, Object> details = new HashMap<>();
try {
if (userSessionData.getValue("idor-authenticated-as").equals("tom")) {
// going to use session auth to view this one
String authUserId = (String) userSessionData.getValue("idor-authenticated-user-id");
UserProfile userProfile = new UserProfile(authUserId);
details.put("userId", userProfile.getUserId());
details.put("name", userProfile.getName());
details.put("color", userProfile.getColor());
details.put("size", userProfile.getSize());
details.put("role", userProfile.getRole());
} else {
details.put(
"error",
"You do not have privileges to view the profile. Authenticate as tom first please.");
}
} catch (Exception ex) {
log.error("something went wrong", ex.getMessage());
}
return details;
}
}

View File

@@ -22,7 +22,6 @@
package org.owasp.webgoat.lessons.idor;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
@@ -34,33 +33,42 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@AssignmentHints({"idor.hints.ownProfileAltUrl1", "idor.hints.ownProfileAltUrl2", "idor.hints.ownProfileAltUrl3"})
@AssignmentHints({
"idor.hints.ownProfileAltUrl1",
"idor.hints.ownProfileAltUrl2",
"idor.hints.ownProfileAltUrl3"
})
public class IDORViewOwnProfileAltUrl extends AssignmentEndpoint {
@Autowired
UserSessionData userSessionData;
@Autowired UserSessionData userSessionData;
@PostMapping("/IDOR/profile/alt-path")
@ResponseBody
public AttackResult completed(@RequestParam String url) {
try {
if (userSessionData.getValue("idor-authenticated-as").equals("tom")) {
//going to use session auth to view this one
String authUserId = (String) userSessionData.getValue("idor-authenticated-user-id");
//don't care about http://localhost:8080 ... just want WebGoat/
String[] urlParts = url.split("/");
if (urlParts[0].equals("WebGoat") && urlParts[1].equals("IDOR") && urlParts[2].equals("profile") && urlParts[3].equals(authUserId)) {
UserProfile userProfile = new UserProfile(authUserId);
return success(this).feedback("idor.view.own.profile.success").output(userProfile.profileToMap().toString()).build();
} else {
return failed(this).feedback("idor.view.own.profile.failure1").build();
}
} else {
return failed(this).feedback("idor.view.own.profile.failure2").build();
}
} catch (Exception ex) {
return failed(this).feedback("an error occurred with your request").build();
@PostMapping("/IDOR/profile/alt-path")
@ResponseBody
public AttackResult completed(@RequestParam String url) {
try {
if (userSessionData.getValue("idor-authenticated-as").equals("tom")) {
// going to use session auth to view this one
String authUserId = (String) userSessionData.getValue("idor-authenticated-user-id");
// don't care about http://localhost:8080 ... just want WebGoat/
String[] urlParts = url.split("/");
if (urlParts[0].equals("WebGoat")
&& urlParts[1].equals("IDOR")
&& urlParts[2].equals("profile")
&& urlParts[3].equals(authUserId)) {
UserProfile userProfile = new UserProfile(authUserId);
return success(this)
.feedback("idor.view.own.profile.success")
.output(userProfile.profileToMap().toString())
.build();
} else {
return failed(this).feedback("idor.view.own.profile.failure1").build();
}
} else {
return failed(this).feedback("idor.view.own.profile.failure2").build();
}
} catch (Exception ex) {
return failed(this).feedback("an error occurred with your request").build();
}
}
}

View File

@@ -25,112 +25,117 @@ package org.owasp.webgoat.lessons.idor;
import java.util.HashMap;
import java.util.Map;
/**
* Created by jason on 1/5/17.
*/
/** Created by jason on 1/5/17. */
public class UserProfile {
private String userId;
private String name;
private String color;
private String size;
private boolean isAdmin;
private int role;
private String userId;
private String name;
private String color;
private String size;
private boolean isAdmin;
private int role;
public UserProfile() {}
public UserProfile() {}
public UserProfile(String id) {
setProfileFromId(id);
public UserProfile(String id) {
setProfileFromId(id);
}
//
private void setProfileFromId(String id) {
// emulate look up from database
if (id.equals("2342384")) {
this.userId = id;
this.color = "yellow";
this.name = "Tom Cat";
this.size = "small";
this.isAdmin = false;
this.role = 3;
} else if (id.equals("2342388")) {
this.userId = id;
this.color = "brown";
this.name = "Buffalo Bill";
this.size = "large";
this.isAdmin = false;
this.role = 3;
} else {
// not found
}
}
//
private void setProfileFromId(String id) {
// emulate look up from database
if (id.equals("2342384")) {
this.userId = id;
this.color = "yellow";
this.name = "Tom Cat";
this.size = "small";
this.isAdmin = false;
this.role = 3;
} else if (id.equals("2342388")) {
this.userId = id;
this.color = "brown";
this.name = "Buffalo Bill";
this.size = "large";
this.isAdmin = false;
this.role = 3;
} else {
//not found
}
public Map<String, Object> profileToMap() {
Map<String, Object> profileMap = new HashMap<>();
profileMap.put("userId", this.userId);
profileMap.put("name", this.name);
profileMap.put("color", this.color);
profileMap.put("size", this.size);
profileMap.put("role", this.role);
return profileMap;
}
}
public String toHTMLString() {
String htmlBreak = "<br/>";
return "userId"
+ this.userId
+ htmlBreak
+ "name"
+ this.name
+ htmlBreak
+ "size"
+ this.size
+ htmlBreak
+ "role"
+ this.role
+ htmlBreak
+ "isAdmin"
+ this.isAdmin;
}
public Map <String,Object> profileToMap () {
Map<String,Object> profileMap = new HashMap<>();
profileMap.put("userId", this.userId);
profileMap.put("name", this.name);
profileMap.put("color", this.color);
profileMap.put("size", this.size);
profileMap.put("role", this.role);
return profileMap;
}
//
public String getUserId() {
return userId;
}
public String toHTMLString() {
String htmlBreak = "<br/>";
return "userId" + this.userId + htmlBreak +
"name" + this.name + htmlBreak +
"size" + this.size + htmlBreak +
"role" + this.role + htmlBreak +
"isAdmin" + this.isAdmin;
}
public void setUserId(String userId) {
this.userId = userId;
}
//
public String getUserId() {
return userId;
}
public String getName() {
return name;
}
public void setUserId(String userId) {
this.userId = userId;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String getColor() {
return color;
}
public void setName(String name) {
this.name = name;
}
public void setColor(String color) {
this.color = color;
}
public String getColor() {
return color;
}
public String getSize() {
return size;
}
public void setColor(String color) {
this.color = color;
}
public void setSize(String size) {
this.size = size;
}
public String getSize() {
return size;
}
public boolean isAdmin() {
return isAdmin;
}
public void setSize(String size) {
this.size = size;
}
public void setAdmin(boolean admin) {
isAdmin = admin;
}
public boolean isAdmin() {
return isAdmin;
}
public void setAdmin(boolean admin) {
isAdmin = admin;
}
public int getRole() {
return role;
}
public void setRole(int role) {
this.role = role;
}
public int getRole() {
return role;
}
public void setRole(int role) {
this.role = role;
}
}

View File

@@ -8,25 +8,26 @@ import org.springframework.stereotype.Component;
* ************************************************************************************************
* 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 - 2014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
*
* <p>Copyright (c) 2002 - 2014 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
*
* <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>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* <p>
*
* @author WebGoat
@@ -35,13 +36,13 @@ import org.springframework.stereotype.Component;
*/
@Component
public class InsecureLogin extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.A7;
}
@Override
public Category getDefaultCategory() {
return Category.A7;
}
@Override
public String getTitle() {
return "insecure-login.title";
}
@Override
public String getTitle() {
return "insecure-login.title";
}
}

View File

@@ -30,18 +30,18 @@ import org.springframework.web.bind.annotation.*;
@RestController
public class InsecureLoginTask extends AssignmentEndpoint {
@PostMapping("/InsecureLogin/task")
@ResponseBody
public AttackResult completed(@RequestParam String username, @RequestParam String password) {
if ("CaptainJack".equals(username) && "BlackPearl".equals(password)) {
return success(this).build();
}
return failed(this).build();
@PostMapping("/InsecureLogin/task")
@ResponseBody
public AttackResult completed(@RequestParam String username, @RequestParam String password) {
if ("CaptainJack".equals(username) && "BlackPearl".equals(password)) {
return success(this).build();
}
return failed(this).build();
}
@PostMapping("/InsecureLogin/login")
@ResponseStatus(HttpStatus.ACCEPTED)
public void login() {
//only need to exists as the JS needs to call an existing endpoint
}
@PostMapping("/InsecureLogin/login")
@ResponseStatus(HttpStatus.ACCEPTED)
public void login() {
// only need to exists as the JS needs to call an existing endpoint
}
}

View File

@@ -33,13 +33,13 @@ import org.springframework.stereotype.Component;
@Component
public class JWT extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.A7;
}
@Override
public Category getDefaultCategory() {
return Category.A7;
}
@Override
public String getTitle() {
return "jwt.title";
}
@Override
public String getTitle() {
return "jwt.title";
}
}

View File

@@ -10,13 +10,13 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
public class JWTDecodeEndpoint extends AssignmentEndpoint {
@PostMapping("/JWT/decode")
@ResponseBody
public AttackResult decode(@RequestParam("jwt-encode-user") String user) {
if ("user".equals(user)) {
return success(this).build();
} else {
return failed(this).build();
}
@PostMapping("/JWT/decode")
@ResponseBody
public AttackResult decode(@RequestParam("jwt-encode-user") String user) {
if ("user".equals(user)) {
return success(this).build();
} else {
return failed(this).build();
}
}
}

View File

@@ -24,6 +24,8 @@ package org.owasp.webgoat.lessons.jwt;
import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.TextCodec;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.commons.lang3.StringUtils;
import org.owasp.webgoat.container.LessonDataSource;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
@@ -31,10 +33,9 @@ import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.springframework.web.bind.annotation.*;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
*
*
* <pre>
* {
* "typ": "JWT",
@@ -59,64 +60,77 @@ import java.sql.SQLException;
* @since 4/23/17.
*/
@RestController
@AssignmentHints({"jwt-final-hint1", "jwt-final-hint2", "jwt-final-hint3", "jwt-final-hint4", "jwt-final-hint5", "jwt-final-hint6"})
@AssignmentHints({
"jwt-final-hint1",
"jwt-final-hint2",
"jwt-final-hint3",
"jwt-final-hint4",
"jwt-final-hint5",
"jwt-final-hint6"
})
public class JWTFinalEndpoint extends AssignmentEndpoint {
private final LessonDataSource dataSource;
private final LessonDataSource dataSource;
private JWTFinalEndpoint(LessonDataSource dataSource) {
this.dataSource = dataSource;
}
@PostMapping("/JWT/final/follow/{user}")
public @ResponseBody
String follow(@PathVariable("user") String user) {
if ("Jerry".equals(user)) {
return "Following yourself seems redundant";
} else {
return "You are now following Tom";
}
}
private JWTFinalEndpoint(LessonDataSource dataSource) {
this.dataSource = dataSource;
}
@PostMapping("/JWT/final/delete")
public @ResponseBody
AttackResult resetVotes(@RequestParam("token") String token) {
if (StringUtils.isEmpty(token)) {
return failed(this).feedback("jwt-invalid-token").build();
} else {
try {
final String[] errorMessage = {null};
Jwt jwt = Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() {
@Override
public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {
@PostMapping("/JWT/final/follow/{user}")
public @ResponseBody String follow(@PathVariable("user") String user) {
if ("Jerry".equals(user)) {
return "Following yourself seems redundant";
} else {
return "You are now following Tom";
}
}
@PostMapping("/JWT/final/delete")
public @ResponseBody AttackResult resetVotes(@RequestParam("token") String token) {
if (StringUtils.isEmpty(token)) {
return failed(this).feedback("jwt-invalid-token").build();
} else {
try {
final String[] errorMessage = {null};
Jwt jwt =
Jwts.parser()
.setSigningKeyResolver(
new SigningKeyResolverAdapter() {
@Override
public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {
final String kid = (String) header.get("kid");
try (var connection = dataSource.getConnection()) {
ResultSet rs = connection.createStatement().executeQuery("SELECT key FROM jwt_keys WHERE id = '" + kid + "'");
while (rs.next()) {
return TextCodec.BASE64.decode(rs.getString(1));
}
ResultSet rs =
connection
.createStatement()
.executeQuery(
"SELECT key FROM jwt_keys WHERE id = '" + kid + "'");
while (rs.next()) {
return TextCodec.BASE64.decode(rs.getString(1));
}
} catch (SQLException e) {
errorMessage[0] = e.getMessage();
errorMessage[0] = e.getMessage();
}
return null;
}
}).parseClaimsJws(token);
if (errorMessage[0] != null) {
return failed(this).output(errorMessage[0]).build();
}
Claims claims = (Claims) jwt.getBody();
String username = (String) claims.get("username");
if ("Jerry".equals(username)) {
return failed(this).feedback("jwt-final-jerry-account").build();
}
if ("Tom".equals(username)) {
return success(this).build();
} else {
return failed(this).feedback("jwt-final-not-tom").build();
}
} catch (JwtException e) {
return failed(this).feedback("jwt-invalid-token").output(e.toString()).build();
}
}
})
.parseClaimsJws(token);
if (errorMessage[0] != null) {
return failed(this).output(errorMessage[0]).build();
}
Claims claims = (Claims) jwt.getBody();
String username = (String) claims.get("username");
if ("Jerry".equals(username)) {
return failed(this).feedback("jwt-final-jerry-account").build();
}
if ("Tom".equals(username)) {
return success(this).build();
} else {
return failed(this).feedback("jwt-final-not-tom").build();
}
} catch (JwtException e) {
return failed(this).feedback("jwt-invalid-token").output(e.toString()).build();
}
}
}
}

View File

@@ -8,42 +8,41 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class JWTQuiz extends AssignmentEndpoint {
private final String[] solutions = {"Solution 1", "Solution 2"};
private final boolean[] guesses = new boolean[solutions.length];
private final String[] solutions = {"Solution 1", "Solution 2"};
private final boolean[] guesses = new boolean[solutions.length];
@PostMapping("/JWT/quiz")
@ResponseBody
public AttackResult completed(@RequestParam String[] question_0_solution, @RequestParam String[] question_1_solution) {
int correctAnswers = 0;
@PostMapping("/JWT/quiz")
@ResponseBody
public AttackResult completed(
@RequestParam String[] question_0_solution, @RequestParam String[] question_1_solution) {
int correctAnswers = 0;
String[] givenAnswers = {question_0_solution[0], question_1_solution[0]};
String[] givenAnswers = {question_0_solution[0], question_1_solution[0]};
for (int i = 0; i < solutions.length; i++) {
if (givenAnswers[i].contains(solutions[i])) {
// answer correct
correctAnswers++;
guesses[i] = true;
} else {
// answer incorrect
guesses[i] = false;
}
}
if (correctAnswers == solutions.length) {
return success(this).build();
} else {
return failed(this).build();
}
for (int i = 0; i < solutions.length; i++) {
if (givenAnswers[i].contains(solutions[i])) {
// answer correct
correctAnswers++;
guesses[i] = true;
} else {
// answer incorrect
guesses[i] = false;
}
}
@GetMapping("/JWT/quiz")
@ResponseBody
public boolean[] getResults() {
return this.guesses;
if (correctAnswers == solutions.length) {
return success(this).build();
} else {
return failed(this).build();
}
}
@GetMapping("/JWT/quiz")
@ResponseBody
public boolean[] getResults() {
return this.guesses;
}
}

View File

@@ -22,12 +22,20 @@
package org.owasp.webgoat.lessons.jwt;
import static org.springframework.http.ResponseEntity.ok;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.RandomStringUtils;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
@@ -41,106 +49,109 @@ import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static org.springframework.http.ResponseEntity.ok;
/**
* @author nbaars
* @since 4/23/17.
*/
@RestController
@AssignmentHints({"jwt-refresh-hint1", "jwt-refresh-hint2", "jwt-refresh-hint3", "jwt-refresh-hint4"})
@AssignmentHints({
"jwt-refresh-hint1",
"jwt-refresh-hint2",
"jwt-refresh-hint3",
"jwt-refresh-hint4"
})
public class JWTRefreshEndpoint extends AssignmentEndpoint {
public static final String PASSWORD = "bm5nhSkxCXZkKRy4";
private static final String JWT_PASSWORD = "bm5n3SkxCX4kKRy4";
private static final List<String> validRefreshTokens = new ArrayList<>();
public static final String PASSWORD = "bm5nhSkxCXZkKRy4";
private static final String JWT_PASSWORD = "bm5n3SkxCX4kKRy4";
private static final List<String> validRefreshTokens = new ArrayList<>();
@PostMapping(value = "/JWT/refresh/login", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity follow(@RequestBody(required = false) Map<String, Object> json) {
if (json == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
String user = (String) json.get("user");
String password = (String) json.get("password");
@PostMapping(
value = "/JWT/refresh/login",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity follow(@RequestBody(required = false) Map<String, Object> json) {
if (json == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
String user = (String) json.get("user");
String password = (String) json.get("password");
if ("Jerry".equalsIgnoreCase(user) && PASSWORD.equals(password)) {
return ok(createNewTokens(user));
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
if ("Jerry".equalsIgnoreCase(user) && PASSWORD.equals(password)) {
return ok(createNewTokens(user));
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
private Map<String, Object> createNewTokens(String user) {
Map<String, Object> claims = new HashMap<>();
claims.put("admin", "false");
claims.put("user", user);
String token =
Jwts.builder()
.setIssuedAt(new Date(System.currentTimeMillis() + TimeUnit.DAYS.toDays(10)))
.setClaims(claims)
.signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, JWT_PASSWORD)
.compact();
Map<String, Object> tokenJson = new HashMap<>();
String refreshToken = RandomStringUtils.randomAlphabetic(20);
validRefreshTokens.add(refreshToken);
tokenJson.put("access_token", token);
tokenJson.put("refresh_token", refreshToken);
return tokenJson;
}
@PostMapping("/JWT/refresh/checkout")
@ResponseBody
public ResponseEntity<AttackResult> checkout(
@RequestHeader(value = "Authorization", required = false) String token) {
if (token == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
try {
Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(token.replace("Bearer ", ""));
Claims claims = (Claims) jwt.getBody();
String user = (String) claims.get("user");
if ("Tom".equals(user)) {
return ok(success(this).build());
}
return ok(failed(this).feedback("jwt-refresh-not-tom").feedbackArgs(user).build());
} catch (ExpiredJwtException e) {
return ok(failed(this).output(e.getMessage()).build());
} catch (JwtException e) {
return ok(failed(this).feedback("jwt-invalid-token").build());
}
}
@PostMapping("/JWT/refresh/newToken")
@ResponseBody
public ResponseEntity newToken(
@RequestHeader(value = "Authorization", required = false) String token,
@RequestBody(required = false) Map<String, Object> json) {
if (token == null || json == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
private Map<String, Object> createNewTokens(String user) {
Map<String, Object> claims = new HashMap<>();
claims.put("admin", "false");
claims.put("user", user);
String token = Jwts.builder()
.setIssuedAt(new Date(System.currentTimeMillis() + TimeUnit.DAYS.toDays(10)))
.setClaims(claims)
.signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, JWT_PASSWORD)
.compact();
Map<String, Object> tokenJson = new HashMap<>();
String refreshToken = RandomStringUtils.randomAlphabetic(20);
validRefreshTokens.add(refreshToken);
tokenJson.put("access_token", token);
tokenJson.put("refresh_token", refreshToken);
return tokenJson;
String user;
String refreshToken;
try {
Jwt<Header, Claims> jwt =
Jwts.parser().setSigningKey(JWT_PASSWORD).parse(token.replace("Bearer ", ""));
user = (String) jwt.getBody().get("user");
refreshToken = (String) json.get("refresh_token");
} catch (ExpiredJwtException e) {
user = (String) e.getClaims().get("user");
refreshToken = (String) json.get("refresh_token");
}
@PostMapping("/JWT/refresh/checkout")
@ResponseBody
public ResponseEntity<AttackResult> checkout(@RequestHeader(value = "Authorization", required = false) String token) {
if (token == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
try {
Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(token.replace("Bearer ", ""));
Claims claims = (Claims) jwt.getBody();
String user = (String) claims.get("user");
if ("Tom".equals(user)) {
return ok(success(this).build());
}
return ok(failed(this).feedback("jwt-refresh-not-tom").feedbackArgs(user).build());
} catch (ExpiredJwtException e) {
return ok(failed(this).output(e.getMessage()).build());
} catch (JwtException e) {
return ok(failed(this).feedback("jwt-invalid-token").build());
}
}
@PostMapping("/JWT/refresh/newToken")
@ResponseBody
public ResponseEntity newToken(@RequestHeader(value = "Authorization", required = false) String token,
@RequestBody(required = false) Map<String, Object> json) {
if (token == null || json == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
String user;
String refreshToken;
try {
Jwt<Header, Claims> jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(token.replace("Bearer ", ""));
user = (String) jwt.getBody().get("user");
refreshToken = (String) json.get("refresh_token");
} catch (ExpiredJwtException e) {
user = (String) e.getClaims().get("user");
refreshToken = (String) json.get("refresh_token");
}
if (user == null || refreshToken == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} else if (validRefreshTokens.contains(refreshToken)) {
validRefreshTokens.remove(refreshToken);
return ok(createNewTokens(user));
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
if (user == null || refreshToken == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} else if (validRefreshTokens.contains(refreshToken)) {
validRefreshTokens.remove(refreshToken);
return ok(createNewTokens(user));
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
}

View File

@@ -27,6 +27,11 @@ import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.TextCodec;
import java.time.Instant;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Random;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
@@ -37,12 +42,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
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.Random;
/**
* @author nbaars
* @since 4/23/17.
@@ -51,45 +50,50 @@ import java.util.Random;
@AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"})
public class JWTSecretKeyEndpoint extends AssignmentEndpoint {
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 List<String> expectedClaims = List.of("iss", "iat", "exp", "aud", "sub", "username", "Email", "Role");
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 List<String> expectedClaims =
List.of("iss", "iat", "exp", "aud", "sub", "username", "Email", "Role");
@RequestMapping(path = "/JWT/secret/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();
}
@RequestMapping(path = "/JWT/secret/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")
@ResponseBody
public AttackResult login(@RequestParam String token) {
try {
Jwt jwt = Jwts.parser().setSigningKey(JWT_SECRET).parseClaimsJws(token);
Claims claims = (Claims) jwt.getBody();
if (!claims.keySet().containsAll(expectedClaims)) {
return failed(this).feedback("jwt-secret-claims-missing").build();
} else {
String user = (String) claims.get("username");
@PostMapping("/JWT/secret")
@ResponseBody
public AttackResult login(@RequestParam String token) {
try {
Jwt jwt = Jwts.parser().setSigningKey(JWT_SECRET).parseClaimsJws(token);
Claims claims = (Claims) jwt.getBody();
if (!claims.keySet().containsAll(expectedClaims)) {
return failed(this).feedback("jwt-secret-claims-missing").build();
} else {
String user = (String) claims.get("username");
if (WEBGOAT_USER.equalsIgnoreCase(user)) {
return success(this).build();
} else {
return failed(this).feedback("jwt-secret-incorrect-user").feedbackArgs(user).build();
}
}
} catch (Exception e) {
return failed(this).feedback("jwt-invalid-token").output(e.getMessage()).build();
if (WEBGOAT_USER.equalsIgnoreCase(user)) {
return success(this).build();
} else {
return failed(this).feedback("jwt-secret-incorrect-user").feedbackArgs(user).build();
}
}
} catch (Exception e) {
return failed(this).feedback("jwt-invalid-token").output(e.getMessage()).build();
}
}
}

View File

@@ -22,11 +22,23 @@
package org.owasp.webgoat.lessons.jwt;
import static java.util.Comparator.comparingLong;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.impl.TextCodec;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
@@ -46,139 +58,164 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import static java.util.Comparator.comparingLong;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;
/**
* @author nbaars
* @since 4/23/17.
*/
@RestController
@AssignmentHints({"jwt-change-token-hint1", "jwt-change-token-hint2", "jwt-change-token-hint3", "jwt-change-token-hint4", "jwt-change-token-hint5"})
@AssignmentHints({
"jwt-change-token-hint1",
"jwt-change-token-hint2",
"jwt-change-token-hint3",
"jwt-change-token-hint4",
"jwt-change-token-hint5"
})
public class JWTVotesEndpoint extends AssignmentEndpoint {
public static final String JWT_PASSWORD = TextCodec.BASE64.encode("victory");
private static String validUsers = "TomJerrySylvester";
public static final String JWT_PASSWORD = TextCodec.BASE64.encode("victory");
private static String validUsers = "TomJerrySylvester";
private static int totalVotes = 38929;
private Map<String, Vote> votes = new HashMap<>();
private static int totalVotes = 38929;
private Map<String, Vote> votes = new HashMap<>();
@PostConstruct
public void initVotes() {
votes.put("Admin lost password", new Vote("Admin lost password",
"In this challenge you will need to help the admin and find the password in order to login",
"challenge1-small.png", "challenge1.png", 36000, totalVotes));
votes.put("Vote for your favourite",
new Vote("Vote for your favourite",
"In this challenge ...",
"challenge5-small.png", "challenge5.png", 30000, totalVotes));
votes.put("Get it for free",
new Vote("Get it for free",
"The objective for this challenge is to buy a Samsung phone for free.",
"challenge2-small.png", "challenge2.png", 20000, totalVotes));
votes.put("Photo comments",
new Vote("Photo comments",
"n this challenge you can comment on the photo you will need to find the flag somewhere.",
"challenge3-small.png", "challenge3.png", 10000, totalVotes));
@PostConstruct
public void initVotes() {
votes.put(
"Admin lost password",
new Vote(
"Admin lost password",
"In this challenge you will need to help the admin and find the password in order to"
+ " login",
"challenge1-small.png",
"challenge1.png",
36000,
totalVotes));
votes.put(
"Vote for your favourite",
new Vote(
"Vote for your favourite",
"In this challenge ...",
"challenge5-small.png",
"challenge5.png",
30000,
totalVotes));
votes.put(
"Get it for free",
new Vote(
"Get it for free",
"The objective for this challenge is to buy a Samsung phone for free.",
"challenge2-small.png",
"challenge2.png",
20000,
totalVotes));
votes.put(
"Photo comments",
new Vote(
"Photo comments",
"n this challenge you can comment on the photo you will need to find the flag"
+ " somewhere.",
"challenge3-small.png",
"challenge3.png",
10000,
totalVotes));
}
@GetMapping("/JWT/votings/login")
public void login(@RequestParam("user") String user, HttpServletResponse response) {
if (validUsers.contains(user)) {
Claims claims = Jwts.claims().setIssuedAt(Date.from(Instant.now().plus(Duration.ofDays(10))));
claims.put("admin", "false");
claims.put("user", user);
String token =
Jwts.builder()
.setClaims(claims)
.signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, JWT_PASSWORD)
.compact();
Cookie cookie = new Cookie("access_token", token);
response.addCookie(cookie);
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
} else {
Cookie cookie = new Cookie("access_token", "");
response.addCookie(cookie);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
}
}
@GetMapping("/JWT/votings/login")
public void login(@RequestParam("user") String user, HttpServletResponse response) {
if (validUsers.contains(user)) {
Claims claims = Jwts.claims().setIssuedAt(Date.from(Instant.now().plus(Duration.ofDays(10))));
claims.put("admin", "false");
claims.put("user", user);
String token = Jwts.builder()
.setClaims(claims)
.signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, JWT_PASSWORD)
.compact();
Cookie cookie = new Cookie("access_token", token);
response.addCookie(cookie);
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
@GetMapping("/JWT/votings")
@ResponseBody
public MappingJacksonValue getVotes(
@CookieValue(value = "access_token", required = false) String accessToken) {
MappingJacksonValue value =
new MappingJacksonValue(
votes.values().stream()
.sorted(comparingLong(Vote::getAverage).reversed())
.collect(toList()));
if (StringUtils.isEmpty(accessToken)) {
value.setSerializationView(Views.GuestView.class);
} else {
try {
Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken);
Claims claims = (Claims) jwt.getBody();
String user = (String) claims.get("user");
if ("Guest".equals(user) || !validUsers.contains(user)) {
value.setSerializationView(Views.GuestView.class);
} else {
Cookie cookie = new Cookie("access_token", "");
response.addCookie(cookie);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
value.setSerializationView(Views.UserView.class);
}
} catch (JwtException e) {
value.setSerializationView(Views.GuestView.class);
}
}
return value;
}
@GetMapping("/JWT/votings")
@ResponseBody
public MappingJacksonValue getVotes(@CookieValue(value = "access_token", required = false) String accessToken) {
MappingJacksonValue value = new MappingJacksonValue(votes.values().stream().sorted(comparingLong(Vote::getAverage).reversed()).collect(toList()));
if (StringUtils.isEmpty(accessToken)) {
value.setSerializationView(Views.GuestView.class);
@PostMapping(value = "/JWT/votings/{title}")
@ResponseBody
@ResponseStatus(HttpStatus.ACCEPTED)
public ResponseEntity<?> vote(
@PathVariable String title,
@CookieValue(value = "access_token", required = false) String accessToken) {
if (StringUtils.isEmpty(accessToken)) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} else {
try {
Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken);
Claims claims = (Claims) jwt.getBody();
String user = (String) claims.get("user");
if (!validUsers.contains(user)) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} else {
try {
Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken);
Claims claims = (Claims) jwt.getBody();
String user = (String) claims.get("user");
if ("Guest".equals(user) || !validUsers.contains(user)) {
value.setSerializationView(Views.GuestView.class);
} else {
value.setSerializationView(Views.UserView.class);
}
} catch (JwtException e) {
value.setSerializationView(Views.GuestView.class);
}
ofNullable(votes.get(title)).ifPresent(v -> v.incrementNumberOfVotes(totalVotes));
return ResponseEntity.accepted().build();
}
return value;
} catch (JwtException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
}
@PostMapping(value = "/JWT/votings/{title}")
@ResponseBody
@ResponseStatus(HttpStatus.ACCEPTED)
public ResponseEntity<?> vote(@PathVariable String title, @CookieValue(value = "access_token", required = false) String accessToken) {
if (StringUtils.isEmpty(accessToken)) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
@PostMapping("/JWT/votings")
@ResponseBody
public AttackResult resetVotes(
@CookieValue(value = "access_token", required = false) String accessToken) {
if (StringUtils.isEmpty(accessToken)) {
return failed(this).feedback("jwt-invalid-token").build();
} else {
try {
Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken);
Claims claims = (Claims) jwt.getBody();
boolean isAdmin = Boolean.valueOf(String.valueOf(claims.get("admin")));
if (!isAdmin) {
return failed(this).feedback("jwt-only-admin").build();
} else {
try {
Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken);
Claims claims = (Claims) jwt.getBody();
String user = (String) claims.get("user");
if (!validUsers.contains(user)) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} else {
ofNullable(votes.get(title)).ifPresent(v -> v.incrementNumberOfVotes(totalVotes));
return ResponseEntity.accepted().build();
}
} catch (JwtException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
}
@PostMapping("/JWT/votings")
@ResponseBody
public AttackResult resetVotes(@CookieValue(value = "access_token", required = false) String accessToken) {
if (StringUtils.isEmpty(accessToken)) {
return failed(this).feedback("jwt-invalid-token").build();
} else {
try {
Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken);
Claims claims = (Claims) jwt.getBody();
boolean isAdmin = Boolean.valueOf(String.valueOf(claims.get("admin")));
if (!isAdmin) {
return failed(this).feedback("jwt-only-admin").build();
} else {
votes.values().forEach(vote -> vote.reset());
return success(this).build();
}
} catch (JwtException e) {
return failed(this).feedback("jwt-invalid-token").output(e.toString()).build();
}
votes.values().forEach(vote -> vote.reset());
return success(this).build();
}
} catch (JwtException e) {
return failed(this).feedback("jwt-invalid-token").output(e.toString()).build();
}
}
}
}

View File

@@ -5,9 +5,7 @@ package org.owasp.webgoat.lessons.jwt.votes;
* @since 4/30/17.
*/
public class Views {
public interface GuestView {
}
public interface GuestView {}
public interface UserView extends GuestView {
}
public interface UserView extends GuestView {}
}

View File

@@ -31,42 +31,53 @@ import lombok.Getter;
*/
@Getter
public class Vote {
@JsonView(Views.GuestView.class)
private final String title;
@JsonView(Views.GuestView.class)
private final String information;
@JsonView(Views.GuestView.class)
private final String imageSmall;
@JsonView(Views.GuestView.class)
private final String imageBig;
@JsonView(Views.UserView.class)
private int numberOfVotes;
@JsonView(Views.UserView.class)
private boolean votingAllowed = true;
@JsonView(Views.UserView.class)
private long average = 0;
@JsonView(Views.GuestView.class)
private final String title;
@JsonView(Views.GuestView.class)
private final String information;
public Vote(String title, String information, String imageSmall, String imageBig, int numberOfVotes, int totalVotes) {
this.title = title;
this.information = information;
this.imageSmall = imageSmall;
this.imageBig = imageBig;
this.numberOfVotes = numberOfVotes;
this.average = calculateStars(totalVotes);
}
@JsonView(Views.GuestView.class)
private final String imageSmall;
public void incrementNumberOfVotes(int totalVotes) {
this.numberOfVotes = this.numberOfVotes + 1;
this.average = calculateStars(totalVotes);
}
@JsonView(Views.GuestView.class)
private final String imageBig;
public void reset() {
this.numberOfVotes = 1;
this.average = 1;
}
@JsonView(Views.UserView.class)
private int numberOfVotes;
private long calculateStars(int totalVotes) {
return Math.round(((double) numberOfVotes / (double) totalVotes) * 4);
}
@JsonView(Views.UserView.class)
private boolean votingAllowed = true;
@JsonView(Views.UserView.class)
private long average = 0;
public Vote(
String title,
String information,
String imageSmall,
String imageBig,
int numberOfVotes,
int totalVotes) {
this.title = title;
this.information = information;
this.imageSmall = imageSmall;
this.imageBig = imageBig;
this.numberOfVotes = numberOfVotes;
this.average = calculateStars(totalVotes);
}
public void incrementNumberOfVotes(int totalVotes) {
this.numberOfVotes = this.numberOfVotes + 1;
this.average = calculateStars(totalVotes);
}
public void reset() {
this.numberOfVotes = 1;
this.average = 1;
}
private long calculateStars(int totalVotes) {
return Math.round(((double) numberOfVotes / (double) totalVotes) * 4);
}
}

View File

@@ -29,13 +29,13 @@ import org.springframework.stereotype.Component;
@Component
public class LessonTemplate extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.GENERAL;
}
@Override
public Category getDefaultCategory() {
return Category.GENERAL;
}
@Override
public String getTitle() {
return "lesson-template.title";
}
@Override
public String getTitle() {
return "lesson-template.title";
}
}

View File

@@ -22,6 +22,7 @@
package org.owasp.webgoat.lessons.lessontemplate;
import java.util.List;
import lombok.AllArgsConstructor;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
@@ -35,56 +36,56 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* Created by jason on 1/5/17.
*/
/** Created by jason on 1/5/17. */
@RestController
@AssignmentHints({"lesson-template.hints.1", "lesson-template.hints.2", "lesson-template.hints.3"})
public class SampleAttack extends AssignmentEndpoint {
String secretValue = "secr37Value";
String secretValue = "secr37Value";
//UserSessionData is bound to session and can be used to persist data across multiple assignments
@Autowired
UserSessionData userSessionData;
// UserSessionData is bound to session and can be used to persist data across multiple assignments
@Autowired UserSessionData userSessionData;
@PostMapping("/lesson-template/sample-attack")
@ResponseBody
public AttackResult completed(@RequestParam("param1") String param1, @RequestParam("param2") String param2) {
if (userSessionData.getValue("some-value") != null) {
// do any session updating you want here ... or not, just comment/example here
//return failed().feedback("lesson-template.sample-attack.failure-2").build());
}
//overly simple example for success. See other existing lesssons for ways to detect 'success' or 'failure'
if (secretValue.equals(param1)) {
return success(this)
.output("Custom Output ...if you want, for success")
.feedback("lesson-template.sample-attack.success")
.build();
//lesson-template.sample-attack.success is defined in src/main/resources/i18n/WebGoatLabels.properties
}
// else
return failed(this)
.feedback("lesson-template.sample-attack.failure-2")
.output("Custom output for this failure scenario, usually html that will get rendered directly ... yes, you can self-xss if you want")
.build();
@PostMapping("/lesson-template/sample-attack")
@ResponseBody
public AttackResult completed(
@RequestParam("param1") String param1, @RequestParam("param2") String param2) {
if (userSessionData.getValue("some-value") != null) {
// do any session updating you want here ... or not, just comment/example here
// return failed().feedback("lesson-template.sample-attack.failure-2").build());
}
@GetMapping("lesson-template/shop/{user}")
@ResponseBody
public List<Item> getItemsInBasket(@PathVariable("user") String user) {
return List.of(new Item("WG-1", "WebGoat promo", 12.0), new Item("WG-2", "WebGoat sticker", 0.00));
// overly simple example for success. See other existing lesssons for ways to detect 'success'
// or 'failure'
if (secretValue.equals(param1)) {
return success(this)
.output("Custom Output ...if you want, for success")
.feedback("lesson-template.sample-attack.success")
.build();
// lesson-template.sample-attack.success is defined in
// src/main/resources/i18n/WebGoatLabels.properties
}
@AllArgsConstructor
private class Item {
private String number;
private String description;
private double price;
}
// else
return failed(this)
.feedback("lesson-template.sample-attack.failure-2")
.output(
"Custom output for this failure scenario, usually html that will get rendered directly"
+ " ... yes, you can self-xss if you want")
.build();
}
@GetMapping("lesson-template/shop/{user}")
@ResponseBody
public List<Item> getItemsInBasket(@PathVariable("user") String user) {
return List.of(
new Item("WG-1", "WebGoat promo", 12.0), new Item("WG-2", "WebGoat sticker", 0.00));
}
@AllArgsConstructor
private class Item {
private String number;
private String description;
private double price;
}
}

View File

@@ -22,6 +22,10 @@
package org.owasp.webgoat.lessons.logging;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.UUID;
import javax.annotation.PostConstruct;
import org.apache.logging.log4j.util.Strings;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
@@ -32,35 +36,31 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.Base64;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
@RestController
public class LogBleedingTask extends AssignmentEndpoint {
Logger log = LoggerFactory.getLogger(this.getClass().getName());
private String password;
Logger log = LoggerFactory.getLogger(this.getClass().getName());
private String password;
@PostConstruct
public void generatePassword(){
password = UUID.randomUUID().toString();
log.info("Password for admin: {}", Base64.getEncoder().encodeToString(password.getBytes(StandardCharsets.UTF_8)));
@PostConstruct
public void generatePassword() {
password = UUID.randomUUID().toString();
log.info(
"Password for admin: {}",
Base64.getEncoder().encodeToString(password.getBytes(StandardCharsets.UTF_8)));
}
@PostMapping("/LogSpoofing/log-bleeding")
@ResponseBody
public AttackResult completed(@RequestParam String username, @RequestParam String password) {
if (Strings.isEmpty(username) || Strings.isEmpty(password)) {
return failed(this).output("Please provide username (Admin) and password").build();
}
@PostMapping("/LogSpoofing/log-bleeding")
@ResponseBody
public AttackResult completed(@RequestParam String username, @RequestParam String password) {
if (Strings.isEmpty(username) || Strings.isEmpty(password)) {
return failed(this).output("Please provide username (Admin) and password").build();
}
if (username.equals("Admin") && password.equals(this.password)) {
return success(this).build();
}
return failed(this).build();
if (username.equals("Admin") && password.equals(this.password)) {
return success(this).build();
}
return failed(this).build();
}
}

View File

@@ -8,25 +8,26 @@ import org.springframework.stereotype.Component;
* ************************************************************************************************
* 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 - 2014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
*
* <p>Copyright (c) 2002 - 2014 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
*
* <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>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* <p>
*
* @author WebGoat
@@ -35,13 +36,13 @@ import org.springframework.stereotype.Component;
*/
@Component
public class LogSpoofing extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.A9;
}
@Override
public Category getDefaultCategory() {
return Category.A9;
}
@Override
public String getTitle() {
return "logging.title";
}
@Override
public String getTitle() {
return "logging.title";
}
}

View File

@@ -33,19 +33,19 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
public class LogSpoofingTask extends AssignmentEndpoint {
@PostMapping("/LogSpoofing/log-spoofing")
@ResponseBody
public AttackResult completed(@RequestParam String username, @RequestParam String password) {
if (Strings.isEmpty(username)) {
return failed(this).output(username).build();
}
username = username.replace("\n", "<br/>");
if (username.contains("<p>") || username.contains("<div>")) {
return failed(this).output("Try to think of something simple ").build();
}
if (username.indexOf("<br/>") < username.indexOf("admin")) {
return success(this).output(username).build();
}
return failed(this).output(username).build();
@PostMapping("/LogSpoofing/log-spoofing")
@ResponseBody
public AttackResult completed(@RequestParam String username, @RequestParam String password) {
if (Strings.isEmpty(username)) {
return failed(this).output(username).build();
}
username = username.replace("\n", "<br/>");
if (username.contains("<p>") || username.contains("<div>")) {
return failed(this).output("Try to think of something simple ").build();
}
if (username.indexOf("<br/>") < username.indexOf("admin")) {
return success(this).output(username).build();
}
return failed(this).output(username).build();
}
}

View File

@@ -1,62 +1,62 @@
package org.owasp.webgoat.lessons.missingac;
import lombok.Getter;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
import lombok.Getter;
/**
* ************************************************************************************************
* 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 - 2014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
*
* <p>Copyright (c) 2002 - 2014 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
*
* <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>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* <p>
*/
@Getter
public class DisplayUser {
//intended to provide a display version of WebGoatUser for admins to view user attributes
// intended to provide a display version of WebGoatUser for admins to view user attributes
private String username;
private boolean admin;
private String userHash;
private String username;
private boolean admin;
private String userHash;
public DisplayUser(User user, String passwordSalt) {
this.username = user.getUsername();
this.admin = user.isAdmin();
public DisplayUser(User user, String passwordSalt) {
this.username = user.getUsername();
this.admin = user.isAdmin();
try {
this.userHash = genUserHash(user.getUsername(), user.getPassword(), passwordSalt);
} catch (Exception ex) {
this.userHash = "Error generating user hash";
}
}
protected String genUserHash(String username, String password, String passwordSalt) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
// salting is good, but static & too predictable ... short too for a salt
String salted = password + passwordSalt + username;
//md.update(salted.getBytes("UTF-8")); // Change this to "UTF-16" if needed
byte[] hash = md.digest(salted.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hash);
try {
this.userHash = genUserHash(user.getUsername(), user.getPassword(), passwordSalt);
} catch (Exception ex) {
this.userHash = "Error generating user hash";
}
}
protected String genUserHash(String username, String password, String passwordSalt)
throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
// salting is good, but static & too predictable ... short too for a salt
String salted = password + passwordSalt + username;
// md.update(salted.getBytes("UTF-8")); // Change this to "UTF-16" if needed
byte[] hash = md.digest(salted.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hash);
}
}

View File

@@ -1,5 +1,6 @@
package org.owasp.webgoat.lessons.missingac;
import java.util.List;
import org.owasp.webgoat.container.LessonDataSource;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
@@ -7,39 +8,42 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
@Component
public class MissingAccessControlUserRepository {
private final NamedParameterJdbcTemplate jdbcTemplate;
private final RowMapper<User> mapper = (rs, rowNum) -> new User(rs.getString("username"), rs.getString("password"), rs.getBoolean("admin"));
private final NamedParameterJdbcTemplate jdbcTemplate;
private final RowMapper<User> mapper =
(rs, rowNum) ->
new User(rs.getString("username"), rs.getString("password"), rs.getBoolean("admin"));
public MissingAccessControlUserRepository(LessonDataSource lessonDataSource) {
this.jdbcTemplate = new NamedParameterJdbcTemplate(lessonDataSource);
}
public List<User> findAllUsers() {
return jdbcTemplate.query("select username, password, admin from access_control_users", mapper);
}
public User findByUsername(String username) {
var users = jdbcTemplate.query("select username, password, admin from access_control_users where username=:username",
new MapSqlParameterSource().addValue("username", username),
mapper);
if (CollectionUtils.isEmpty(users)) {
return null;
}
return users.get(0);
}
public User save(User user) {
jdbcTemplate.update("INSERT INTO access_control_users(username, password, admin) VALUES(:username,:password,:admin)",
new MapSqlParameterSource()
.addValue("username", user.getUsername())
.addValue("password", user.getPassword())
.addValue("admin", user.isAdmin()));
return user;
public MissingAccessControlUserRepository(LessonDataSource lessonDataSource) {
this.jdbcTemplate = new NamedParameterJdbcTemplate(lessonDataSource);
}
public List<User> findAllUsers() {
return jdbcTemplate.query("select username, password, admin from access_control_users", mapper);
}
public User findByUsername(String username) {
var users =
jdbcTemplate.query(
"select username, password, admin from access_control_users where username=:username",
new MapSqlParameterSource().addValue("username", username),
mapper);
if (CollectionUtils.isEmpty(users)) {
return null;
}
return users.get(0);
}
public User save(User user) {
jdbcTemplate.update(
"INSERT INTO access_control_users(username, password, admin)"
+ " VALUES(:username,:password,:admin)",
new MapSqlParameterSource()
.addValue("username", user.getUsername())
.addValue("password", user.getPassword())
.addValue("admin", user.isAdmin()));
return user;
}
}

View File

@@ -29,16 +29,16 @@ import org.springframework.stereotype.Component;
@Component
public class MissingFunctionAC extends Lesson {
public static final String PASSWORD_SALT_SIMPLE = "DeliberatelyInsecure1234";
public static final String PASSWORD_SALT_ADMIN = "DeliberatelyInsecure1235";
public static final String PASSWORD_SALT_SIMPLE = "DeliberatelyInsecure1234";
public static final String PASSWORD_SALT_ADMIN = "DeliberatelyInsecure1235";
@Override
public Category getDefaultCategory() {
return Category.A1;
}
@Override
public Category getDefaultCategory() {
return Category.A1;
}
@Override
public String getTitle() {
return "missing-function-access-control.title";
}
@Override
public String getTitle() {
return "missing-function-access-control.title";
}
}

View File

@@ -29,33 +29,28 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by jason on 1/5/17.
*/
/** Created by jason on 1/5/17. */
@RestController
@AssignmentHints({"access-control.hidden-menus.hint1","access-control.hidden-menus.hint2","access-control.hidden-menus.hint3"})
@AssignmentHints({
"access-control.hidden-menus.hint1",
"access-control.hidden-menus.hint2",
"access-control.hidden-menus.hint3"
})
public class MissingFunctionACHiddenMenus extends AssignmentEndpoint {
@PostMapping(path = "/access-control/hidden-menu", produces = {"application/json"})
@ResponseBody
public AttackResult completed(String hiddenMenu1, String hiddenMenu2) {
if (hiddenMenu1.equals("Users") && hiddenMenu2.equals("Config")) {
return success(this)
.output("")
.feedback("access-control.hidden-menus.success")
.build();
}
if (hiddenMenu1.equals("Config") && hiddenMenu2.equals("Users")) {
return failed(this)
.output("")
.feedback("access-control.hidden-menus.close")
.build();
}
return failed(this)
.feedback("access-control.hidden-menus.failure")
.output("")
.build();
@PostMapping(
path = "/access-control/hidden-menu",
produces = {"application/json"})
@ResponseBody
public AttackResult completed(String hiddenMenu1, String hiddenMenu2) {
if (hiddenMenu1.equals("Users") && hiddenMenu2.equals("Config")) {
return success(this).output("").feedback("access-control.hidden-menus.success").build();
}
if (hiddenMenu1.equals("Config") && hiddenMenu2.equals("Users")) {
return failed(this).output("").feedback("access-control.hidden-menus.close").build();
}
return failed(this).feedback("access-control.hidden-menus.failure").output("").build();
}
}

View File

@@ -22,6 +22,12 @@
package org.owasp.webgoat.lessons.missingac;
import static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_ADMIN;
import static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_SIMPLE;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.container.session.WebSession;
@@ -34,70 +40,75 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_ADMIN;
import static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_SIMPLE;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* Created by jason on 1/5/17.
*/
/** Created by jason on 1/5/17. */
@Controller
@AllArgsConstructor
@Slf4j
public class MissingFunctionACUsers {
private final MissingAccessControlUserRepository userRepository;
private final WebSession webSession;
private final MissingAccessControlUserRepository userRepository;
private final WebSession webSession;
@GetMapping(path = {"access-control/users"})
public ModelAndView listUsers() {
@GetMapping(path = {"access-control/users"})
public ModelAndView listUsers() {
ModelAndView model = new ModelAndView();
model.setViewName("list_users");
List<User> allUsers = userRepository.findAllUsers();
model.addObject("numUsers", allUsers.size());
//add display user objects in place of direct users
List<DisplayUser> displayUsers = new ArrayList<>();
for (User user : allUsers) {
displayUsers.add(new DisplayUser(user, PASSWORD_SALT_SIMPLE));
}
model.addObject("allUsers", displayUsers);
ModelAndView model = new ModelAndView();
model.setViewName("list_users");
List<User> allUsers = userRepository.findAllUsers();
model.addObject("numUsers", allUsers.size());
// add display user objects in place of direct users
List<DisplayUser> displayUsers = new ArrayList<>();
for (User user : allUsers) {
displayUsers.add(new DisplayUser(user, PASSWORD_SALT_SIMPLE));
}
model.addObject("allUsers", displayUsers);
return model;
return model;
}
@GetMapping(
path = {"access-control/users"},
consumes = "application/json")
@ResponseBody
public ResponseEntity<List<DisplayUser>> usersService() {
return ResponseEntity.ok(
userRepository.findAllUsers().stream()
.map(user -> new DisplayUser(user, PASSWORD_SALT_SIMPLE))
.collect(Collectors.toList()));
}
@GetMapping(
path = {"access-control/users-admin-fix"},
consumes = "application/json")
@ResponseBody
public ResponseEntity<List<DisplayUser>> usersFixed() {
var currentUser = userRepository.findByUsername(webSession.getUserName());
if (currentUser != null && currentUser.isAdmin()) {
return ResponseEntity.ok(
userRepository.findAllUsers().stream()
.map(user -> new DisplayUser(user, PASSWORD_SALT_ADMIN))
.collect(Collectors.toList()));
}
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
@PostMapping(
path = {"access-control/users", "access-control/users-admin-fix"},
consumes = "application/json",
produces = "application/json")
@ResponseBody
public User addUser(@RequestBody User newUser) {
try {
userRepository.save(newUser);
return newUser;
} catch (Exception ex) {
log.error("Error creating new User", ex);
return null;
}
@GetMapping(path = {"access-control/users"}, consumes = "application/json")
@ResponseBody
public ResponseEntity<List<DisplayUser>> usersService() {
return ResponseEntity.ok(userRepository.findAllUsers().stream().map(user -> new DisplayUser(user, PASSWORD_SALT_SIMPLE)).collect(Collectors.toList()));
}
// @RequestMapping(path = {"user/{username}","/"}, method = RequestMethod.DELETE, consumes =
// "application/json", produces = "application/json")
// TODO implement delete method with id param and authorization
@GetMapping(path = {"access-control/users-admin-fix"}, consumes = "application/json")
@ResponseBody
public ResponseEntity<List<DisplayUser>> usersFixed() {
var currentUser = userRepository.findByUsername(webSession.getUserName());
if (currentUser != null && currentUser.isAdmin()) {
return ResponseEntity.ok(userRepository.findAllUsers().stream().map(user -> new DisplayUser(user, PASSWORD_SALT_ADMIN)).collect(Collectors.toList()));
}
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
@PostMapping(path = {"access-control/users", "access-control/users-admin-fix"}, consumes = "application/json", produces = "application/json")
@ResponseBody
public User addUser(@RequestBody User newUser) {
try {
userRepository.save(newUser);
return newUser;
} catch (Exception ex) {
log.error("Error creating new User", ex);
return null;
}
//@RequestMapping(path = {"user/{username}","/"}, method = RequestMethod.DELETE, consumes = "application/json", produces = "application/json")
//TODO implement delete method with id param and authorization
}
}
}

View File

@@ -22,10 +22,9 @@
package org.owasp.webgoat.lessons.missingac;
import lombok.RequiredArgsConstructor;
import static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_SIMPLE;
import lombok.RequiredArgsConstructor;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
@@ -34,22 +33,29 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@AssignmentHints({"access-control.hash.hint1", "access-control.hash.hint2", "access-control.hash.hint3", "access-control.hash.hint4", "access-control.hash.hint5"})
@AssignmentHints({
"access-control.hash.hint1",
"access-control.hash.hint2",
"access-control.hash.hint3",
"access-control.hash.hint4",
"access-control.hash.hint5"
})
@RequiredArgsConstructor
public class MissingFunctionACYourHash extends AssignmentEndpoint {
private final MissingAccessControlUserRepository userRepository;
private final MissingAccessControlUserRepository userRepository;
@PostMapping(path = "/access-control/user-hash", produces = {"application/json"})
@ResponseBody
public AttackResult simple(String userHash) {
User user = userRepository.findByUsername("Jerry");
DisplayUser displayUser = new DisplayUser(user, PASSWORD_SALT_SIMPLE);
if (userHash.equals(displayUser.getUserHash())) {
return success(this).feedback("access-control.hash.success").build();
} else {
return failed(this).build();
}
@PostMapping(
path = "/access-control/user-hash",
produces = {"application/json"})
@ResponseBody
public AttackResult simple(String userHash) {
User user = userRepository.findByUsername("Jerry");
DisplayUser displayUser = new DisplayUser(user, PASSWORD_SALT_SIMPLE);
if (userHash.equals(displayUser.getUserHash())) {
return success(this).feedback("access-control.hash.success").build();
} else {
return failed(this).build();
}
}
}

View File

@@ -32,29 +32,37 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@AssignmentHints({"access-control.hash.hint6", "access-control.hash.hint7",
"access-control.hash.hint8", "access-control.hash.hint9", "access-control.hash.hint10", "access-control.hash.hint11", "access-control.hash.hint12"})
@AssignmentHints({
"access-control.hash.hint6",
"access-control.hash.hint7",
"access-control.hash.hint8",
"access-control.hash.hint9",
"access-control.hash.hint10",
"access-control.hash.hint11",
"access-control.hash.hint12"
})
public class MissingFunctionACYourHashAdmin extends AssignmentEndpoint {
private final MissingAccessControlUserRepository userRepository;
private final MissingAccessControlUserRepository userRepository;
public MissingFunctionACYourHashAdmin(MissingAccessControlUserRepository userRepository) {
this.userRepository = userRepository;
public MissingFunctionACYourHashAdmin(MissingAccessControlUserRepository userRepository) {
this.userRepository = userRepository;
}
@PostMapping(
path = "/access-control/user-hash-fix",
produces = {"application/json"})
@ResponseBody
public AttackResult admin(String userHash) {
// current user should be in the DB
// if not admin then return 403
var user = userRepository.findByUsername("Jerry");
var displayUser = new DisplayUser(user, PASSWORD_SALT_ADMIN);
if (userHash.equals(displayUser.getUserHash())) {
return success(this).feedback("access-control.hash.success").build();
} else {
return failed(this).feedback("access-control.hash.close").build();
}
@PostMapping(path = "/access-control/user-hash-fix", produces = {"application/json"})
@ResponseBody
public AttackResult admin(String userHash) {
//current user should be in the DB
//if not admin then return 403
var user = userRepository.findByUsername("Jerry");
var displayUser = new DisplayUser(user, PASSWORD_SALT_ADMIN);
if (userHash.equals(displayUser.getUserHash())) {
return success(this).feedback("access-control.hash.success").build();
} else {
return failed(this).feedback("access-control.hash.close").build();
}
}
}
}

View File

@@ -9,7 +9,7 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
public class User {
private String username;
private String password;
private boolean admin;
private String username;
private String password;
private boolean admin;
}

View File

@@ -28,13 +28,13 @@ import org.springframework.stereotype.Component;
@Component
public class PasswordReset extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.A7;
}
@Override
public Category getDefaultCategory() {
return Category.A7;
}
@Override
public String getTitle() {
return "password-reset.title";
}
@Override
public String getTitle() {
return "password-reset.title";
}
}

View File

@@ -22,19 +22,18 @@
package org.owasp.webgoat.lessons.passwordreset;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Builder;
import lombok.Data;
@Builder
@Data
public class PasswordResetEmail implements Serializable {
private LocalDateTime time;
private String contents;
private String sender;
private String title;
private String recipient;
private LocalDateTime time;
private String contents;
private String sender;
private String title;
private String recipient;
}

View File

@@ -22,6 +22,8 @@
package org.owasp.webgoat.lessons.passwordreset;
import java.util.HashMap;
import java.util.Map;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.springframework.http.MediaType;
@@ -30,9 +32,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* @author nbaars
* @since 8/20/17.
@@ -40,32 +39,37 @@ import java.util.Map;
@RestController
public class QuestionsAssignment extends AssignmentEndpoint {
private static final Map<String, String> COLORS = new HashMap<>();
private static final Map<String, String> COLORS = new HashMap<>();
static {
COLORS.put("admin", "green");
COLORS.put("jerry", "orange");
COLORS.put("tom", "purple");
COLORS.put("larry", "yellow");
COLORS.put("webgoat", "red");
static {
COLORS.put("admin", "green");
COLORS.put("jerry", "orange");
COLORS.put("tom", "purple");
COLORS.put("larry", "yellow");
COLORS.put("webgoat", "red");
}
@PostMapping(
path = "/PasswordReset/questions",
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseBody
public AttackResult passwordReset(@RequestParam Map<String, Object> json) {
String securityQuestion = (String) json.getOrDefault("securityQuestion", "");
String username = (String) json.getOrDefault("username", "");
if ("webgoat".equalsIgnoreCase(username.toLowerCase())) {
return failed(this).feedback("password-questions-wrong-user").build();
}
@PostMapping(path = "/PasswordReset/questions", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseBody
public AttackResult passwordReset(@RequestParam Map<String, Object> json) {
String securityQuestion = (String) json.getOrDefault("securityQuestion", "");
String username = (String) json.getOrDefault("username", "");
if ("webgoat".equalsIgnoreCase(username.toLowerCase())) {
return failed(this).feedback("password-questions-wrong-user").build();
}
String validAnswer = COLORS.get(username.toLowerCase());
if (validAnswer == null) {
return failed(this).feedback("password-questions-unknown-user").feedbackArgs(username).build();
} else if (validAnswer.equals(securityQuestion)) {
return success(this).build();
}
return failed(this).build();
String validAnswer = COLORS.get(username.toLowerCase());
if (validAnswer == null) {
return failed(this)
.feedback("password-questions-unknown-user")
.feedbackArgs(username)
.build();
} else if (validAnswer.equals(securityQuestion)) {
return success(this).build();
}
return failed(this).build();
}
}

View File

@@ -23,6 +23,10 @@
package org.owasp.webgoat.lessons.passwordreset;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
@@ -38,94 +42,100 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author nbaars
* @since 8/20/17.
*/
@RestController
@AssignmentHints({"password-reset-hint1", "password-reset-hint2", "password-reset-hint3", "password-reset-hint4", "password-reset-hint5", "password-reset-hint6"})
@AssignmentHints({
"password-reset-hint1",
"password-reset-hint2",
"password-reset-hint3",
"password-reset-hint4",
"password-reset-hint5",
"password-reset-hint6"
})
public class ResetLinkAssignment extends AssignmentEndpoint {
static final String PASSWORD_TOM_9 = "somethingVeryRandomWhichNoOneWillEverTypeInAsPasswordForTom";
static final String TOM_EMAIL = "tom@webgoat-cloud.org";
static Map<String, String> userToTomResetLink = new HashMap<>();
static Map<String, String> usersToTomPassword = Maps.newHashMap();
static List<String> resetLinks = new ArrayList<>();
static final String PASSWORD_TOM_9 =
"somethingVeryRandomWhichNoOneWillEverTypeInAsPasswordForTom";
static final String TOM_EMAIL = "tom@webgoat-cloud.org";
static Map<String, String> userToTomResetLink = new HashMap<>();
static Map<String, String> usersToTomPassword = Maps.newHashMap();
static List<String> resetLinks = new ArrayList<>();
static final String TEMPLATE = "Hi, you requested a password reset link, please use this "
+ "<a target='_blank' href='http://%s/WebGoat/PasswordReset/reset/reset-password/%s'>link</a> to reset your password."
+ "\n \n\n"
+ "If you did not request this password change you can ignore this message."
+ "\n"
+ "If you have any comments or questions, please do not hesitate to reach us at support@webgoat-cloud.org"
+ "\n\n"
+ "Kind regards, \nTeam WebGoat";
static final String TEMPLATE =
"Hi, you requested a password reset link, please use this <a target='_blank'"
+ " href='http://%s/WebGoat/PasswordReset/reset/reset-password/%s'>link</a> to reset your"
+ " password.\n"
+ " \n\n"
+ "If you did not request this password change you can ignore this message.\n"
+ "If you have any comments or questions, please do not hesitate to reach us at"
+ " support@webgoat-cloud.org\n\n"
+ "Kind regards, \n"
+ "Team WebGoat";
@PostMapping("/PasswordReset/reset/login")
@ResponseBody
public AttackResult login(@RequestParam String password, @RequestParam String email) {
if (TOM_EMAIL.equals(email)) {
String passwordTom = usersToTomPassword.getOrDefault(getWebSession().getUserName(), PASSWORD_TOM_9);
if (passwordTom.equals(PASSWORD_TOM_9)) {
return failed(this).feedback("login_failed").build();
} else if (passwordTom.equals(password)) {
return success(this).build();
}
}
return failed(this).feedback("login_failed.tom").build();
@PostMapping("/PasswordReset/reset/login")
@ResponseBody
public AttackResult login(@RequestParam String password, @RequestParam String email) {
if (TOM_EMAIL.equals(email)) {
String passwordTom =
usersToTomPassword.getOrDefault(getWebSession().getUserName(), PASSWORD_TOM_9);
if (passwordTom.equals(PASSWORD_TOM_9)) {
return failed(this).feedback("login_failed").build();
} else if (passwordTom.equals(password)) {
return success(this).build();
}
}
return failed(this).feedback("login_failed.tom").build();
}
@GetMapping("/PasswordReset/reset/reset-password/{link}")
public ModelAndView resetPassword(@PathVariable(value = "link") String link, Model model) {
ModelAndView modelAndView = new ModelAndView();
if (ResetLinkAssignment.resetLinks.contains(link)) {
PasswordChangeForm form = new PasswordChangeForm();
form.setResetLink(link);
model.addAttribute("form", form);
modelAndView.addObject("form", form);
modelAndView.setViewName("password_reset"); //Display html page for changing password
} else {
modelAndView.setViewName("password_link_not_found");
}
return modelAndView;
@GetMapping("/PasswordReset/reset/reset-password/{link}")
public ModelAndView resetPassword(@PathVariable(value = "link") String link, Model model) {
ModelAndView modelAndView = new ModelAndView();
if (ResetLinkAssignment.resetLinks.contains(link)) {
PasswordChangeForm form = new PasswordChangeForm();
form.setResetLink(link);
model.addAttribute("form", form);
modelAndView.addObject("form", form);
modelAndView.setViewName("password_reset"); // Display html page for changing password
} else {
modelAndView.setViewName("password_link_not_found");
}
return modelAndView;
}
@GetMapping("/PasswordReset/reset/change-password")
public ModelAndView illegalCall() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("password_link_not_found");
return modelAndView;
}
@PostMapping("/PasswordReset/reset/change-password")
public ModelAndView changePassword(@ModelAttribute("form") PasswordChangeForm form, BindingResult bindingResult) {
ModelAndView modelAndView = new ModelAndView();
if (!org.springframework.util.StringUtils.hasText(form.getPassword())) {
bindingResult.rejectValue("password", "not.empty");
}
if (bindingResult.hasErrors()) {
modelAndView.setViewName("password_reset");
return modelAndView;
}
if (!resetLinks.contains(form.getResetLink())) {
modelAndView.setViewName("password_link_not_found");
return modelAndView;
}
if (checkIfLinkIsFromTom(form.getResetLink())) {
usersToTomPassword.put(getWebSession().getUserName(), form.getPassword());
}
modelAndView.setViewName("lessons/passwordreset/templates/success.html");
return modelAndView;
}
@GetMapping("/PasswordReset/reset/change-password")
public ModelAndView illegalCall() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("password_link_not_found");
return modelAndView;
}
private boolean checkIfLinkIsFromTom(String resetLinkFromForm) {
String resetLink = userToTomResetLink.getOrDefault(getWebSession().getUserName(), "unknown");
return resetLink.equals(resetLinkFromForm);
@PostMapping("/PasswordReset/reset/change-password")
public ModelAndView changePassword(
@ModelAttribute("form") PasswordChangeForm form, BindingResult bindingResult) {
ModelAndView modelAndView = new ModelAndView();
if (!org.springframework.util.StringUtils.hasText(form.getPassword())) {
bindingResult.rejectValue("password", "not.empty");
}
if (bindingResult.hasErrors()) {
modelAndView.setViewName("password_reset");
return modelAndView;
}
if (!resetLinks.contains(form.getResetLink())) {
modelAndView.setViewName("password_link_not_found");
return modelAndView;
}
if (checkIfLinkIsFromTom(form.getResetLink())) {
usersToTomPassword.put(getWebSession().getUserName(), form.getPassword());
}
modelAndView.setViewName("lessons/passwordreset/templates/success.html");
return modelAndView;
}
private boolean checkIfLinkIsFromTom(String resetLinkFromForm) {
String resetLink = userToTomResetLink.getOrDefault(getWebSession().getUserName(), "unknown");
return resetLink.equals(resetLinkFromForm);
}
}

View File

@@ -22,6 +22,8 @@
package org.owasp.webgoat.lessons.passwordreset;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.springframework.beans.factory.annotation.Value;
@@ -34,9 +36,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import java.util.UUID;
/**
* Part of the password reset assignment. Used to send the e-mail.
*
@@ -46,59 +45,70 @@ import java.util.UUID;
@RestController
public class ResetLinkAssignmentForgotPassword extends AssignmentEndpoint {
private final RestTemplate restTemplate;
private String webWolfHost;
private String webWolfPort;
private final String webWolfMailURL;
private final RestTemplate restTemplate;
private String webWolfHost;
private String webWolfPort;
private final String webWolfMailURL;
public ResetLinkAssignmentForgotPassword(RestTemplate restTemplate,
@Value("${webwolf.host}") String webWolfHost,
@Value("${webwolf.port}") String webWolfPort,
@Value("${webwolf.mail.url}") String webWolfMailURL) {
this.restTemplate = restTemplate;
this.webWolfHost = webWolfHost;
this.webWolfPort = webWolfPort;
this.webWolfMailURL = webWolfMailURL;
public ResetLinkAssignmentForgotPassword(
RestTemplate restTemplate,
@Value("${webwolf.host}") String webWolfHost,
@Value("${webwolf.port}") String webWolfPort,
@Value("${webwolf.mail.url}") String webWolfMailURL) {
this.restTemplate = restTemplate;
this.webWolfHost = webWolfHost;
this.webWolfPort = webWolfPort;
this.webWolfMailURL = webWolfMailURL;
}
@PostMapping("/PasswordReset/ForgotPassword/create-password-reset-link")
@ResponseBody
public AttackResult sendPasswordResetLink(
@RequestParam String email, HttpServletRequest request) {
String resetLink = UUID.randomUUID().toString();
ResetLinkAssignment.resetLinks.add(resetLink);
String host = request.getHeader("host");
if (ResetLinkAssignment.TOM_EMAIL.equals(email)
&& (host.contains(webWolfPort)
|| host.contains(webWolfHost))) { // User indeed changed the host header.
ResetLinkAssignment.userToTomResetLink.put(getWebSession().getUserName(), resetLink);
fakeClickingLinkEmail(host, resetLink);
} else {
try {
sendMailToUser(email, host, resetLink);
} catch (Exception e) {
return failed(this).output("E-mail can't be send. please try again.").build();
}
}
@PostMapping("/PasswordReset/ForgotPassword/create-password-reset-link")
@ResponseBody
public AttackResult sendPasswordResetLink(@RequestParam String email, HttpServletRequest request) {
String resetLink = UUID.randomUUID().toString();
ResetLinkAssignment.resetLinks.add(resetLink);
String host = request.getHeader("host");
if (ResetLinkAssignment.TOM_EMAIL.equals(email) && (host.contains(webWolfPort) || host.contains(webWolfHost))) { //User indeed changed the host header.
ResetLinkAssignment.userToTomResetLink.put(getWebSession().getUserName(), resetLink);
fakeClickingLinkEmail(host, resetLink);
} else {
try {
sendMailToUser(email, host, resetLink);
} catch (Exception e) {
return failed(this).output("E-mail can't be send. please try again.").build();
}
}
return success(this).feedback("email.send").feedbackArgs(email).build();
}
return success(this).feedback("email.send").feedbackArgs(email).build();
}
private void sendMailToUser(String email, String host, String resetLink) {
int index = email.indexOf("@");
String username = email.substring(0, index == -1 ? email.length() : index);
PasswordResetEmail mail =
PasswordResetEmail.builder()
.title("Your password reset link")
.contents(String.format(ResetLinkAssignment.TEMPLATE, host, resetLink))
.sender("password-reset@webgoat-cloud.net")
.recipient(username)
.build();
this.restTemplate.postForEntity(webWolfMailURL, mail, Object.class);
}
private void sendMailToUser(String email, String host, String resetLink) {
int index = email.indexOf("@");
String username = email.substring(0, index == -1 ? email.length() : index);
PasswordResetEmail mail = PasswordResetEmail.builder()
.title("Your password reset link")
.contents(String.format(ResetLinkAssignment.TEMPLATE, host, resetLink))
.sender("password-reset@webgoat-cloud.net")
.recipient(username).build();
this.restTemplate.postForEntity(webWolfMailURL, mail, Object.class);
}
private void fakeClickingLinkEmail(String host, String resetLink) {
try {
HttpHeaders httpHeaders = new HttpHeaders();
HttpEntity httpEntity = new HttpEntity(httpHeaders);
new RestTemplate().exchange(String.format("http://%s/PasswordReset/reset/reset-password/%s", host, resetLink), HttpMethod.GET, httpEntity, Void.class);
} catch (Exception e) {
//don't care
}
private void fakeClickingLinkEmail(String host, String resetLink) {
try {
HttpHeaders httpHeaders = new HttpHeaders();
HttpEntity httpEntity = new HttpEntity(httpHeaders);
new RestTemplate()
.exchange(
String.format("http://%s/PasswordReset/reset/reset-password/%s", host, resetLink),
HttpMethod.GET,
httpEntity,
Void.class);
} catch (Exception e) {
// don't care
}
}
}

View File

@@ -22,6 +22,10 @@
package org.owasp.webgoat.lessons.passwordreset;
import static java.util.Optional.of;
import java.util.HashMap;
import java.util.Map;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.springframework.beans.factory.annotation.Autowired;
@@ -30,11 +34,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
import static java.util.Optional.of;
/**
* Assignment for picking a good security question.
*
@@ -44,42 +43,66 @@ import static java.util.Optional.of;
@RestController
public class SecurityQuestionAssignment extends AssignmentEndpoint {
@Autowired
private TriedQuestions triedQuestions;
@Autowired private TriedQuestions triedQuestions;
private static Map<String, String> questions;
private static Map<String, String> questions;
static {
questions = new HashMap<>();
questions.put("What is your favorite animal?", "The answer can easily be guessed and figured out through social media.");
questions.put("In what year was your mother born?", "Can be easily guessed.");
questions.put("What was the time you were born?", "This may first seem like a good question, but you most likely dont know the exact time, so it might be hard to remember.");
questions.put("What is the name of the person you first kissed?", "Can be figured out through social media, or even guessed by trying the most common names.");
questions.put("What was the house number and street name you lived in as a child?", "Answer can be figured out through social media, or worse it might be your current address.");
questions.put("In what town or city was your first full time job?", "In times of LinkedIn and Facebook, the answer can be figured out quite easily.");
questions.put("In what city were you born?", "Easy to figure out through social media.");
questions.put("What was the last name of your favorite teacher in grade three?", "Most people would probably not know the answer to that.");
questions.put("What is the name of a college/job you applied to but didn't attend?", "It might not be easy to remember and an hacker could just try some company's/colleges in your area.");
questions.put("What are the last 5 digits of your drivers license?", "Is subject to change, and the last digit of your driver license might follow a specific pattern. (For example your birthday).");
questions.put("What was your childhood nickname?", "Not all people had a nickname.");
questions.put("Who was your childhood hero?", "Most Heroes we had as a child where quite obvious ones, like Superman for example.");
questions.put("On which wrist do you wear your watch?", "There are only to possible real answers, so really easy to guess.");
questions.put("What is your favorite color?", "Can easily be guessed.");
}
@PostMapping("/PasswordReset/SecurityQuestions")
@ResponseBody
public AttackResult completed(@RequestParam String question) {
var answer = of(questions.get(question));
if (answer.isPresent()) {
triedQuestions.incr(question);
if (triedQuestions.isComplete()) {
return success(this).output("<b>" + answer + "</b>").build();
}
}
return informationMessage(this)
.feedback("password-questions-one-successful")
.output(answer.orElse("Unknown question, please try again..."))
.build();
static {
questions = new HashMap<>();
questions.put(
"What is your favorite animal?",
"The answer can easily be guessed and figured out through social media.");
questions.put("In what year was your mother born?", "Can be easily guessed.");
questions.put(
"What was the time you were born?",
"This may first seem like a good question, but you most likely dont know the exact time, so"
+ " it might be hard to remember.");
questions.put(
"What is the name of the person you first kissed?",
"Can be figured out through social media, or even guessed by trying the most common"
+ " names.");
questions.put(
"What was the house number and street name you lived in as a child?",
"Answer can be figured out through social media, or worse it might be your current"
+ " address.");
questions.put(
"In what town or city was your first full time job?",
"In times of LinkedIn and Facebook, the answer can be figured out quite easily.");
questions.put("In what city were you born?", "Easy to figure out through social media.");
questions.put(
"What was the last name of your favorite teacher in grade three?",
"Most people would probably not know the answer to that.");
questions.put(
"What is the name of a college/job you applied to but didn't attend?",
"It might not be easy to remember and an hacker could just try some company's/colleges in"
+ " your area.");
questions.put(
"What are the last 5 digits of your drivers license?",
"Is subject to change, and the last digit of your driver license might follow a specific"
+ " pattern. (For example your birthday).");
questions.put("What was your childhood nickname?", "Not all people had a nickname.");
questions.put(
"Who was your childhood hero?",
"Most Heroes we had as a child where quite obvious ones, like Superman for example.");
questions.put(
"On which wrist do you wear your watch?",
"There are only to possible real answers, so really easy to guess.");
questions.put("What is your favorite color?", "Can easily be guessed.");
}
@PostMapping("/PasswordReset/SecurityQuestions")
@ResponseBody
public AttackResult completed(@RequestParam String question) {
var answer = of(questions.get(question));
if (answer.isPresent()) {
triedQuestions.incr(question);
if (triedQuestions.isComplete()) {
return success(this).output("<b>" + answer + "</b>").build();
}
}
return informationMessage(this)
.feedback("password-questions-one-successful")
.output(answer.orElse("Unknown question, please try again..."))
.build();
}
}

View File

@@ -22,6 +22,9 @@
package org.owasp.webgoat.lessons.passwordreset;
import static java.util.Optional.ofNullable;
import java.time.LocalDateTime;
import org.apache.commons.lang3.StringUtils;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
@@ -34,10 +37,6 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.time.LocalDateTime;
import static java.util.Optional.ofNullable;
/**
* @author nbaars
* @since 8/20/17.
@@ -45,56 +44,74 @@ import static java.util.Optional.ofNullable;
@RestController
public class SimpleMailAssignment extends AssignmentEndpoint {
private final String webWolfURL;
private RestTemplate restTemplate;
private final String webWolfURL;
private RestTemplate restTemplate;
public SimpleMailAssignment(RestTemplate restTemplate, @Value("${webwolf.mail.url}") String webWolfURL) {
this.restTemplate = restTemplate;
this.webWolfURL = webWolfURL;
public SimpleMailAssignment(
RestTemplate restTemplate, @Value("${webwolf.mail.url}") String webWolfURL) {
this.restTemplate = restTemplate;
this.webWolfURL = webWolfURL;
}
@PostMapping(
path = "/PasswordReset/simple-mail",
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseBody
public AttackResult login(@RequestParam String email, @RequestParam String password) {
String emailAddress = ofNullable(email).orElse("unknown@webgoat.org");
String username = extractUsername(emailAddress);
if (username.equals(getWebSession().getUserName())
&& StringUtils.reverse(username).equals(password)) {
return success(this).build();
} else {
return failed(this).feedbackArgs("password-reset-simple.password_incorrect").build();
}
}
@PostMapping(path = "/PasswordReset/simple-mail", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseBody
public AttackResult login(@RequestParam String email, @RequestParam String password) {
String emailAddress = ofNullable(email).orElse("unknown@webgoat.org");
String username = extractUsername(emailAddress);
@PostMapping(
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
value = "/PasswordReset/simple-mail/reset")
@ResponseBody
public AttackResult resetPassword(@RequestParam String emailReset) {
String email = ofNullable(emailReset).orElse("unknown@webgoat.org");
return sendEmail(extractUsername(email), email);
}
if (username.equals(getWebSession().getUserName()) && StringUtils.reverse(username).equals(password)) {
return success(this).build();
} else {
return failed(this).feedbackArgs("password-reset-simple.password_incorrect").build();
}
}
@PostMapping(consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, value = "/PasswordReset/simple-mail/reset")
@ResponseBody
public AttackResult resetPassword(@RequestParam String emailReset) {
String email = ofNullable(emailReset).orElse("unknown@webgoat.org");
return sendEmail(extractUsername(email), email);
}
private String extractUsername(String email) {
int index = email.indexOf("@");
return email.substring(0, index == -1 ? email.length() : index);
}
private AttackResult sendEmail(String username, String email) {
if (username.equals(getWebSession().getUserName())) {
PasswordResetEmail mailEvent = PasswordResetEmail.builder()
.recipient(username)
.title("Simple e-mail assignment")
.time(LocalDateTime.now())
.contents("Thanks for resetting your password, your new password is: " + StringUtils.reverse(username))
.sender("webgoat@owasp.org")
.build();
try {
restTemplate.postForEntity(webWolfURL, mailEvent, Object.class);
} catch (RestClientException e) {
return informationMessage(this).feedback("password-reset-simple.email_failed").output(e.getMessage()).build();
}
return informationMessage(this).feedback("password-reset-simple.email_send").feedbackArgs(email).build();
} else {
return informationMessage(this).feedback("password-reset-simple.email_mismatch").feedbackArgs(username).build();
}
private String extractUsername(String email) {
int index = email.indexOf("@");
return email.substring(0, index == -1 ? email.length() : index);
}
private AttackResult sendEmail(String username, String email) {
if (username.equals(getWebSession().getUserName())) {
PasswordResetEmail mailEvent =
PasswordResetEmail.builder()
.recipient(username)
.title("Simple e-mail assignment")
.time(LocalDateTime.now())
.contents(
"Thanks for resetting your password, your new password is: "
+ StringUtils.reverse(username))
.sender("webgoat@owasp.org")
.build();
try {
restTemplate.postForEntity(webWolfURL, mailEvent, Object.class);
} catch (RestClientException e) {
return informationMessage(this)
.feedback("password-reset-simple.email_failed")
.output(e.getMessage())
.build();
}
return informationMessage(this)
.feedback("password-reset-simple.email_send")
.feedbackArgs(email)
.build();
} else {
return informationMessage(this)
.feedback("password-reset-simple.email_mismatch")
.feedbackArgs(username)
.build();
}
}
}

View File

@@ -22,23 +22,22 @@
package org.owasp.webgoat.lessons.passwordreset;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.SessionScope;
import java.util.HashSet;
import java.util.Set;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.SessionScope;
@Component
@SessionScope
public class TriedQuestions {
private Set<String> answeredQuestions = new HashSet<>();
private Set<String> answeredQuestions = new HashSet<>();
public void incr(String question) {
answeredQuestions.add(question);
}
public void incr(String question) {
answeredQuestions.add(question);
}
public boolean isComplete() {
return answeredQuestions.size() > 1;
}
public boolean isComplete() {
return answeredQuestions.size() > 1;
}
}

Some files were not shown because too many files have changed in this diff Show More