New lesson working

This commit is contained in:
Nanne Baars
2018-05-26 15:09:18 +02:00
parent eaf68d38c5
commit f8a7a61e85
16 changed files with 172 additions and 231 deletions

View File

@ -0,0 +1,22 @@
package org.owasp.webgoat.plugin.resetlink;
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;
}

View File

@ -0,0 +1,149 @@
package org.owasp.webgoat.plugin.resetlink;
import com.google.common.collect.EvictingQueue;
import com.google.common.collect.Maps;
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.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.ui.Model;
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.springframework.web.bind.annotation.RequestMethod.POST;
/**
* @author nbaars
* @since 8/20/17.
*/
@AssignmentPath("/PasswordReset/reset")
public class ResetLinkAssignment extends AssignmentEndpoint {
private static final String PASSWORD_TOM_9 = "somethingVeryRandomWhichNoOneWillEverTypeInAsPasswordForTom";
private static final String TOM_EMAIL = "tom@webgoat-cloud.org";
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/PasswordReset/reset/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";
private final RestTemplate restTemplate;
private final String webWolfMailURL;
private final String webwolfLandingURL;
public ResetLinkAssignment(RestTemplate restTemplate, @Value("${webwolf.url.mail}") String webWolfMailURL, @Value("${webwolf.url.landingpage}") String webwolfLandingURL) {
this.restTemplate = restTemplate;
this.webWolfMailURL = webWolfMailURL;
this.webwolfLandingURL = webwolfLandingURL;
}
@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 (org.springframework.util.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) {
int index = email.indexOf("@");
String username = email.substring(0, index == -1 ? email.length() : index);
PasswordResetEmail mail = PasswordResetEmail.builder()
.title("Your password reset link")
.contents(String.format(TEMPLATE, host, resetLink))
.sender("password-reset@webgoat-cloud.net")
.recipient(username)
.time(LocalDateTime.now()).build();
restTemplate.postForEntity(webWolfMailURL, mail, Object.class);
}
/**
* 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();
HttpEntity httpEntity = new HttpEntity(httpHeaders);
new RestTemplate().exchange(String.format("http://%s/PasswordReset/reset/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("test").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 (!org.springframework.util.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);
}
}

View File

@ -111,7 +111,8 @@
</div>
<div class="form-group">
<label>What is your favorite color?</label>
<input class="form-control" placeholder="Answer security question" type="text" name="securityQuestion"/>
<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>
@ -130,5 +131,108 @@
</div>
</div>
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:PasswordReset_host_header.adoc"></div>
<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>
<div class="container-fluid">
<div class="row">
<div class="col-md-3">
<h4 style="border-bottom: 1px solid #c5c5c5;">
<i class="glyphicon glyphicon-user"></i>
Account Access
</h4>
<div style="padding: 20px;" id="password-login">
<form id="login-form" class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form"
action="/WebGoat/PasswordReset/reset/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="#" onclick="showPasswordReset()">
<small>Forgot your password?</small>
</a>
</p>
</div>
</fieldset>
</form>
</div>
<div style="display: none;" id="password-reset">
<h4 class="">
Forgot your password?
</h4>
<form class="attack-form" accept-charset="UNKNOWN"
method="POST" name="form"
action="/WebGoat/PasswordReset/reset/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="#" onclick="showPassword()">
<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>

View File

@ -7,3 +7,13 @@ password-reset-simple.email_mismatch=Of course you can send mail to user {0} how
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.
password-reset-no-user=Please supply a valid e-mail address.
password-reset-solved=Congratulations you solved the assignment, please type in the following code in the e-mail field: {0}
password-reset-not-solved=Sorry but you did not redirect the reset link to WebWolf
password-reset-hint1=Try to send a password reset link to your own account at {user}@webgoat.org, you can read this e-mail in WebWolf.
password-reset-hint2=Look at the link, can you think how the server creates this link?
password-reset-hint3=Tom clicks all the links he receives in his mailbox, you can use the landing page in WebWolf to get the reset link...
password-reset-hint4=The link points to localhost:8080/PasswordReset/.... can you change the host to localhost:8081
password-reset-hint5=Intercept the request and change the host header

View File

@ -7,4 +7,16 @@ $(document).ready(function() {
e.preventDefault();
$('div#form-olvidado').toggle('500');
});
});
});
function showPasswordReset() {
console.log("clicking")
$('#password-reset').show();
$('#password-login').hide();
}
function showPassword() {
console.log("clicking")
$('#password-login').show();
$('#password-reset').hide();
}

View File

@ -12,6 +12,7 @@ The time out is necessary to restrict the attack window, having a link opens up
== 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.
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. If you did submit is in the e-mail address and submit again.

View File

@ -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>

View File

@ -0,0 +1,32 @@
<!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/PasswordReset/reset/change-password" th:object="${form}">
<h2 class="sign_up_title">Reset your password</h2>
<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="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>

View File

@ -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>