From a30f12303a238caa66f1119e3ef0d9c8739d8d0c Mon Sep 17 00:00:00 2001 From: beanbeah <24713371+beanbeah@users.noreply.github.com> Date: Sun, 9 Aug 2026 08:37:18 -0700 Subject: [PATCH 1/2] fix(build): bump lombok to 1.18.46 for JDK 25 CI compatibility Every branch built against this base was failing to build/boot in CI with ExceptionInInitializerError: TypeTag :: UNKNOWN, because lombok 1.18.36 can't reflectively patch JDK 25's javac internals. Baking the fix into the fork's own dc34-ctf base so every future branch inherits it automatically instead of needing a manual per-branch cherry-pick. Co-Authored-By: Claude Sonnet 5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b5ad9015d..3259ca38a 100644 --- a/pom.xml +++ b/pom.xml @@ -238,7 +238,7 @@ org.projectlombok lombok - 1.18.36 + 1.18.46 provided true From 488c4b55280b3395541df34b31d885c4ee7059c4 Mon Sep 17 00:00:00 2001 From: beanbeah <24713371+beanbeah@users.noreply.github.com> Date: Sun, 9 Aug 2026 11:40:01 -0700 Subject: [PATCH 2/2] fix(html-tampering): recompute price server-side instead of trusting client Total The HtmlTampering task previously accepted whatever 'Total' value the client submitted alongside 'QTY' and marked the assignment as solved whenever that submitted total was lower than the real unit price times quantity - classic client-side price tampering / broken access control, since the price shown to the user comes from a hidden form field the client fully controls. Fix: the server now holds the unit price as a private, authoritative constant and recomputes the expected total itself (quantity * unit price), rejecting any request whose client-supplied Total does not match within a small rounding tolerance. Non-numeric QTY/Total and QTY <= 0 are also rejected. This follows the lesson's own mitigation guidance (HtmlTampering_Mitigation.adoc): 'look up the price ... and calculate the total price again' / 'NEVER TRUST INPUT SENT BY A CLIENT'. Locally verified in WSL: built + booted the app, replayed the original exploit (QTY=1, Total=1) which now returns tamper.failure instead of success, confirmed a legitimate request (QTY=1, Total=2999.99, and QTY=2, Total=5999.98) still returns tamper.success, and confirmed a QTY-only tamper (QTY=100, Total=2999.99) is also rejected. --- .../htmltampering/HtmlTamperingTask.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTamperingTask.java b/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTamperingTask.java index 3552883f4..bc59322db 100644 --- a/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTamperingTask.java +++ b/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTamperingTask.java @@ -19,12 +19,36 @@ @AssignmentHints({"hint1", "hint2", "hint3"}) public class HtmlTamperingTask implements AssignmentEndpoint { + // The unit price is authoritative server-side state (e.g. a catalog/database lookup in a + // real application). It must never be derived from client-supplied input. + private static final double UNIT_PRICE = 2999.99; + private static final double ROUNDING_TOLERANCE = 0.01; + @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(); + final double quantity; + final double submittedTotal; + try { + quantity = Double.parseDouble(QTY); + submittedTotal = Double.parseDouble(Total); + } catch (NumberFormatException e) { + return failed(this).feedback("html-tampering.tamper.failure").build(); + } + + if (quantity <= 0) { + return failed(this).feedback("html-tampering.tamper.failure").build(); } - return failed(this).feedback("html-tampering.tamper.failure").build(); + + // Recompute the total from the trusted, server-side unit price instead of trusting the + // "Total" value the client sent along with the request. Any mismatch means the price shown + // to the checkout endpoint was tampered with (e.g. edited in the HTML/hidden field before + // submit) and must be rejected rather than honored. + double serverCalculatedTotal = quantity * UNIT_PRICE; + if (Math.abs(serverCalculatedTotal - submittedTotal) > ROUNDING_TOLERANCE) { + return failed(this).feedback("html-tampering.tamper.failure").build(); + } + + return success(this).feedback("html-tampering.tamper.success").build(); } }