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

@ -15,6 +15,4 @@ public interface SolutionConstants {
String PASSWORD_LARRY = "larryknows";
String JWT_PASSWORD = "victory";
String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2";
String PASSWORD_TOM_9 = "somethingVeryRandomWhichNoOneWillEverTypeInAsPasswordForTom";
String TOM_EMAIL = "tom@webgoat-cloud.org";
}

View File

@ -1,161 +0,0 @@
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.plugin.Email;
import org.owasp.webgoat.users.UserRepository;
import org.owasp.webgoat.users.WebGoatUser;
import org.springframework.beans.factory.annotation.Autowired;
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.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 RestTemplate restTemplate;
@Autowired
private UserRepository userRepository;
@Value("${webwolf.url}")
private String webWolfURL;
@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();
Email mail = Email.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();
restTemplate.postForEntity(webWolfURL + "/WebWolf/mail", 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();
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);
}
}

View File

@ -1,39 +0,0 @@
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";
}
}

View File

@ -1,22 +0,0 @@
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;
}

View File

@ -1,109 +0,0 @@
<!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>

View File

@ -1,10 +0,0 @@
$(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');
});
});

View File

@ -1,3 +0,0 @@
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.

View File

@ -1,19 +0,0 @@
<!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

@ -1,48 +0,0 @@
<!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>

View File

@ -1,19 +0,0 @@
<!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>