diff --git a/src/main/java/servlets/module/challenge/XssChallengeFour.java b/src/main/java/servlets/module/challenge/XssChallengeFour.java index 11beb2531..19f66e36a 100644 --- a/src/main/java/servlets/module/challenge/XssChallengeFour.java +++ b/src/main/java/servlets/module/challenge/XssChallengeFour.java @@ -1,6 +1,5 @@ package servlets.module.challenge; -import dbProcs.Getter; import java.io.IOException; import java.io.PrintWriter; import java.util.Locale; @@ -13,8 +12,6 @@ import javax.servlet.http.HttpSession; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import utils.FindXSS; -import utils.Hash; import utils.ShepherdLogManager; import utils.Validate; import utils.XssFilter; @@ -41,8 +38,6 @@ public class XssChallengeFour extends HttpServlet { private static final long serialVersionUID = 1L; private static final Logger log = LogManager.getLogger(XssChallengeFour.class); - private static final String levelHash = - "06f81ca93f26236112f8e31f32939bd496ffe8c9f7b564bce32bd5e3a8c2f751"; private static String levelName = "XSS Challenge 4"; /** @@ -88,27 +83,10 @@ public void doPost(HttpServletRequest request, HttpServletResponse response) + searchTerm + ""; } else { - searchTerm = XssFilter.encodeForHtml(searchTerm); userPost = "" + searchTerm + ""; log.debug("After Encoding - " + searchTerm); - if (FindXSS.search(userPost)) { - htmlOutput = - "
"
- + bundle.getString("result.youDidIt")
- + "
"
- + bundle.getString("result.resultKey")
- + " "
- + Hash.generateUserSolution(
- Getter.getModuleResultFromHash(
- getServletContext().getRealPath(""), levelHash),
- (String) ses.getAttribute("userName"))
- + "";
- }
}
log.debug("Adding searchTerm to Html: " + searchTerm);
htmlOutput +=
diff --git a/src/main/java/utils/XssFilter.java b/src/main/java/utils/XssFilter.java
index ad1c27d58..68af7ef7d 100644
--- a/src/main/java/utils/XssFilter.java
+++ b/src/main/java/utils/XssFilter.java
@@ -100,10 +100,12 @@ public static String encodeForHtml(String input) {
log.debug("Filtering input at XSS white list");
input = Encode.forHtml(input);
- // Decode quotes to open a security hole in Encoder
- input = input.replaceFirst(""", "\"");
- // Encode lower-case "on" and upper-case "on" to complicate the required attack vectors to pass
- return input.replaceAll("on", "on").replaceAll("ON", "ON");
+ // Quotes are intentionally left HTML-encoded here (no longer decoded back to a raw
+ // double-quote) so that reflecting this value inside an HTML attribute (e.g. href="...")
+ // can no longer be used to break out of the attribute and inject new attributes/handlers.
+ // Encode every case-variant of "on" (on/On/oN/ON) as defence-in-depth against inline event
+ // handlers, since HTML attribute names are matched case-insensitively by browsers.
+ return input.replaceAll("(?i)on", "on");
}
/**
diff --git a/src/test/java/servlets/module/challenge/XssChallengeFourVulnerabilityTest.java b/src/test/java/servlets/module/challenge/XssChallengeFourVulnerabilityTest.java
new file mode 100644
index 000000000..21cdeaa88
--- /dev/null
+++ b/src/test/java/servlets/module/challenge/XssChallengeFourVulnerabilityTest.java
@@ -0,0 +1,76 @@
+package servlets.module.challenge;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+import utils.FindXSS;
+import utils.XssFilter;
+
+/**
+ * Reproduces the exact HTML construction that {@link XssChallengeFour#doPost} performs on a
+ * user-submitted "http..." searchTerm (see the else-branch that builds `userPost`), without
+ * requiring a live servlet container. This lets us prove, at the feature level, that:
+ *
+ *