Refactoring (#1201)
* Some initial refactoring * Make it one application * Got it working * Fix problem on Windows * Move WebWolf * Move first lesson * Moved all lessons * Fix pom.xml * Fix tests * Add option to initialize a lesson This way we can create content for each user inside a lesson. The initialize method will be called when a new user is created or when a lesson reset happens * Clean up pom.xml files * Remove fetching labels based on language. We only support English at the moment, all the lesson explanations are written in English which makes it very difficult to translate. If we only had labels it would make sense to support multiple languages * Fix SonarLint issues * And move it all to the main project * Fix for documentation paths * Fix pom warnings * Remove PMD as it does not work * Update release notes about refactoring Update release notes about refactoring Update release notes about refactoring * Fix lesson template * Update release notes * Keep it in the same repo in Dockerhub * Update documentation to show how the connection is obtained. Resolves: #1180 * Rename all integration tests * Remove command from Dockerfile * Simplify GitHub actions Currently, we use a separate actions for pull-requests and branch build. This is now consolidated in one action. The PR action triggers always, it now only trigger when the PR is opened and not in draft. Running all platforms on a branch build is a bit too much, it is better to only run all platforms when someone opens a PR. * Remove duplicate entry from release notes * Add explicit registry for base image * Lesson scanner not working when fat jar When running the fat jar we have to take into account we are reading from the jar file and not the filesystem. In this case you cannot use `getFile` for example. * added info in README and fixed release docker * changed base image and added ignore file Co-authored-by: Zubcevic.com <rene@zubcevic.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
package org.owasp.webgoat.lessons.challenges;
|
||||
|
||||
import org.owasp.webgoat.container.lessons.Category;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 3/21/17.
|
||||
*/
|
||||
public class ChallengeIntro extends Lesson {
|
||||
|
||||
@Override
|
||||
public Category getDefaultCategory() {
|
||||
return Category.CHALLENGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "challenge0.title";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
|
||||
*
|
||||
* Copyright (c) 2002 - 2019 Bruce Mayhew
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Getting Source ==============
|
||||
*
|
||||
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
|
||||
*/
|
||||
|
||||
package org.owasp.webgoat.lessons.challenges;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 8/20/17.
|
||||
*/
|
||||
@Builder
|
||||
@Data
|
||||
public class Email implements Serializable {
|
||||
|
||||
private LocalDateTime time;
|
||||
private String contents;
|
||||
private String sender;
|
||||
private String title;
|
||||
private String recipient;
|
||||
}
|
||||
88
src/main/java/org/owasp/webgoat/lessons/challenges/Flag.java
Normal file
88
src/main/java/org/owasp/webgoat/lessons/challenges/Flag.java
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
|
||||
*
|
||||
* Copyright (c) 2002 - 2019 Bruce Mayhew
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Getting Source ==============
|
||||
*
|
||||
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
|
||||
*/
|
||||
|
||||
package org.owasp.webgoat.lessons.challenges;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.owasp.webgoat.container.users.UserTracker;
|
||||
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 3/23/17.
|
||||
*/
|
||||
@RestController
|
||||
public class Flag extends AssignmentEndpoint {
|
||||
|
||||
public static final Map<Integer, String> FLAGS = new HashMap<>();
|
||||
@Autowired
|
||||
private UserTrackerRepository userTrackerRepository;
|
||||
@Autowired
|
||||
private WebSession webSession;
|
||||
|
||||
@AllArgsConstructor
|
||||
private class FlagPosted {
|
||||
@Getter
|
||||
private boolean lessonCompleted;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void initFlags() {
|
||||
IntStream.range(1, 10).forEach(i -> FLAGS.put(i, UUID.randomUUID().toString()));
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/challenge/flag", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@ResponseBody
|
||||
public AttackResult postFlag(@RequestParam String flag) {
|
||||
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
|
||||
String currentChallenge = webSession.getCurrentLesson().getName();
|
||||
int challengeNumber = Integer.valueOf(currentChallenge.substring(currentChallenge.length() - 1, currentChallenge.length()));
|
||||
String expectedFlag = FLAGS.get(challengeNumber);
|
||||
final AttackResult attackResult;
|
||||
if (expectedFlag.equals(flag)) {
|
||||
userTracker.assignmentSolved(webSession.getCurrentLesson(), "Assignment" + challengeNumber);
|
||||
attackResult = success(this).feedback("challenge.flag.correct").build();
|
||||
} else {
|
||||
userTracker.assignmentFailed(webSession.getCurrentLesson());
|
||||
attackResult = failed(this).feedback("challenge.flag.incorrect").build();
|
||||
}
|
||||
userTrackerRepository.save(userTracker);
|
||||
return attackResult;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
|
||||
*
|
||||
* Copyright (c) 2002 - 2019 Bruce Mayhew
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Getting Source ==============
|
||||
*
|
||||
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
|
||||
*/
|
||||
|
||||
package org.owasp.webgoat.lessons.challenges;
|
||||
|
||||
/**
|
||||
* Interface with constants so we can easily change the flags
|
||||
*
|
||||
* @author nbaars
|
||||
* @since 3/23/17.
|
||||
*/
|
||||
public interface SolutionConstants {
|
||||
|
||||
//TODO should be random generated when starting the server
|
||||
String PASSWORD = "!!webgoat_admin_1234!!";
|
||||
String PASSWORD_TOM = "thisisasecretfortomonly";
|
||||
String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2";
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.owasp.webgoat.lessons.challenges.challenge1;
|
||||
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.lessons.challenges.Flag;
|
||||
import org.springframework.util.StringUtils;
|
||||
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.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import static org.owasp.webgoat.lessons.challenges.SolutionConstants.PASSWORD;
|
||||
|
||||
/**
|
||||
* ************************************************************************************************
|
||||
* 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 - 2014 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 August 11, 2016
|
||||
*/
|
||||
@RestController
|
||||
public class Assignment1 extends AssignmentEndpoint {
|
||||
|
||||
@PostMapping("/challenge/1")
|
||||
@ResponseBody
|
||||
public AttackResult completed(@RequestParam String username, @RequestParam String password, HttpServletRequest request) {
|
||||
boolean ipAddressKnown = true;
|
||||
boolean passwordCorrect = "admin".equals(username) && PASSWORD.replace("1234", String.format("%04d",ImageServlet.PINCODE)).equals(password);
|
||||
if (passwordCorrect && ipAddressKnown) {
|
||||
return success(this).feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(1)).build();
|
||||
} else if (passwordCorrect) {
|
||||
return failed(this).feedback("ip.address.unknown").build();
|
||||
}
|
||||
return failed(this).build();
|
||||
}
|
||||
|
||||
public static boolean containsHeader(HttpServletRequest request) {
|
||||
return StringUtils.hasText(request.getHeader("X-Forwarded-For"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.owasp.webgoat.lessons.challenges.challenge1;
|
||||
|
||||
import org.owasp.webgoat.container.lessons.Category;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 3/21/17.
|
||||
*/
|
||||
@Component
|
||||
public class Challenge1 extends Lesson {
|
||||
|
||||
@Override
|
||||
public Category getDefaultCategory() {
|
||||
return Category.CHALLENGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "challenge1.title";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.owasp.webgoat.lessons.challenges.challenge1;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
|
||||
@RestController
|
||||
public class ImageServlet extends HttpServlet {
|
||||
|
||||
private static final long serialVersionUID = 9132775506936676850L;
|
||||
static final public int PINCODE = new SecureRandom().nextInt(10000);
|
||||
|
||||
@RequestMapping(method = {GET, POST}, value = "/challenge/logo", produces = MediaType.IMAGE_PNG_VALUE)
|
||||
@ResponseBody
|
||||
public byte[] logo() throws IOException {
|
||||
byte[] in = new ClassPathResource("lessons/challenges/images/webgoat2.png").getInputStream().readAllBytes();
|
||||
|
||||
String pincode = String.format("%04d", PINCODE);
|
||||
|
||||
in[81216]=(byte) pincode.charAt(0);
|
||||
in[81217]=(byte) pincode.charAt(1);
|
||||
in[81218]=(byte) pincode.charAt(2);
|
||||
in[81219]=(byte) pincode.charAt(3);
|
||||
|
||||
return in;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
|
||||
*
|
||||
* Copyright (c) 2002 - 2019 Bruce Mayhew
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Getting Source ==============
|
||||
*
|
||||
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
|
||||
*/
|
||||
|
||||
package org.owasp.webgoat.lessons.challenges.challenge5;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.owasp.webgoat.container.LessonDataSource;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.lessons.challenges.Flag;
|
||||
import org.springframework.util.StringUtils;
|
||||
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.bind.annotation.RestController;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
|
||||
@RestController
|
||||
@Slf4j
|
||||
public class Assignment5 extends AssignmentEndpoint {
|
||||
|
||||
private final LessonDataSource dataSource;
|
||||
|
||||
public Assignment5(LessonDataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@PostMapping("/challenge/5")
|
||||
@ResponseBody
|
||||
public AttackResult login(@RequestParam String username_login, @RequestParam String password_login) throws Exception {
|
||||
if (!StringUtils.hasText(username_login) || !StringUtils.hasText(password_login)) {
|
||||
return failed(this).feedback("required4").build();
|
||||
}
|
||||
if (!"Larry".equals(username_login)) {
|
||||
return failed(this).feedback("user.not.larry").feedbackArgs(username_login).build();
|
||||
}
|
||||
try (var connection = dataSource.getConnection()) {
|
||||
PreparedStatement statement = connection.prepareStatement("select password from challenge_users where userid = '" + username_login + "' and password = '" + password_login + "'");
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
|
||||
if (resultSet.next()) {
|
||||
return success(this).feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(5)).build();
|
||||
} else {
|
||||
return failed(this).feedback("challenge.close").build();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
|
||||
*
|
||||
* Copyright (c) 2002 - 2019 Bruce Mayhew
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Getting Source ==============
|
||||
*
|
||||
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
|
||||
*/
|
||||
|
||||
package org.owasp.webgoat.lessons.challenges.challenge5;
|
||||
|
||||
import org.owasp.webgoat.container.lessons.Category;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 3/21/17.
|
||||
*/
|
||||
@Component
|
||||
public class Challenge5 extends Lesson {
|
||||
|
||||
@Override
|
||||
public Category getDefaultCategory() {
|
||||
return Category.CHALLENGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "challenge5.title";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package org.owasp.webgoat.lessons.challenges.challenge7;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.lessons.challenges.Email;
|
||||
import org.owasp.webgoat.lessons.challenges.SolutionConstants;
|
||||
import org.owasp.webgoat.lessons.challenges.Flag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
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.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 4/8/17.
|
||||
*/
|
||||
@RestController
|
||||
@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 RestTemplate restTemplate;
|
||||
@Value("${webwolf.mail.url}")
|
||||
private String webWolfMailURL;
|
||||
|
||||
@GetMapping("/challenge/7/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>" + Flag.FLAGS.get(7) + "</b>");
|
||||
}
|
||||
return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT).body("That is not the reset link for admin");
|
||||
}
|
||||
|
||||
@PostMapping("/challenge/7")
|
||||
@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());
|
||||
Email mail = Email.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();
|
||||
restTemplate.postForEntity(webWolfMailURL, mail, Object.class);
|
||||
}
|
||||
}
|
||||
return success(this).feedback("email.send").feedbackArgs(email).build();
|
||||
}
|
||||
|
||||
@GetMapping(value = "/challenge/7/.git", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
@ResponseBody
|
||||
public ClassPathResource git() {
|
||||
return new ClassPathResource("challenge7/git.zip");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.owasp.webgoat.lessons.challenges.challenge7;
|
||||
|
||||
import org.owasp.webgoat.container.lessons.Category;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 3/21/17.
|
||||
*/
|
||||
@Component
|
||||
public class Challenge7 extends Lesson {
|
||||
|
||||
@Override
|
||||
public Category getDefaultCategory() {
|
||||
return Category.CHALLENGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "challenge7.title";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,688 @@
|
||||
package org.owasp.webgoat.lessons.challenges.challenge7;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* 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.lessons.challenges.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,69 @@
|
||||
package org.owasp.webgoat.lessons.challenges.challenge8;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.lessons.challenges.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 org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 4/8/17.
|
||||
*/
|
||||
@RestController
|
||||
@Slf4j
|
||||
public class Assignment8 extends AssignmentEndpoint {
|
||||
|
||||
private static final Map<Integer, Integer> votes = new HashMap<>();
|
||||
|
||||
static {
|
||||
votes.put(1, 400);
|
||||
votes.put(2, 120);
|
||||
votes.put(3, 140);
|
||||
votes.put(4, 150);
|
||||
votes.put(5, 300);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/challenge/8/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")) {
|
||||
var json = Map.of("error", true, "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("/challenge/8/votes/")
|
||||
public ResponseEntity<?> getVotes() {
|
||||
return ResponseEntity.ok(votes.entrySet().stream().collect(Collectors.toMap(e -> "" + e.getKey(), e -> e.getValue())));
|
||||
}
|
||||
|
||||
@GetMapping("/challenge/8/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);
|
||||
var json = Map.of("average", (int) Math.ceil((double) categories / totalNumberOfVotes));
|
||||
return ResponseEntity.ok(json);
|
||||
}
|
||||
|
||||
@GetMapping("/challenge/8/notUsed")
|
||||
public AttackResult notUsed() {
|
||||
throw new IllegalStateException("Should never be called, challenge specific method");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.owasp.webgoat.lessons.challenges.challenge8;
|
||||
|
||||
import org.owasp.webgoat.container.lessons.Category;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 3/21/17.
|
||||
*/
|
||||
@Component
|
||||
public class Challenge8 extends Lesson {
|
||||
|
||||
@Override
|
||||
public Category getDefaultCategory() {
|
||||
return Category.CHALLENGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "challenge8.title";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user