Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -9,6 +9,7 @@

import java.util.function.Predicate;
import java.util.regex.Pattern;
import org.apache.commons.text.StringEscapeUtils;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
Expand Down Expand Up @@ -60,9 +61,18 @@ public AttackResult completed(
+ QTY4.intValue() * 299.99;

userSessionData.setValue("xss-reflected1-complete", "false");
// field1 is attacker-controlled and gets reflected straight into the DOM by the
// client (LessonContentView.js renders "output" via jQuery .html()). Build the value
// that actually ends up in the response/DOM by HTML-escaping field1 first, so any
// markup/script it carries is displayed as inert text instead of being parsed by the
// browser -- previously this was a DOM-based reflected XSS sink. Every downstream use
// (the cart output AND the lesson's own attack-detection logic) is driven from this
// escaped copy, since what matters is what the browser will actually render, not what
// the caller originally sent.
String renderedCardNumber = StringEscapeUtils.escapeHtml4(field1);
StringBuilder cart = new StringBuilder();
cart.append("Thank you for shopping at WebGoat. <br />Your support is appreciated<hr />");
cart.append("<p>We have charged credit card:" + field1 + "<br />");
cart.append("<p>We have charged credit card:" + renderedCardNumber + "<br />");
cart.append(" ------------------- <br />");
cart.append(" $" + totalSale);

Expand All @@ -71,9 +81,9 @@ public AttackResult completed(
userSessionData.setValue("xss-reflected1-complete", "false");
}

if (XSS_PATTERN.test(field1)) {
if (XSS_PATTERN.test(renderedCardNumber)) {
userSessionData.setValue("xss-reflected-5a-complete", "true");
if (field1.toLowerCase().contains("console.log")) {
if (renderedCardNumber.toLowerCase().contains("console.log")) {
return success(this)
.feedback("xss-reflected-5a-success-console")
.output(cart.toString())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,11 @@ define(['jquery',

/* for testing */
showTestParam: function (param) {
this.$el.find('.lesson-content').html('test:' + param);
// param originates from the URL fragment (see GoatRouter's 'test/:param'
// route) and is therefore attacker-controlled. Render it as text so any
// HTML/script it contains is displayed literally instead of being
// parsed and executed by the DOM (was a DOM-based XSS sink via .html()).
this.$el.find('.lesson-content').text('test:' + param);
},

resetLesson: function () {
Expand Down
Loading