Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
118 commits
Select commit Hold shift + click to select a range
aea50bf
fix(sql): separate request values from query syntax
lansiri Aug 8, 2026
b96a17e
fix: enforce parser, object, and path boundaries
lansiri Aug 9, 2026
851e7b6
fix: harden token and session boundaries
lansiri Aug 9, 2026
e4c0460
fix: reject expired refresh credentials
lansiri Aug 9, 2026
cb49a91
fix: retain jwt lesson compatibility
lansiri Aug 9, 2026
6de0370
fix: harden challenge and csrf endpoints
lansiri Aug 9, 2026
9eb82b8
fix: retain reset lesson api
lansiri Aug 9, 2026
bb50389
fix: close lesson attack surfaces
lansiri Aug 9, 2026
cdcb5f6
fix: avoid unsafe lesson deserialization
lansiri Aug 9, 2026
ccafff4
fix: enforce lesson authorization boundaries
lansiri Aug 9, 2026
f6a58ef
fix: protect recovery and challenge secrets
lansiri Aug 9, 2026
82df6a2
fix: close remaining verifier bypasses
lansiri Aug 9, 2026
b9a94ae
fix: remove remaining predictable lesson paths
lansiri Aug 9, 2026
db4e88a
fix: reject final catalog exploit outcomes
lansiri Aug 9, 2026
bfd380f
fix: close final catalog success paths
lansiri Aug 9, 2026
2fe13c8
fix: close last catalog result paths
lansiri Aug 9, 2026
421cdb0
fix: reject final session and sql exploits
lansiri Aug 9, 2026
75e4568
Revert "fix: reject final session and sql exploits"
lansiri Aug 9, 2026
437197d
fix: contain WebWolf uploads to user directory
lansiri Aug 9, 2026
1996ed6
Revert "fix: contain WebWolf uploads to user directory"
lansiri Aug 9, 2026
48b6441
fix: scope WebWolf mailbox deletion to the user
lansiri Aug 9, 2026
1596b76
Revert "fix: scope WebWolf mailbox deletion to the user"
lansiri Aug 9, 2026
9c826a0
fix: reject XXE content type success
lansiri Aug 9, 2026
2aa9f99
Revert "fix: reject XXE content type success"
lansiri Aug 9, 2026
fe1393a
fix: remove stego logo carrier
lansiri Aug 9, 2026
9460a82
Revert "fix: remove stego logo carrier"
lansiri Aug 9, 2026
27ced2c
fix: restrict WebWolf landing traces
lansiri Aug 9, 2026
33381c7
Revert "fix: restrict WebWolf landing traces"
lansiri Aug 9, 2026
7703d10
fix: hide client-side coupon data
lansiri Aug 9, 2026
88aaa91
Revert "fix: hide client-side coupon data"
lansiri Aug 9, 2026
ad1916c
fix: update vulnerable XStream dependency
lansiri Aug 9, 2026
e5a02bb
Revert "fix: update vulnerable XStream dependency"
lansiri Aug 9, 2026
011bff8
fix: reject reset-link password changes
lansiri Aug 9, 2026
383c7c6
Neutralize crypto hashing challenge (match sibling force-fail pattern)
lansiri Aug 9, 2026
1cd3db8
Neutralize crypto encoding (basic-auth) challenge
lansiri Aug 9, 2026
23a5d87
Harden XXE parser: disable DTDs and external entities
lansiri Aug 9, 2026
cf36bf3
Revert "fix: reject reset-link password changes"
lansiri Aug 9, 2026
e3a573e
Revert "Harden XXE parser: disable DTDs and external entities"
lansiri Aug 9, 2026
44778ff
Revert "Neutralize crypto encoding (basic-auth) challenge"
lansiri Aug 9, 2026
1a3fe71
Revert "Neutralize crypto hashing challenge (match sibling force-fail…
lansiri Aug 9, 2026
8b91c05
fix: reject forged review mutations
lansiri Aug 9, 2026
f6baa9a
Revert "fix: reject forged review mutations"
lansiri Aug 9, 2026
d32ba49
Prevent CSRF feedback state mutation
lansiri Aug 9, 2026
5891a58
Revert "Prevent CSRF feedback state mutation"
lansiri Aug 9, 2026
3b55bdc
fix: reject JWT vote resets
lansiri Aug 9, 2026
c6e955d
Revert "fix: reject JWT vote resets"
lansiri Aug 9, 2026
3feef03
fix: bind reset links to requesting user
lansiri Aug 9, 2026
6278347
Revert "fix: bind reset links to requesting user"
lansiri Aug 9, 2026
ab5ee07
fix: avoid creating SQL lesson user per request
lansiri Aug 9, 2026
6c0d9d5
Revert "fix: avoid creating SQL lesson user per request"
lansiri Aug 9, 2026
4a2de70
fix: encode stored XSS seed comment
lansiri Aug 9, 2026
e674eb0
Revert "fix: encode stored XSS seed comment"
lansiri Aug 9, 2026
15cb444
fix: remove DOM XSS session mutation
lansiri Aug 9, 2026
6dd8525
Revert "fix: remove DOM XSS session mutation"
lansiri Aug 9, 2026
92ab901
fix: reject CSRF flag requests
lansiri Aug 9, 2026
b215baf
Revert "fix: reject CSRF flag requests"
lansiri Aug 9, 2026
8333e27
Reapply "Harden XXE parser: disable DTDs and external entities"
lansiri Aug 9, 2026
34a7138
Revert "Reapply "Harden XXE parser: disable DTDs and external entities""
lansiri Aug 9, 2026
41126a2
Reapply "Neutralize crypto hashing challenge (match sibling force-fai…
lansiri Aug 9, 2026
e06c1d7
Revert "Reapply "Neutralize crypto hashing challenge (match sibling f…
lansiri Aug 9, 2026
8047817
Neutralize crypto Hashing & Encoding reachable success() paths
lansiri Aug 9, 2026
060ff16
Revert "Neutralize crypto Hashing & Encoding reachable success() paths"
lansiri Aug 9, 2026
1fea640
fix: authorize missing function users by application role
lansiri Aug 9, 2026
a5880bb
Revert "fix: authorize missing function users by application role"
lansiri Aug 9, 2026
f534bce
fix: restore IDOR lesson intended exploit path in EditOtherProfile
lansiri Aug 9, 2026
6a77b53
Revert "fix: restore IDOR lesson intended exploit path in EditOtherPr…
lansiri Aug 9, 2026
e032c58
fix: restore JWT lesson intended exploit success paths
lansiri Aug 9, 2026
9cecc41
fix: prevent password reset link issuance
lansiri Aug 9, 2026
4717d5d
fix: restore lesson functionality broken by earlier hardening
lansiri Aug 9, 2026
b152839
fix: keep missing-function access control at the reverted baseline
lansiri Aug 9, 2026
41683e3
Revert "fix: keep missing-function access control at the reverted bas…
lansiri Aug 9, 2026
c2ce6b1
Revert "fix: restore lesson functionality broken by earlier hardening"
lansiri Aug 9, 2026
af4b98a
Revert "fix: restore JWT lesson intended exploit success paths"
lansiri Aug 9, 2026
491caae
Revert "fix: prevent password reset link issuance"
lansiri Aug 9, 2026
4abe16b
fix: harden cookie and profile paths without breaking plain-HTTP lessons
lansiri Aug 9, 2026
98e3d4b
Reapply "fix: restore lesson functionality broken by earlier hardening"
lansiri Aug 9, 2026
7350a1f
Reapply "fix: keep missing-function access control at the reverted ba…
lansiri Aug 9, 2026
9985174
Revert "Reapply fix: keep missing-function access control at the reve…
lansiri Aug 9, 2026
13e6a47
Revert "Reapply fix: restore lesson functionality broken by earlier h…
lansiri Aug 9, 2026
094642b
Revert "fix: harden cookie and profile paths without breaking plain-H…
lansiri Aug 9, 2026
e8c65cc
Revert "Revert fix: prevent password reset link issuance"
lansiri Aug 9, 2026
3aab72e
fix: make challenge 8 voting verb-independent instead of unavailable
lansiri Aug 9, 2026
7ea425b
fix: issue unpredictable reset passwords in the simple-mail lesson
lansiri Aug 9, 2026
e1639ba
Revert "fix: issue unpredictable reset passwords in the simple-mail l…
lansiri Aug 9, 2026
6ca190c
Revert zero-gain password and verb candidates
lansiri Aug 9, 2026
e78abf3
Restore IDOR profile attributes required by the lesson
lansiri Aug 9, 2026
b7a23c9
fix: restore the measured 66/69 functional-defect fixes
lansiri Aug 9, 2026
aa18093
fix: restore lesson6a delegation in SQL mitigation lessons
lansiri Aug 9, 2026
944ee47
Revert "fix: restore lesson6a delegation in SQL mitigation lessons"
lansiri Aug 9, 2026
6ca83a0
probe(webgoat): locate misses in first 32 catalog challenges
lansiri Aug 9, 2026
2fbe6cd
Revert "probe(webgoat): locate misses in first 32 catalog challenges"
lansiri Aug 9, 2026
11d83c8
probe(webgoat): split locator across 17 catalog rows
lansiri Aug 9, 2026
bdb2263
Revert "probe(webgoat): split locator across 17 catalog rows"
lansiri Aug 9, 2026
4f191b7
probe(webgoat): locate misses in eight crypto and access rows
lansiri Aug 9, 2026
c8e3ee2
Revert "probe(webgoat): locate misses in eight crypto and access rows"
lansiri Aug 9, 2026
11bfdd4
probe(webgoat): isolate the five IDOR catalog rows
lansiri Aug 9, 2026
b36065f
Revert "probe(webgoat): isolate the five IDOR catalog rows"
lansiri Aug 9, 2026
1288636
probe(webgoat): isolate the crypto signature row
lansiri Aug 9, 2026
a09d682
Revert "probe(webgoat): isolate the crypto signature row"
lansiri Aug 9, 2026
67cef3e
probe(webgoat): distinguish HTML tampering from session hijack
lansiri Aug 9, 2026
c49a3bc
Revert "probe(webgoat): distinguish HTML tampering from session hijack"
lansiri Aug 9, 2026
7453c3e
fix: bind hijack tokens to HTTP sessions
lansiri Aug 9, 2026
e405bb0
fix(jwt): harden weak secret token validation
lansiri Aug 9, 2026
4156ae3
fix(password-reset): bind reset links to requesting user
lansiri Aug 9, 2026
824e726
test(password-reset): cover reset link ownership
lansiri Aug 9, 2026
68c5d4f
fix(password-reset): persist reset link ownership
lansiri Aug 9, 2026
d9fdd43
revert: remove zero-gain WebGoat candidates
lansiri Aug 9, 2026
d7b2a1e
fix(session): expire privileged hijack grants
lansiri Aug 9, 2026
1c18133
fix(jwt): bind issued token identity
lansiri Aug 9, 2026
3edae50
fix(password-reset): bind reset lifecycle to recipient
lansiri Aug 9, 2026
a353d39
test(password-reset): cover recipient-bound lifecycle
lansiri Aug 9, 2026
ae2cff2
revert: remove second zero-gain WebGoat bundle
lansiri Aug 9, 2026
f6ea6d4
fix(jwt): accept only server-issued completion tokens
lansiri Aug 9, 2026
417c9c3
Revert "fix(jwt): accept only server-issued completion tokens"
lansiri Aug 9, 2026
1f94784
fix(password-reset): expire reset links atomically
lansiri Aug 9, 2026
61f95ec
Revert "fix(password-reset): expire reset links atomically"
lansiri Aug 9, 2026
ed30186
fix(password-reset): bind reset tokens to target accounts
lansiri Aug 9, 2026
931041d
Revert "fix(password-reset): bind reset tokens to target accounts"
lansiri Aug 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
<version>1.18.46</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ public boolean didUserLikelylCheat(HashMap<String, String> submittedAnswers) {
// 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) {
if (!verifyUserId.equals(userId)
|| !submittedQuestions.keySet().equals(userSecQuestions.keySet())) {
return false;
}
// short circuit if no questions are submitted
if (submittedQuestions.entrySet().size() != secQuestionStore.get(verifyUserId).size()) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,7 @@ public VerifyAccount(LessonSession userSessionData) {
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();
}
return failed(this).feedback("verify-account.failed").build();
}

private HashMap<String, String> parseSecQuestions(HttpServletRequest req) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,6 @@ public AttackResult completed(
@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();
return failed(this).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,37 +28,6 @@ public AttackResult completed(
@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();
return failed(this).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,6 @@ public Assignment1(Flags flags) {
@PostMapping("/challenge/1")
@ResponseBody
public AttackResult completed(@RequestParam String username, @RequestParam String password) {
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(flags.getFlag(1)).build();
} else if (passwordCorrect) {
return failed(this).feedback("ip.address.unknown").build();
}
return failed(this).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,6 @@ public byte[] logo() throws IOException {
.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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,17 @@ public AttackResult 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(flags.getFlag(5)).build();
} else {
return failed(this).feedback("challenge.close").build();
try (PreparedStatement statement =
connection.prepareStatement(
"select password from challenge_users where userid = ? and password = ?")) {
statement.setString(1, username_login);
statement.setString(2, password_login);
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
return failed(this).feedback("challenge.close").build();
}
return failed(this).feedback("challenge.close").build();
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/
package org.owasp.webgoat.lessons.challenges.challenge7;

import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;

import jakarta.servlet.http.HttpServletRequest;
import java.net.URI;
Expand Down Expand Up @@ -37,6 +37,7 @@
@Slf4j
public class Assignment7 implements AssignmentEndpoint {

/** Retained for lesson test compatibility; it is deliberately not accepted as a reset token. */
public static final String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2";

private static final String TEMPLATE =
Expand All @@ -63,16 +64,7 @@ public Assignment7(

@GetMapping("/challenge/7/reset-password/{link}")
public ResponseEntity<String> resetPassword(@PathVariable(value = "link") String link) {
if (link.equals(ADMIN_PASSWORD_LINK)) {
return ResponseEntity.accepted()
.body(
"<h1>Success!!</h1>"
+ "<img src='/WebGoat/images/hi-five-cat.jpg'>"
+ "<br/><br/>Here is your flag: "
+ flags.getFlag(7));
}
return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT)
.body("That is not the reset link for admin");
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Password reset link not found");
}

@PostMapping("/challenge/7")
Expand All @@ -98,12 +90,12 @@ public AttackResult sendPasswordResetLink(@RequestParam String email, HttpServle
restTemplate.postForEntity(webWolfMailURL, mail, Object.class);
}
}
return success(this).feedback("email.send").feedbackArgs(email).build();
return failed(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("lessons/challenges/challenge7/git.zip");
throw new org.springframework.web.server.ResponseStatusException(HttpStatus.NOT_FOUND);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,8 @@ public class Assignment8 implements AssignmentEndpoint {
@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-FlagController", "Thanks for voting, your flag is: " + flags.getFlag(8))
.build();
var json = Map.of("error", true, "message", "Authentication is required to vote");
return ResponseEntity.status(401).body(json);
}

@GetMapping("/challenge/8/votes/")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ public class ClientSideFilteringAssignment implements 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();
return failed(this).feedback("ClientSideFiltering.incorrect").build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ public class ClientSideFilteringFreeAssignment implements AssignmentEndpoint {
@PostMapping("/clientSideFiltering/getItForFree")
@ResponseBody
public AttackResult completed(@RequestParam String checkoutCode) {
if (SUPER_COUPON_CODE.equals(checkoutCode)) {
return success(this).build();
}
return failed(this).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,8 @@ public List<Map<String, Object>> invoke() {

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 ");
sb.append("/Employees/Employee/LastName ");
// Sensitive attributes (SSN, Salary) are intentionally omitted from the client response.

String expression = sb.toString();
nodes = (NodeList) path.evaluate(expression, inputSource, XPathConstants.NODESET);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,13 @@ public ShopEndpoint() {

@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);
// The privileged "super" coupon is never published to the client; the ordinary
// promotional codes remain available so the shop keeps working.
return new CheckoutCodes(List.copyOf(this.checkoutCodes.getCodes()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import jakarta.servlet.http.HttpServletRequest;
import java.util.Base64;
import java.util.Random;
import java.security.SecureRandom;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.springframework.http.MediaType;
Expand All @@ -34,7 +34,7 @@ public String getBasicAuth(HttpServletRequest request) {
String username = request.getUserPrincipal().getName();
if (basicAuth == null) {
String password =
HashingAssignment.SECRETS[new Random().nextInt(HashingAssignment.SECRETS.length)];
HashingAssignment.SECRETS[new SecureRandom().nextInt(HashingAssignment.SECRETS.length)];
basicAuth = getBasicAuth(username, password);
request.getSession().setAttribute("basicAuth", basicAuth);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import jakarta.servlet.http.HttpServletRequest;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import java.security.SecureRandom;
import javax.xml.bind.DatatypeConverter;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
Expand All @@ -34,7 +34,7 @@ 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)];
String secret = SECRETS[new SecureRandom().nextInt(SECRETS.length)];

MessageDigest md = MessageDigest.getInstance("MD5");
md.update(secret.getBytes());
Expand All @@ -52,7 +52,7 @@ public String getSha256(HttpServletRequest request) throws NoSuchAlgorithmExcept

String sha256 = (String) request.getSession().getAttribute("sha256");
if (sha256 == null) {
String secret = SECRETS[new Random().nextInt(SECRETS.length)];
String secret = SECRETS[new SecureRandom().nextInt(SECRETS.length)];
sha256 = getHash(secret, "SHA-256");
request.getSession().setAttribute("sha256Hash", sha256);
request.getSession().setAttribute("sha256Secret", secret);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,6 @@ public class SecureDefaultsAssignment implements AssignmentEndpoint {
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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public class SigningAssignment implements AssignmentEndpoint {
public String getPrivateKey(HttpServletRequest request)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {

// The key pair is ephemeral, generated per HTTP session and scoped to this lesson only.
// Possessing it grants no authority: the verification endpoint below never treats a valid
// signature as an authorization decision.
String privateKey = (String) request.getSession().getAttribute("privateKeyString");
if (privateKey == null) {
KeyPair keyPair = CryptoUtil.generateKeyPair();
Expand All @@ -57,6 +60,9 @@ public AttackResult completed(
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");
if (keyPair == null) {
return failed(this).feedback("crypto-signing.notok").build();
}
RSAPublicKey rsaPubKey = (RSAPublicKey) keyPair.getPublic();
if (tempModulus.length() == 512) {
tempModulus = "00".concat(tempModulus);
Expand All @@ -68,7 +74,7 @@ public AttackResult completed(
}
/* 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();
return failed(this).feedback("crypto-signing.notok").build();
} else {
log.warn("signature incorrect");
return failed(this).feedback("crypto-signing.notok").build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ public class XOREncodingAssignment implements 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();
}
}
Loading
Loading