Initial commit for password reset lesson

This commit is contained in:
Nanne Baars 2018-05-25 14:27:45 +02:00
parent 8d7ecb19d7
commit eaf68d38c5
23 changed files with 464 additions and 1 deletions

View File

@ -48,7 +48,7 @@ public class HttpBasicsInterceptRequest extends AssignmentEndpoint {
@RequestMapping(method = RequestMethod.GET)
public @ResponseBody
AttackResult completed(HttpServletRequest request) throws IOException {
AttackResult completed(HttpServletRequest request) {
String header = null;
String param = null;
if (request != null && (header = request.getHeader("x-request-intercepted")) != null

View File

@ -0,0 +1,21 @@
<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>password-reset</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0.M14</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>4.1.3.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,34 @@
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;
public class PasswordReset extends NewLesson {
@Override
public Category getDefaultCategory() {
return Category.AUTHENTICATION;
}
@Override
public List<String> getHints() {
return new ArrayList();
}
@Override
public Integer getDefaultRanking() {
return 10;
}
@Override
public String getTitle() {
return "password-reset.title";
}
@Override
public String getId() {
return "PasswordReset";
}
}

View File

@ -0,0 +1,18 @@
package org.owasp.webgoat.plugin;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Builder
@Data
public class PasswordResetEmail implements Serializable {
private LocalDateTime time;
private String contents;
private String sender;
private String title;
private String recipient;
}

View File

@ -0,0 +1,55 @@
package org.owasp.webgoat.plugin.questions;
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.plugin.PasswordResetEmail;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* @author nbaars
* @since 8/20/17.
*/
@AssignmentPath("/PasswordReset/questions")
public class QuestionsAssignment extends AssignmentEndpoint {
private final static Map<String, String> COLORS = new HashMap<>();
static {
COLORS.put("admin", "green");
COLORS.put("jerry", "orange");
COLORS.put("tom", "purple");
COLORS.put("larry", "yellow");
COLORS.put("webgoat", "red");
}
@PostMapping(consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseBody
public AttackResult passwordReset(@RequestParam Map<String, Object> json) {
String securityQuestion = (String) json.getOrDefault("securityQuestion", "");
String username = (String) json.getOrDefault("username", "");
if ("webgoat".equalsIgnoreCase(username.toLowerCase())) {
return trackProgress(failed().feedback("password-questions-wrong-user").build());
}
String validAnswer = COLORS.get(username.toLowerCase());
if (validAnswer == null) {
return trackProgress(failed().feedback("password-questions-unknown-user").feedbackArgs(username).build());
} else if (validAnswer.equals(securityQuestion)) {
return trackProgress(success().build());
}
return trackProgress(failed().build());
}
}

View File

@ -0,0 +1,82 @@
package org.owasp.webgoat.plugin.simple;
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.plugin.PasswordResetEmail;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Optional;
import static java.util.Optional.ofNullable;
/**
* @author nbaars
* @since 8/20/17.
*/
@AssignmentPath("/PasswordReset/simple-mail")
public class SimpleMailAssignment extends AssignmentEndpoint {
private final String webWolfURL;
private RestTemplate restTemplate;
public SimpleMailAssignment(RestTemplate restTemplate, @Value("${webwolf.url.mail}") String webWolfURL) {
this.restTemplate = restTemplate;
this.webWolfURL = webWolfURL;
}
@PostMapping(consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseBody
public AttackResult sendEmail(@RequestParam Map<String, Object> json) {
String email = (String) json.get("emailReset");
if (StringUtils.isEmpty(email)) {
email = (String) json.getOrDefault("email", "unknown@webgoat.org");
}
String password = (String) json.getOrDefault("password", "");
int index = email.indexOf("@");
String username = email.substring(0, index == -1 ? email.length() : index);
if (StringUtils.isEmpty(password)) {
return sendEmail(username, email);
} else {
return checkPassword(password, username);
}
}
private AttackResult checkPassword(String password, String username) {
if (username.equals(getWebSession().getUserName()) && StringUtils.reverse(username).equals(password)) {
return trackProgress(success().build());
} else {
return trackProgress(failed().feedbackArgs("password-reset-simple.password_incorrect").build());
}
}
private AttackResult sendEmail(String username, String email) {
if (username.equals(getWebSession().getUserName())) {
PasswordResetEmail mailEvent = PasswordResetEmail.builder()
.recipient(username)
.title("Simple e-mail assignment")
.time(LocalDateTime.now())
.contents("Thanks your resetting your password, your new password is: " + StringUtils.reverse(username))
.sender("webgoat@owasp.org")
.build();
try {
restTemplate.postForEntity(webWolfURL, mailEvent, Object.class);
} catch (RestClientException e) {
return informationMessage().feedback("password-reset-simple.email_failed").output(e.getMessage()).build();
}
return informationMessage().feedback("password-reset-simple.email_send").feedbackArgs(email).build();
} else {
return informationMessage().feedback("password-reset-simple.email_mismatch").feedbackArgs(username).build();
}
}
}

View File

@ -0,0 +1,134 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:PasswordReset_plan.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:PasswordReset_simple.adoc"></div>
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/password.css}"/>
<script th:src="@{/lesson_js/bootstrap.min.js}" language="JavaScript"></script>
<script th:src="@{/lesson_js/password-reset-simple.js}" language="JavaScript"></script>
<div class="attack-container">
<img th:src="@{/images/wolf-enabled.png}" class="webwolf-enabled"/>
<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"
action="/WebGoat/PasswordReset/simple-mail"
enctype="application/json;charset=UTF-8">
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
<h4 style="border-bottom: 1px solid #c5c5c5;"><i class="glyphicon glyphicon-user"></i> Account
Access</h4>
<div style="padding: 20px;" id="form-olvidado">
<fieldset>
<div class="form-group input-group">
<span class="input-group-addon">@</span>
<input class="form-control" placeholder="Email" name="email" type="email"
autofocus=""></input>
</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=""/>
</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="olvidado">
<small>Forgot your password?</small>
</a>
</p>
</div>
</fieldset>
</div>
<div style="display: none;" id="form-olvidado">
<h4 class="">Forgot your password?</h4>
<fieldset>
<span class="help-block">Please type your e-mail address</span>
<div class="form-group input-group">
<span class="input-group-addon">@</span>
<input class="form-control" placeholder="test1233@webgoat.org" name="emailReset"
type="email"/>
</div>
<button type="submit" class="btn btn-primary btn-block" id="btn-olvidado">Continue
</button>
<p class="help-block">
<a class="text-muted" href="#" id="acceso">
<small>Account Access</small>
</a>
</p>
</fieldset>
</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:PasswordReset_wrong_message.adoc"></div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:PasswordReset_known_questions.adoc"></div>
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/password.css}"/>
<script th:src="@{/lesson_js/bootstrap.min.js}" language="JavaScript"></script>
<script th:src="@{/lesson_js/password-reset-simple.js}" language="JavaScript"></script>
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN"
method="POST"
action="/WebGoat/PasswordReset/questions"
enctype="application/json;charset=UTF-8">
<div class="container-fluid">
<div class="col-md-2">
<article class="card-body">
<a href="" class="float-right btn btn-outline-primary">Sign up</a>
<a href="" class="float-right btn btn-outline-primary">Login</a>
<h4 class="card-title mb-4 mt-1">WebGoat Password Recovery</h4>
<form>
<div class="form-group">
<label>Your username</label>
<input name="username" class="form-control" placeholder="Username" type="text"/>
</div>
<div class="form-group">
<label>What is your favorite color?</label>
<input class="form-control" placeholder="Answer security question" type="text" name="securityQuestion"/>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block"> Submit</button>
</div>
</form>
</article>
</div>
</div>
</form>
<br/>
<br/>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
</html>

View File

@ -0,0 +1,9 @@
password-reset.title=Password reset
password-reset-simple.email_send=An email has been send to {0} please check your inbox.
password-reset-simple.password_incorrect=Not the correct password please try again.
password-reset-simple.email_failed=There was an error while sending the e-mail. Is WebWolf running?
password-reset-simple.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.
password-questions-wrong-user=You need to find a different user you are logging in with 'webgoat'.
password-questions-unknown-user=User {0} is not a valid user.

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,10 @@
$(document).ready(function() {
$('#olvidado').click(function(e) {
e.preventDefault();
$('div#form-olvidado').toggle('500');
});
$('#acceso').click(function(e) {
e.preventDefault();
$('div#form-olvidado').toggle('500');
});
});

View File

@ -0,0 +1,17 @@
== Creating the password reset link
When creating a password reset link you need to make sure:
- It is a unique link with a random token
- It can only be used once
- The link is only valid for one hour
Send a link with a random token means an attacker cannot start a simple DOS attack to your website by starting to
block users. The link should not be used more then once which makes it impossible to change the password again.
The time out is necessary to restrict the attack window, having a link opens up a lot of possibilities for the attacker.
== Assignment
In this assignment Tom uses the password reset functionality, can you try to find a way to e-mail the password
reset link to your own inbox at user@webwolf.org. Use WebWolf to read the email and paste the token in the box
below.

View File

@ -0,0 +1,23 @@
== Security questions
This has been an issue and still is for a lot of websites, when you lost your password the website will ask you
for a security question which you answered during the sign up process. Most of the time this list contains a fixed
number of question and which sometimes even have a limited set of answers. In order to use this functionality
a user should be able to select a question by itself and type in the answer as well. This way users will not share
the question which makes it more difficult for an attacker.
One important thing to remember the answers to these security question(s) should be treated with the same level of
security which is applied for storing a password in a database. If the database leaks an attacker should not be able
to perform password reset based on the answer of the security question.
Users share so much information on social media these days it becomes difficult to use security questions for password
resets, a good resource for security questions is: http://goodsecurityquestions.com/
== Assignment
Users can retrieve their password if they can answer the secret question properly. There is no lock-out mechanism on
this 'Forgot Password' page. Your username is 'webgoat' and your favorite color is 'red'. The goal is to retrieve the
password of another user.

View File

@ -0,0 +1,3 @@
== Password reset link
Should be unique, do

View File

@ -0,0 +1,22 @@
= Password reset
== Concept
This lesson teaches about password reset functionality which most of the time is an overlooked part of the application
leading to all kind of interesting logic flaws.
== Goals
Teach how to securely implement password reset functionality within your application.
== Introduction
Each and every one of us will have used the password reset functionality on websites before. Each website implements
this functionality in a different manner. On some site you have to answer some question on other sites an e-mail
with an activation link will be send to you. In this lesson we will go through some of the most common password
reset functionalities and show where it can go wrong.
Still there are companies which will send the password in plaintext to a user in an e-mail. For a couple of examples
you can take a look at http://plaintextoffenders.com/ Here you will find website which still send you the plaintext
password in an e-mail. Not only this should make you question the security of the site but this also mean they store
your password in plaintext!

View File

@ -0,0 +1,6 @@
== Email functionality with WebWolf
Let's first do a simple assignment to make sure you are able to read e-mails with WebWolf, first start WebWolf (see http://)
In the reset page below send an e-mail to `username@webgoat.org` (part behind the @ is not important)
Open WebWolf and read the e-mail and login with your username and the password provided in the e-mail.

View File

@ -0,0 +1,21 @@
:half-size: width='20%'
== Find out if account exists
As stated before during a password reset often you will find a different message depending on whether an e-mail
address exists or not. By itself this might not look like a big deal but it can give an attacker information which
can be used in a phishing attack. If the attacker knows you have a registered account at a site, the attacker can
for example create a phishing mail and send it to the user. The user might be more tempted to click the e-mail because
the user has a valid account at the website. On the other hand for some websites this is not really important but
some website users would like some more privacy.
The screenshots below are taken from a real website:
image:images/reset2.png[align="top", {half-size}]
image:images/reset1.png[align="top", {half-size}]
Below you see how Slack implemented the same two pages, no matter what e-mail address you enter the message will
be exactly the same:
image:images/slack1.png[{half-size}]
image:images/slack2.png[{half-size}]

View File

@ -33,6 +33,7 @@
<module>auth-bypass</module>
<module>missing-function-ac</module>
<module>csrf</module>
<module>password-reset</module>
<!-- uncomment below to include lesson template in build, also uncomment the dependency in webgoat-server/pom.xml to have it run in the project fully -->
<!--<module>webgoat-lesson-template</module>-->
</modules>

View File

@ -1,4 +1,6 @@
We now explained the basic steps involved in an SQL injection. In this assignment you will need to combine all
the things we explained in the SQL lessons.
Goal: Can you login as Tom?
Have fun!

View File

@ -190,6 +190,11 @@
<artifactId>missing-function-ac</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>password-reset</artifactId>
<version>${project.version}</version>
</dependency>
<!--uncommment below to run/include lesson template in WebGoat Build-->
<!--<dependency>-->