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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user