- Added new challenges
- Added new webapplication called WebWolf to make attacks more realistic - Added WebWolf lesson to explain the concepts behind this new application
This commit is contained in:
@ -45,7 +45,7 @@ public class Flag extends Endpoint {
|
||||
|
||||
@PostConstruct
|
||||
public void initFlags() {
|
||||
IntStream.range(1, 7).forEach(i -> FLAGS.put(i, UUID.randomUUID().toString()));
|
||||
IntStream.range(1, 10).forEach(i -> FLAGS.put(i, UUID.randomUUID().toString()));
|
||||
FLAGS.entrySet().stream().forEach(e -> log.debug("Flag {} {}", e.getKey(), e.getValue()));
|
||||
}
|
||||
|
||||
|
@ -14,5 +14,7 @@ public interface SolutionConstants {
|
||||
String PASSWORD_TOM = "thisisasecretfortomonly";
|
||||
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";
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import com.google.common.collect.EvictingQueue;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.io.Files;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
@ -45,6 +45,7 @@ import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
* @since 4/8/17.
|
||||
*/
|
||||
@AssignmentPath("/challenge/3")
|
||||
@Slf4j
|
||||
public class Assignment3 extends AssignmentEndpoint {
|
||||
|
||||
@Value("${webgoat.server.directory}")
|
||||
@ -66,10 +67,11 @@ public class Assignment3 extends AssignmentEndpoint {
|
||||
@PostConstruct
|
||||
@SneakyThrows
|
||||
public void copyFile() {
|
||||
File targetDirectory = new File(webGoatHomeDirectory, "/challenges");
|
||||
File targetDirectory = new File(webGoatHomeDirectory);
|
||||
if (!targetDirectory.exists()) {
|
||||
targetDirectory.mkdir();
|
||||
}
|
||||
log.info("Copied secret.txt to: {}", targetDirectory);
|
||||
Files.write(secretContents, new File(targetDirectory, "secret.txt"), Charset.defaultCharset());
|
||||
}
|
||||
|
||||
@ -106,14 +108,14 @@ public class Assignment3 extends AssignmentEndpoint {
|
||||
userComments.put(webSession.getUserName(), comments);
|
||||
}
|
||||
if (checkSolution(comment)) {
|
||||
attackResult = success().feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(2)).build();
|
||||
attackResult = success().feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(3)).build();
|
||||
}
|
||||
return attackResult;
|
||||
}
|
||||
|
||||
private boolean checkSolution(Comment comment) {
|
||||
if (StringUtils.equals(comment.getText(), secretContents)) {
|
||||
comment.setText("Congratulations to " + webSession.getUserName() + " for finding the flag!!");
|
||||
if (comment.getText().contains(secretContents)) {
|
||||
comment.setText("Congratulations to " + webSession.getUserName() + " for finding the flag!! Check your original response where you posted the XXE attack ");
|
||||
comments.add(comment);
|
||||
return true;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import org.owasp.webgoat.plugin.Flag;
|
||||
import org.owasp.webgoat.session.DatabaseUtilities;
|
||||
import org.owasp.webgoat.session.WebSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
@ -38,6 +39,13 @@ public class Assignment5 extends AssignmentEndpoint {
|
||||
Connection connection = DatabaseUtilities.getConnection(webSession);
|
||||
checkDatabase(connection);
|
||||
|
||||
if (!StringUtils.hasText(username_login) || !StringUtils.hasText(password_login)) {
|
||||
return failed().feedback("required4").build();
|
||||
}
|
||||
if (!"Larry".equals(username_login)) {
|
||||
return failed().feedback("user.not.larry").feedbackArgs(username_login).build();
|
||||
}
|
||||
|
||||
PreparedStatement statement = connection.prepareStatement("select password from " + USERS_TABLE_NAME + " where userid = '" + username_login + "' and password = '" + password_login + "'");
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
|
||||
|
@ -0,0 +1,84 @@
|
||||
package org.owasp.webgoat.plugin.challenge7;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.owasp.webgoat.mail.IncomingMailEvent;
|
||||
import org.owasp.webgoat.plugin.SolutionConstants;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.jms.core.JmsTemplate;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.owasp.webgoat.plugin.Flag.FLAGS;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 4/8/17.
|
||||
*/
|
||||
@AssignmentPath("/challenge/7")
|
||||
@Slf4j
|
||||
public class Assignment7 extends AssignmentEndpoint {
|
||||
|
||||
private static final String TEMPLATE = "Hi, you requested a password reset link, please use this " +
|
||||
"<a target='_blank' href='%s:8080/WebGoat/challenge/7/reset-password/%s'>link</a> to reset your password." +
|
||||
"\n \n\n" +
|
||||
"If you did not request this password change you can ignore this message." +
|
||||
"\n" +
|
||||
"If you have any comments or questions, please do not hesitate to reach us at support@webgoat-cloud.org" +
|
||||
"\n\n" +
|
||||
"Kind regards, \nTeam WebGoat";
|
||||
|
||||
@Autowired
|
||||
private JmsTemplate jmsTemplate;
|
||||
|
||||
@GetMapping("/reset-password/{link}")
|
||||
public ResponseEntity<String> resetPassword(@PathVariable(value = "link") String link) {
|
||||
if (link.equals(SolutionConstants.ADMIN_PASSWORD_LINK)) {
|
||||
return ResponseEntity.accepted().body("<h1>Success!!</h1>" +
|
||||
"<img src='/WebGoat/images/hi-five-cat.jpg'>" +
|
||||
"<br/><br/>Here is your flag: " + "<b>" + FLAGS.get(7) + "</b>");
|
||||
}
|
||||
return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT).body("That is not the reset link for admin");
|
||||
}
|
||||
|
||||
@RequestMapping(method = POST)
|
||||
@ResponseBody
|
||||
public AttackResult sendPasswordResetLink(@RequestParam String email, HttpServletRequest request) throws URISyntaxException {
|
||||
if (StringUtils.hasText(email)) {
|
||||
String username = email.substring(0, email.indexOf("@"));
|
||||
if (StringUtils.hasText(username)) {
|
||||
URI uri = new URI(request.getRequestURL().toString());
|
||||
IncomingMailEvent mail = IncomingMailEvent.builder()
|
||||
.title("Your password reset link for challenge 7")
|
||||
.contents(String.format(TEMPLATE, uri.getScheme() + "://" + uri.getHost(), new PasswordResetLink().createPasswordReset(username, "webgoat")))
|
||||
.sender("password-reset@webgoat-cloud.net")
|
||||
.recipient(username)
|
||||
.time(LocalDateTime.now()).build();
|
||||
jmsTemplate.convertAndSend("mailbox", mail);
|
||||
}
|
||||
}
|
||||
return success().feedback("email.send").feedbackArgs(email).build();
|
||||
}
|
||||
|
||||
@RequestMapping(method = GET, value = "/.git", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
@ResponseBody
|
||||
@SneakyThrows
|
||||
public ClassPathResource git() {
|
||||
return new ClassPathResource("challenge7/git.zip");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
package org.owasp.webgoat.plugin.challenge7;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.owasp.webgoat.lessons.Category;
|
||||
import org.owasp.webgoat.lessons.NewLesson;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 3/21/17.
|
||||
*/
|
||||
public class Challenge7 extends NewLesson {
|
||||
|
||||
@Override
|
||||
public Category getDefaultCategory() {
|
||||
return Category.CHALLENGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getHints() {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getDefaultRanking() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "challenge7.title";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "Challenge7";
|
||||
}
|
||||
}
|
@ -0,0 +1,689 @@
|
||||
package org.owasp.webgoat.plugin.challenge7;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* MD5 hash generator.
|
||||
* More information about this class is available from <a target="_top" href=
|
||||
* "http://ostermiller.org/utils/MD5.html">ostermiller.org</a>.
|
||||
* <p>
|
||||
* This class takes as input a message of arbitrary length and produces
|
||||
* as output a 128-bit "fingerprint" or "message digest" of the input.
|
||||
* It is conjectured that it is computationally infeasible to produce
|
||||
* two messages having the same message digest, or to produce any
|
||||
* message having a given pre-specified target message digest. The MD5
|
||||
* algorithm is intended for digital signature applications, where a
|
||||
* large file must be "compressed" in a secure manner before being
|
||||
* encrypted with a private (secret) key under a public-key cryptosystem
|
||||
* such as RSA.
|
||||
* <p>
|
||||
* For more information see RFC1321.
|
||||
*
|
||||
* @author Santeri Paavolainen http://santtu.iki.fi/md5/
|
||||
* @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public class MD5 {
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public MD5() {
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Command line program that will take files as arguments
|
||||
* and output the MD5 sum for each file.
|
||||
*
|
||||
* @param args command line arguments
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
if (args.length == 0) {
|
||||
System.err.println("Please specify a file.");
|
||||
} else {
|
||||
for (String element : args) {
|
||||
try {
|
||||
System.out.println(MD5.getHashString(new File(element)) + " " + element);
|
||||
} catch (IOException x) {
|
||||
System.err.println(x.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this hash sum as an array of 16 bytes.
|
||||
*
|
||||
* @return Array of 16 bytes, the hash of all updated bytes.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public byte[] getHash() {
|
||||
if (!finalState.valid) {
|
||||
finalState.copy(workingState);
|
||||
long bitCount = finalState.bitCount;
|
||||
// Compute the number of left over bits
|
||||
int leftOver = (int) (((bitCount >>> 3)) & 0x3f);
|
||||
// Compute the amount of padding to add based on number of left over bits.
|
||||
int padlen = (leftOver < 56) ? (56 - leftOver) : (120 - leftOver);
|
||||
// add the padding
|
||||
update(finalState, padding, 0, padlen);
|
||||
// add the length (computed before padding was added)
|
||||
update(finalState, encode(bitCount), 0, 8);
|
||||
finalState.valid = true;
|
||||
}
|
||||
// make a copy of the hash before returning it.
|
||||
return encode(finalState.state, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 32-character hex representation of this hash.
|
||||
*
|
||||
* @return String representation of this object's hash.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public String getHashString() {
|
||||
return toHex(this.getHash());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MD5 hash of the given byte array.
|
||||
*
|
||||
* @param b byte array for which an MD5 hash is desired.
|
||||
* @return Array of 16 bytes, the hash of all updated bytes.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public static byte[] getHash(byte[] b) {
|
||||
MD5 md5 = new MD5();
|
||||
md5.update(b);
|
||||
return md5.getHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MD5 hash of the given byte array.
|
||||
*
|
||||
* @param b byte array for which an MD5 hash is desired.
|
||||
* @return 32-character hex representation the data's MD5 hash.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public static String getHashString(byte[] b) {
|
||||
MD5 md5 = new MD5();
|
||||
md5.update(b);
|
||||
return md5.getHashString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MD5 hash the data on the given InputStream.
|
||||
*
|
||||
* @param in byte array for which an MD5 hash is desired.
|
||||
* @return Array of 16 bytes, the hash of all updated bytes.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public static byte[] getHash(InputStream in) throws IOException {
|
||||
MD5 md5 = new MD5();
|
||||
byte[] buffer = new byte[1024];
|
||||
int read;
|
||||
while ((read = in.read(buffer)) != -1) {
|
||||
md5.update(buffer, read);
|
||||
}
|
||||
return md5.getHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MD5 hash the data on the given InputStream.
|
||||
*
|
||||
* @param in byte array for which an MD5 hash is desired.
|
||||
* @return 32-character hex representation the data's MD5 hash.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public static String getHashString(InputStream in) throws IOException {
|
||||
MD5 md5 = new MD5();
|
||||
byte[] buffer = new byte[1024];
|
||||
int read;
|
||||
while ((read = in.read(buffer)) != -1) {
|
||||
md5.update(buffer, read);
|
||||
}
|
||||
return md5.getHashString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MD5 hash of the given file.
|
||||
*
|
||||
* @param f file for which an MD5 hash is desired.
|
||||
* @return Array of 16 bytes, the hash of all updated bytes.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public static byte[] getHash(File f) throws IOException {
|
||||
InputStream is = new FileInputStream(f);
|
||||
byte[] hash = getHash(is);
|
||||
is.close();
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MD5 hash of the given file.
|
||||
*
|
||||
* @param f file array for which an MD5 hash is desired.
|
||||
* @return 32-character hex representation the data's MD5 hash.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public static String getHashString(File f) throws IOException {
|
||||
InputStream is = new FileInputStream(f);
|
||||
String hash = getHashString(is);
|
||||
is.close();
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MD5 hash of the given String.
|
||||
* The string is converted to bytes using the current
|
||||
* platform's default character encoding.
|
||||
*
|
||||
* @param s String for which an MD5 hash is desired.
|
||||
* @return Array of 16 bytes, the hash of all updated bytes.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public static byte[] getHash(String s) {
|
||||
MD5 md5 = new MD5();
|
||||
md5.update(s);
|
||||
return md5.getHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MD5 hash of the given String.
|
||||
* The string is converted to bytes using the current
|
||||
* platform's default character encoding.
|
||||
*
|
||||
* @param s String for which an MD5 hash is desired.
|
||||
* @return 32-character hex representation the data's MD5 hash.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public static String getHashString(String s) {
|
||||
MD5 md5 = new MD5();
|
||||
md5.update(s);
|
||||
return md5.getHashString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the MD5 hash of the given String.
|
||||
*
|
||||
* @param s String for which an MD5 hash is desired.
|
||||
* @param enc The name of a supported character encoding.
|
||||
* @return Array of 16 bytes, the hash of all updated bytes.
|
||||
* @throws UnsupportedEncodingException If the named encoding is not supported.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public static byte[] getHash(String s, String enc) throws UnsupportedEncodingException {
|
||||
MD5 md5 = new MD5();
|
||||
md5.update(s, enc);
|
||||
return md5.getHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MD5 hash of the given String.
|
||||
*
|
||||
* @param s String for which an MD5 hash is desired.
|
||||
* @param enc The name of a supported character encoding.
|
||||
* @return 32-character hex representation the data's MD5 hash.
|
||||
* @throws UnsupportedEncodingException If the named encoding is not supported.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public static String getHashString(String s, String enc) throws UnsupportedEncodingException {
|
||||
MD5 md5 = new MD5();
|
||||
md5.update(s, enc);
|
||||
return md5.getHashString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reset the MD5 sum to its initial state.
|
||||
*
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public void reset() {
|
||||
workingState.reset();
|
||||
finalState.valid = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 32-character hex representation of this hash.
|
||||
*
|
||||
* @return String representation of this object's hash.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return getHashString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this hash with the given data.
|
||||
* <p>
|
||||
* A state may be passed into this method so that we can add padding
|
||||
* and finalize a md5 hash without limiting our ability to update
|
||||
* more data later.
|
||||
* <p>
|
||||
* If length bytes are not available to be hashed, as many bytes as
|
||||
* possible will be hashed.
|
||||
*
|
||||
* @param state Which state is updated.
|
||||
* @param buffer Array of bytes to be hashed.
|
||||
* @param offset Offset to buffer array.
|
||||
* @param length number of bytes to hash.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
private void update(MD5State state, byte buffer[], int offset, int length) {
|
||||
|
||||
finalState.valid = false;
|
||||
|
||||
// if length goes beyond the end of the buffer, cut it short.
|
||||
if ((length + offset) > buffer.length) {
|
||||
length = buffer.length - offset;
|
||||
}
|
||||
|
||||
// compute number of bytes mod 64
|
||||
// this is what we have sitting in a buffer
|
||||
// that have not been hashed yet
|
||||
int index = (int) (state.bitCount >>> 3) & 0x3f;
|
||||
|
||||
// add the length to the count (translate bytes to bits)
|
||||
state.bitCount += length << 3;
|
||||
|
||||
int partlen = 64 - index;
|
||||
|
||||
int i = 0;
|
||||
if (length >= partlen) {
|
||||
System.arraycopy(buffer, offset, state.buffer, index, partlen);
|
||||
transform(state, decode(state.buffer, 64, 0));
|
||||
for (i = partlen; (i + 63) < length; i += 64) {
|
||||
transform(state, decode(buffer, 64, i));
|
||||
}
|
||||
index = 0;
|
||||
}
|
||||
|
||||
// buffer remaining input
|
||||
if (i < length) {
|
||||
for (int start = i; i < length; i++) {
|
||||
state.buffer[index + i - start] = buffer[i + offset];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this hash with the given data.
|
||||
* <p>
|
||||
* If length bytes are not available to be hashed, as many bytes as
|
||||
* possible will be hashed.
|
||||
*
|
||||
* @param buffer Array of bytes to be hashed.
|
||||
* @param offset Offset to buffer array.
|
||||
* @param length number of bytes to hash.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public void update(byte buffer[], int offset, int length) {
|
||||
update(workingState, buffer, offset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this hash with the given data.
|
||||
* <p>
|
||||
* If length bytes are not available to be hashed, as many bytes as
|
||||
* possible will be hashed.
|
||||
*
|
||||
* @param buffer Array of bytes to be hashed.
|
||||
* @param length number of bytes to hash.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public void update(byte buffer[], int length) {
|
||||
update(buffer, 0, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this hash with the given data.
|
||||
*
|
||||
* @param buffer Array of bytes to be hashed.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public void update(byte buffer[]) {
|
||||
update(buffer, 0, buffer.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this hash with a single byte.
|
||||
*
|
||||
* @param b byte to be hashed.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public void update(byte b) {
|
||||
byte buffer[] = new byte[1];
|
||||
buffer[0] = b;
|
||||
update(buffer, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this hash with a String.
|
||||
* The string is converted to bytes using the current
|
||||
* platform's default character encoding.
|
||||
*
|
||||
* @param s String to be hashed.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public void update(String s) {
|
||||
update(s.getBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this hash with a String.
|
||||
*
|
||||
* @param s String to be hashed.
|
||||
* @param enc The name of a supported character encoding.
|
||||
* @throws UnsupportedEncodingException If the named encoding is not supported.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
public void update(String s, String enc) throws UnsupportedEncodingException {
|
||||
update(s.getBytes(enc));
|
||||
}
|
||||
|
||||
/**
|
||||
* The current state from which the hash sum
|
||||
* can be computed or updated.
|
||||
*
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
private MD5State workingState = new MD5State();
|
||||
|
||||
/**
|
||||
* Cached copy of the final MD5 hash sum. This is created when
|
||||
* the hash is requested and it is invalidated when the hash
|
||||
* is updated.
|
||||
*
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
private MD5State finalState = new MD5State();
|
||||
|
||||
/**
|
||||
* Temporary buffer cached here for performance reasons.
|
||||
*
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
private int[] decodeBuffer = new int[16];
|
||||
|
||||
/**
|
||||
* 64 bytes of padding that can be added if the length
|
||||
* is not divisible by 64.
|
||||
*
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
private static final byte padding[] = {
|
||||
(byte) 0x80, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains internal state of the MD5 class.
|
||||
* Passes MD5 test suite as defined in RFC1321.
|
||||
*
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
private class MD5State {
|
||||
|
||||
/**
|
||||
* True if this state is valid.
|
||||
*
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
private boolean valid = true;
|
||||
|
||||
/**
|
||||
* Reset to initial state.
|
||||
*
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
private void reset() {
|
||||
state[0] = 0x67452301;
|
||||
state[1] = 0xefcdab89;
|
||||
state[2] = 0x98badcfe;
|
||||
state[3] = 0x10325476;
|
||||
|
||||
bitCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 128-byte state
|
||||
*
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
private int state[] = new int[4];
|
||||
|
||||
/**
|
||||
* 64-bit count of the number of bits that have been hashed.
|
||||
*
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
private long bitCount;
|
||||
|
||||
/**
|
||||
* 64-byte buffer (512 bits) for storing to-be-hashed characters
|
||||
*
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
private byte buffer[] = new byte[64];
|
||||
|
||||
private MD5State() {
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this state to be exactly the same as some other.
|
||||
*
|
||||
* @param from state to copy from.
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
private void copy(MD5State from) {
|
||||
System.arraycopy(from.buffer, 0, this.buffer, 0, this.buffer.length);
|
||||
System.arraycopy(from.state, 0, this.state, 0, this.state.length);
|
||||
this.valid = from.valid;
|
||||
this.bitCount = from.bitCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Turns array of bytes into string representing each byte as
|
||||
* a two digit unsigned hex number.
|
||||
*
|
||||
* @param hash Array of bytes to convert to hex-string
|
||||
* @return Generated hex string
|
||||
* @since ostermillerutils 1.00.00
|
||||
*/
|
||||
private static String toHex(byte hash[]) {
|
||||
StringBuffer buf = new StringBuffer(hash.length * 2);
|
||||
for (byte element : hash) {
|
||||
int intVal = element & 0xff;
|
||||
if (intVal < 0x10) {
|
||||
// append a zero before a one digit hex
|
||||
// number to make it two digits.
|
||||
buf.append("0");
|
||||
}
|
||||
buf.append(Integer.toHexString(intVal));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static int FF(int a, int b, int c, int d, int x, int s, int ac) {
|
||||
a += ((b & c) | (~b & d));
|
||||
a += x;
|
||||
a += ac;
|
||||
//return rotateLeft(a, s) + b;
|
||||
a = (a << s) | (a >>> (32 - s));
|
||||
return a + b;
|
||||
}
|
||||
|
||||
private static int GG(int a, int b, int c, int d, int x, int s, int ac) {
|
||||
a += ((b & d) | (c & ~d));
|
||||
a += x;
|
||||
a += ac;
|
||||
//return rotateLeft(a, s) + b;
|
||||
a = (a << s) | (a >>> (32 - s));
|
||||
return a + b;
|
||||
}
|
||||
|
||||
private static int HH(int a, int b, int c, int d, int x, int s, int ac) {
|
||||
a += (b ^ c ^ d);
|
||||
a += x;
|
||||
a += ac;
|
||||
//return rotateLeft(a, s) + b;
|
||||
a = (a << s) | (a >>> (32 - s));
|
||||
return a + b;
|
||||
}
|
||||
|
||||
private static int II(int a, int b, int c, int d, int x, int s, int ac) {
|
||||
a += (c ^ (b | ~d));
|
||||
a += x;
|
||||
a += ac;
|
||||
//return rotateLeft(a, s) + b;
|
||||
a = (a << s) | (a >>> (32 - s));
|
||||
return a + b;
|
||||
}
|
||||
|
||||
private static byte[] encode(long l) {
|
||||
byte[] out = new byte[8];
|
||||
out[0] = (byte) (l & 0xff);
|
||||
out[1] = (byte) ((l >>> 8) & 0xff);
|
||||
out[2] = (byte) ((l >>> 16) & 0xff);
|
||||
out[3] = (byte) ((l >>> 24) & 0xff);
|
||||
out[4] = (byte) ((l >>> 32) & 0xff);
|
||||
out[5] = (byte) ((l >>> 40) & 0xff);
|
||||
out[6] = (byte) ((l >>> 48) & 0xff);
|
||||
out[7] = (byte) ((l >>> 56) & 0xff);
|
||||
return out;
|
||||
}
|
||||
|
||||
private static byte[] encode(int input[], int len) {
|
||||
byte[] out = new byte[len];
|
||||
int i, j;
|
||||
for (i = j = 0; j < len; i++, j += 4) {
|
||||
out[j] = (byte) (input[i] & 0xff);
|
||||
out[j + 1] = (byte) ((input[i] >>> 8) & 0xff);
|
||||
out[j + 2] = (byte) ((input[i] >>> 16) & 0xff);
|
||||
out[j + 3] = (byte) ((input[i] >>> 24) & 0xff);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private int[] decode(byte buffer[], int len, int offset) {
|
||||
int i, j;
|
||||
for (i = j = 0; j < len; i++, j += 4) {
|
||||
decodeBuffer[i] = (
|
||||
(buffer[j + offset] & 0xff)) |
|
||||
(((buffer[j + 1 + offset] & 0xff)) << 8) |
|
||||
(((buffer[j + 2 + offset] & 0xff)) << 16) |
|
||||
(((buffer[j + 3 + offset] & 0xff)) << 24
|
||||
);
|
||||
}
|
||||
return decodeBuffer;
|
||||
}
|
||||
|
||||
private static void transform(MD5State state, int[] x) {
|
||||
int a = state.state[0];
|
||||
int b = state.state[1];
|
||||
int c = state.state[2];
|
||||
int d = state.state[3];
|
||||
|
||||
/* Round 1 */
|
||||
a = FF(a, b, c, d, x[0], 7, 0xd76aa478); /* 1 */
|
||||
d = FF(d, a, b, c, x[1], 12, 0xe8c7b756); /* 2 */
|
||||
c = FF(c, d, a, b, x[2], 17, 0x242070db); /* 3 */
|
||||
b = FF(b, c, d, a, x[3], 22, 0xc1bdceee); /* 4 */
|
||||
a = FF(a, b, c, d, x[4], 7, 0xf57c0faf); /* 5 */
|
||||
d = FF(d, a, b, c, x[5], 12, 0x4787c62a); /* 6 */
|
||||
c = FF(c, d, a, b, x[6], 17, 0xa8304613); /* 7 */
|
||||
b = FF(b, c, d, a, x[7], 22, 0xfd469501); /* 8 */
|
||||
a = FF(a, b, c, d, x[8], 7, 0x698098d8); /* 9 */
|
||||
d = FF(d, a, b, c, x[9], 12, 0x8b44f7af); /* 10 */
|
||||
c = FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */
|
||||
b = FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */
|
||||
a = FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */
|
||||
d = FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */
|
||||
c = FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */
|
||||
b = FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */
|
||||
|
||||
/* Round 2 */
|
||||
a = GG(a, b, c, d, x[1], 5, 0xf61e2562); /* 17 */
|
||||
d = GG(d, a, b, c, x[6], 9, 0xc040b340); /* 18 */
|
||||
c = GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */
|
||||
b = GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); /* 20 */
|
||||
a = GG(a, b, c, d, x[5], 5, 0xd62f105d); /* 21 */
|
||||
d = GG(d, a, b, c, x[10], 9, 0x02441453); /* 22 */
|
||||
c = GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */
|
||||
b = GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); /* 24 */
|
||||
a = GG(a, b, c, d, x[9], 5, 0x21e1cde6); /* 25 */
|
||||
d = GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */
|
||||
c = GG(c, d, a, b, x[3], 14, 0xf4d50d87); /* 27 */
|
||||
b = GG(b, c, d, a, x[8], 20, 0x455a14ed); /* 28 */
|
||||
a = GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */
|
||||
d = GG(d, a, b, c, x[2], 9, 0xfcefa3f8); /* 30 */
|
||||
c = GG(c, d, a, b, x[7], 14, 0x676f02d9); /* 31 */
|
||||
b = GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */
|
||||
|
||||
/* Round 3 */
|
||||
a = HH(a, b, c, d, x[5], 4, 0xfffa3942); /* 33 */
|
||||
d = HH(d, a, b, c, x[8], 11, 0x8771f681); /* 34 */
|
||||
c = HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */
|
||||
b = HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */
|
||||
a = HH(a, b, c, d, x[1], 4, 0xa4beea44); /* 37 */
|
||||
d = HH(d, a, b, c, x[4], 11, 0x4bdecfa9); /* 38 */
|
||||
c = HH(c, d, a, b, x[7], 16, 0xf6bb4b60); /* 39 */
|
||||
b = HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */
|
||||
a = HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */
|
||||
d = HH(d, a, b, c, x[0], 11, 0xeaa127fa); /* 42 */
|
||||
c = HH(c, d, a, b, x[3], 16, 0xd4ef3085); /* 43 */
|
||||
b = HH(b, c, d, a, x[6], 23, 0x04881d05); /* 44 */
|
||||
a = HH(a, b, c, d, x[9], 4, 0xd9d4d039); /* 45 */
|
||||
d = HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */
|
||||
c = HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */
|
||||
b = HH(b, c, d, a, x[2], 23, 0xc4ac5665); /* 48 */
|
||||
|
||||
/* Round 4 */
|
||||
a = II(a, b, c, d, x[0], 6, 0xf4292244); /* 49 */
|
||||
d = II(d, a, b, c, x[7], 10, 0x432aff97); /* 50 */
|
||||
c = II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */
|
||||
b = II(b, c, d, a, x[5], 21, 0xfc93a039); /* 52 */
|
||||
a = II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */
|
||||
d = II(d, a, b, c, x[3], 10, 0x8f0ccc92); /* 54 */
|
||||
c = II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */
|
||||
b = II(b, c, d, a, x[1], 21, 0x85845dd1); /* 56 */
|
||||
a = II(a, b, c, d, x[8], 6, 0x6fa87e4f); /* 57 */
|
||||
d = II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */
|
||||
c = II(c, d, a, b, x[6], 15, 0xa3014314); /* 59 */
|
||||
b = II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */
|
||||
a = II(a, b, c, d, x[4], 6, 0xf7537e82); /* 61 */
|
||||
d = II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */
|
||||
c = II(c, d, a, b, x[2], 15, 0x2ad7d2bb); /* 63 */
|
||||
b = II(b, c, d, a, x[9], 21, 0xeb86d391); /* 64 */
|
||||
|
||||
state.state[0] += a;
|
||||
state.state[1] += b;
|
||||
state.state[2] += c;
|
||||
state.state[3] += d;
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package org.owasp.webgoat.plugin.challenge7;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* WARNING: DO NOT CHANGE FILE WITHOUT CHANGING .git contents
|
||||
*
|
||||
* @author nbaars
|
||||
* @since 8/17/17.
|
||||
*/
|
||||
public class PasswordResetLink {
|
||||
|
||||
public String createPasswordReset(String username, String key) {
|
||||
Random random = new Random();
|
||||
if (username.equalsIgnoreCase("admin")) {
|
||||
//Admin has a fix reset link
|
||||
random.setSeed(key.length());
|
||||
}
|
||||
return scramble(random, scramble(random, scramble(random, MD5.getHashString(username))));
|
||||
}
|
||||
|
||||
public static String scramble(Random random, String inputString) {
|
||||
char a[] = inputString.toCharArray();
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
int j = random.nextInt(a.length);
|
||||
char temp = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = temp;
|
||||
}
|
||||
return new String(a);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args == null || args.length != 2) {
|
||||
System.out.println("Need a username and key");
|
||||
System.exit(1);
|
||||
}
|
||||
String username = args[0];
|
||||
String key = args[1];
|
||||
System.out.println("Generation password reset link for " + username);
|
||||
System.out.println("Created password reset link: " + new PasswordResetLink().createPasswordReset(username, key));
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package org.owasp.webgoat.plugin.challenge8;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||
import org.owasp.webgoat.plugin.Flag;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 4/8/17.
|
||||
*/
|
||||
@AssignmentPath("/challenge/8")
|
||||
@Slf4j
|
||||
public class Assignment8 extends AssignmentEndpoint {
|
||||
|
||||
private static final Map<Integer, Integer> votes = Maps.newHashMap();
|
||||
|
||||
static {
|
||||
votes.put(1, 400);
|
||||
votes.put(2, 120);
|
||||
votes.put(3, 140);
|
||||
votes.put(4, 150);
|
||||
votes.put(5, 300);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/vote/{stars}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@ResponseBody
|
||||
public ResponseEntity<?> vote(@PathVariable(value = "stars") int nrOfStars, HttpServletRequest request) {
|
||||
//Simple implementation of VERB Based Authentication
|
||||
String msg = "";
|
||||
if (request.getMethod().equals("GET")) {
|
||||
HashMap<String, Object> json = Maps.newHashMap();
|
||||
json.put("error", true);
|
||||
json.put("message", "Sorry but you need to login first in order to vote");
|
||||
return ResponseEntity.status(200).body(json);
|
||||
}
|
||||
Integer allVotesForStar = votes.getOrDefault(nrOfStars, 0);
|
||||
votes.put(nrOfStars, allVotesForStar + 1);
|
||||
return ResponseEntity.ok().header("X-Flag", "Thanks for voting, your flag is: " + Flag.FLAGS.get(8)).build();
|
||||
}
|
||||
|
||||
@GetMapping("/votes/")
|
||||
public ResponseEntity<?> getVotes() {
|
||||
return ResponseEntity.ok(votes.entrySet().stream().collect(Collectors.toMap(e -> "" + e.getKey(), e -> e.getValue())));
|
||||
}
|
||||
|
||||
@GetMapping("/votes/average")
|
||||
public ResponseEntity<Map<String, Integer>> average() {
|
||||
int totalNumberOfVotes = votes.values().stream().mapToInt(i -> i.intValue()).sum();
|
||||
int categories = votes.entrySet().stream().mapToInt(e -> e.getKey() * e.getValue()).reduce(0, (a, b) -> a + b);
|
||||
Map json = Maps.newHashMap();
|
||||
json.put("average", (int) Math.ceil((double) categories / totalNumberOfVotes));
|
||||
return ResponseEntity.ok(json);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
package org.owasp.webgoat.plugin.challenge8;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.owasp.webgoat.lessons.Category;
|
||||
import org.owasp.webgoat.lessons.NewLesson;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 3/21/17.
|
||||
*/
|
||||
public class Challenge8 extends NewLesson {
|
||||
|
||||
@Override
|
||||
public Category getDefaultCategory() {
|
||||
return Category.CHALLENGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getHints() {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getDefaultRanking() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "challenge8.title";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "Challenge8";
|
||||
}
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
package org.owasp.webgoat.plugin.challenge9;
|
||||
|
||||
import com.beust.jcommander.internal.Lists;
|
||||
import com.beust.jcommander.internal.Maps;
|
||||
import com.google.common.collect.EvictingQueue;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentPath;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.owasp.webgoat.mail.IncomingMailEvent;
|
||||
import org.owasp.webgoat.users.UserRepository;
|
||||
import org.owasp.webgoat.users.WebGoatUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.jms.core.JmsTemplate;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.owasp.webgoat.plugin.Flag.FLAGS;
|
||||
import static org.owasp.webgoat.plugin.SolutionConstants.PASSWORD_TOM_9;
|
||||
import static org.owasp.webgoat.plugin.SolutionConstants.TOM_EMAIL;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 4/8/17.
|
||||
*/
|
||||
@AssignmentPath("/challenge/9")
|
||||
@Slf4j
|
||||
public class Assignment9 extends AssignmentEndpoint {
|
||||
|
||||
private static Map<String, String> userToTomResetLink = Maps.newHashMap();
|
||||
private static Map<String, String> usersToTomPassword = Maps.newHashMap();
|
||||
private static EvictingQueue resetLinks = EvictingQueue.create(1000);
|
||||
|
||||
private static final String TEMPLATE = "Hi, you requested a password reset link, please use this " +
|
||||
"<a target='_blank' href='http://%s/WebGoat/challenge/9/reset-password/%s'>link</a> to reset your password." +
|
||||
"\n \n\n" +
|
||||
"If you did not request this password change you can ignore this message." +
|
||||
"\n" +
|
||||
"If you have any comments or questions, please do not hesitate to reach us at support@webgoat-cloud.org" +
|
||||
"\n\n" +
|
||||
"Kind regards, \nTeam WebGoat";
|
||||
|
||||
@Autowired
|
||||
private JmsTemplate jmsTemplate;
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@RequestMapping(method = POST, value = "/create-password-reset-link")
|
||||
@ResponseBody
|
||||
public AttackResult sendPasswordResetLink(@RequestParam String email, HttpServletRequest request, @CookieValue("JSESSIONID") String cookie) {
|
||||
String resetLink = UUID.randomUUID().toString();
|
||||
resetLinks.add(resetLink);
|
||||
String host = request.getHeader("host");
|
||||
if (StringUtils.hasText(email)) {
|
||||
if (email.equals(TOM_EMAIL) && host.contains("8081")) { //User indeed changed the host header.
|
||||
userToTomResetLink.put(getWebSession().getUserName(), resetLink);
|
||||
fakeClickingLinkEmail(cookie, host, resetLink);
|
||||
} else {
|
||||
sendMailToUser(email, host, resetLink);
|
||||
}
|
||||
}
|
||||
return success().feedback("email.send").feedbackArgs(email).build();
|
||||
}
|
||||
|
||||
private void sendMailToUser(@RequestParam String email, String host, String resetLink) {
|
||||
String username;
|
||||
WebGoatUser webGoatUser = userRepository.findByUsername(email.substring(0, email.indexOf("@")));
|
||||
if (webGoatUser != null) {
|
||||
username = webGoatUser.getUsername();
|
||||
IncomingMailEvent mail = IncomingMailEvent.builder()
|
||||
.title("Your password reset link for challenge 9")
|
||||
.contents(String.format(TEMPLATE, host, resetLink))
|
||||
.sender("password-reset@webgoat-cloud.net")
|
||||
.recipient(username)
|
||||
.time(LocalDateTime.now()).build();
|
||||
jmsTemplate.convertAndSend("mailbox", mail);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to add the current cookie of the user otherwise we cannot distinguish in WebWolf for
|
||||
* which user we need to trace the incoming request. In normal situation this HOST will be in your
|
||||
* full control so every incoming request would be valid.
|
||||
*/
|
||||
private void fakeClickingLinkEmail(String cookie, String host, String resetLink) {
|
||||
try {
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.put(HttpHeaders.COOKIE, Lists.newArrayList("JSESSIONID=" + cookie));
|
||||
HttpEntity httpEntity = new HttpEntity(httpHeaders);
|
||||
new RestTemplate().exchange(String.format("http://%s/challenge/9/reset-password/%s", host, resetLink), HttpMethod.GET, httpEntity, Void.class);
|
||||
} catch (Exception e) {
|
||||
//don't care
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
@ResponseBody
|
||||
public AttackResult login(@RequestParam String password, @RequestParam String email) {
|
||||
if (TOM_EMAIL.equals(email)) {
|
||||
String passwordTom = usersToTomPassword.getOrDefault(getWebSession().getUserName(), PASSWORD_TOM_9);
|
||||
if (passwordTom.equals(PASSWORD_TOM_9)) {
|
||||
return failed().feedback("login_failed").build();
|
||||
} else if (passwordTom.equals(password)) {
|
||||
return success().feedback("challenge.solved").feedbackArgs(FLAGS.get(9)).build();
|
||||
}
|
||||
}
|
||||
return failed().feedback("login_failed.tom").build();
|
||||
}
|
||||
|
||||
@GetMapping("/reset-password/{link}")
|
||||
public String resetPassword(@PathVariable(value = "link") String link, Model model) {
|
||||
if (this.resetLinks.contains(link)) {
|
||||
PasswordChangeForm form = new PasswordChangeForm();
|
||||
form.setResetLink(link);
|
||||
model.addAttribute("form", form);
|
||||
return "password_reset"; //Display html page for changing password
|
||||
} else {
|
||||
return "password_link_not_found";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/change-password")
|
||||
public String changePassword(@ModelAttribute("form") PasswordChangeForm form, BindingResult bindingResult) {
|
||||
if (!StringUtils.hasText(form.getPassword())) {
|
||||
bindingResult.rejectValue("password", "not.empty");
|
||||
}
|
||||
if (bindingResult.hasErrors()) {
|
||||
return "password_reset";
|
||||
}
|
||||
if (!resetLinks.contains(form.getResetLink())) {
|
||||
return "password_link_not_found";
|
||||
}
|
||||
if (checkIfLinkIsFromTom(form.getResetLink())) {
|
||||
usersToTomPassword.put(getWebSession().getUserName(), form.getPassword());
|
||||
}
|
||||
return "success";
|
||||
}
|
||||
|
||||
private boolean checkIfLinkIsFromTom(String resetLinkFromForm) {
|
||||
String resetLink = userToTomResetLink.getOrDefault(getWebSession().getUserName(), "unknown");
|
||||
return resetLink.equals(resetLinkFromForm);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
package org.owasp.webgoat.plugin.challenge9;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.owasp.webgoat.lessons.Category;
|
||||
import org.owasp.webgoat.lessons.NewLesson;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 3/21/17.
|
||||
*/
|
||||
public class Challenge9 extends NewLesson {
|
||||
|
||||
@Override
|
||||
public Category getDefaultCategory() {
|
||||
return Category.CHALLENGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getHints() {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getDefaultRanking() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return "challenge9.title";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "Challenge9";
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package org.owasp.webgoat.plugin.challenge9;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 8/18/17.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class PasswordChangeForm {
|
||||
|
||||
@NotNull
|
||||
@Size(min=6, max=10)
|
||||
private String password;
|
||||
private String resetLink;
|
||||
|
||||
}
|
BIN
webgoat-lessons/challenge/src/main/resources/challenge7/git.zip
Normal file
BIN
webgoat-lessons/challenge/src/main/resources/challenge7/git.zip
Normal file
Binary file not shown.
@ -0,0 +1,43 @@
|
||||
.btn-grey{
|
||||
background-color:#D8D8D8;
|
||||
color:#FFF;
|
||||
}
|
||||
.rating-block{
|
||||
background-color:#FAFAFA;
|
||||
border:1px solid #EFEFEF;
|
||||
padding:15px 15px 20px 15px;
|
||||
border-radius:3px;
|
||||
}
|
||||
.bold{
|
||||
font-weight:700;
|
||||
}
|
||||
.padding-bottom-7{
|
||||
padding-bottom:7px;
|
||||
}
|
||||
|
||||
.review-block{
|
||||
background-color:#FAFAFA;
|
||||
border:1px solid #EFEFEF;
|
||||
padding:15px;
|
||||
border-radius:3px;
|
||||
margin-bottom:15px;
|
||||
}
|
||||
.review-block-name{
|
||||
font-size:12px;
|
||||
margin:10px 0;
|
||||
}
|
||||
.review-block-date{
|
||||
font-size:12px;
|
||||
}
|
||||
.review-block-rate{
|
||||
font-size:13px;
|
||||
margin-bottom:15px;
|
||||
}
|
||||
.review-block-title{
|
||||
font-size:15px;
|
||||
font-weight:700;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
.review-block-description{
|
||||
font-size:13px;
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<!--
|
||||
** Revision history (automatically added by: /challenge/7/.git/hooks)
|
||||
|
||||
2e29cacb85ce5066b8d011bb9769b666812b2fd9 Updated copyright to 2017
|
||||
ac937c7aab89e042ca32efeb00d4ca08a95b50d6 Removed hardcoded key
|
||||
f94008f801fceb8833a30fe56a8b26976347edcf First version of WebGoat Cloud website
|
||||
|
||||
-->
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:Challenge_7.adoc"></div>
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div class="text-center">
|
||||
<h3><i class="fa fa-lock fa-4x"></i></h3>
|
||||
<h2 class="text-center">Forgot Password?</h2>
|
||||
<p>You can reset your password here.</p>
|
||||
<div class="panel-body">
|
||||
|
||||
<form id="login-form" class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/challenge/7"
|
||||
enctype="application/json;charset=UTF-8" role="form">
|
||||
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i
|
||||
class="glyphicon glyphicon-envelope color-blue"></i></span>
|
||||
<input id="email" name="email" placeholder="email address"
|
||||
class="form-control" type="email"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input name="recover-submit" class="btn btn-lg btn-primary btn-block"
|
||||
value="Reset Password" type="submit"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<p>(c) 2017 WebGoat Cloud Platform</p>
|
||||
</div>
|
||||
|
||||
<input type="hidden" class="hide" name="token" id="token" value=""/>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<form class="attack-form" method="POST" name="form" action="/WebGoat/challenge/flag">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon"><i class="fa fa-flag-checkered" aria-hidden="true"
|
||||
style="font-size:20px"></i></div>
|
||||
<input type="text" class="form-control" id="flag" name="flag"
|
||||
placeholder="a7179f89-906b-4fec-9d99-f15b796e7208"/>
|
||||
</div>
|
||||
<div class="input-group" style="margin-top: 10px">
|
||||
<button type="submit" class="btn btn-primary">Submit flag</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<br/>
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</html>
|
@ -0,0 +1,255 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:Challenge_8.adoc"></div>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/challenge8.css}"/>
|
||||
<script th:src="@{/lesson_js/challenge8.js}" language="JavaScript"></script>
|
||||
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
<div class="rating-block">
|
||||
<h4>Average user rating</h4>
|
||||
<h2 class="bold padding-bottom-7">4.3
|
||||
<small>/ 5</small>
|
||||
</h2>
|
||||
<button id="star1" onClick="doVote(1)" type="button" class="btn btn-warning btn-sm" aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button id="star2" onClick="doVote(2)" type="button" class="btn btn-warning btn-sm" aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button id="star3" onClick="doVote(3)" type="button" class="btn btn-warning btn-sm" aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button id="star4" onClick="doVote(4)" type="button" class="btn btn-default btn-grey btn-sm" aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button id="star5" onClick="doVote(5)" type="button" class="btn btn-default btn-grey btn-sm" aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<h4>Rating breakdown</h4>
|
||||
<div class="pull-left">
|
||||
<div class="pull-left" style="width:35px; line-height:1;">
|
||||
<div style="height:9px; margin:5px 0;">5 <span class="glyphicon glyphicon-star"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-left" style="width:180px;">
|
||||
<div class="progress" style="height:9px; margin:8px 0;">
|
||||
<div id="progressBar5" class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="5"
|
||||
aria-valuemin="0" aria-valuemax="5">
|
||||
<span class="sr-only">5</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="nrOfVotes5" class="pull-right" style="margin-left:10px;">0</div>
|
||||
</div>
|
||||
<div class="pull-left">
|
||||
<div class="pull-left" style="width:35px; line-height:1;">
|
||||
<div style="height:9px; margin:5px 0;">4 <span class="glyphicon glyphicon-star"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-left" style="width:180px;">
|
||||
<div class="progress" style="height:9px; margin:8px 0;">
|
||||
<div id="progressBar4" class="progress-bar progress-bar-primary" role="progressbar" aria-valuenow="5"
|
||||
aria-valuemin="0" aria-valuemax="5">
|
||||
<span class="sr-only">4</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="nrOfVotes4" class="pull-right" style="margin-left:10px;">0</div>
|
||||
</div>
|
||||
<div class="pull-left">
|
||||
<div class="pull-left" style="width:35px; line-height:1;">
|
||||
<div style="height:9px; margin:5px 0;">3 <span class="glyphicon glyphicon-star"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-left" style="width:180px;">
|
||||
<div class="progress" style="height:9px; margin:8px 0;">
|
||||
<div id="progressBar3" class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="5"
|
||||
aria-valuemin="0" aria-valuemax="5">
|
||||
<span class="sr-only">4</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="nrOfVotes3" class="pull-right" style="margin-left:10px;">0</div>
|
||||
</div>
|
||||
<div class="pull-left">
|
||||
<div class="pull-left" style="width:35px; line-height:1;">
|
||||
<div style="height:9px; margin:5px 0;">2 <span class="glyphicon glyphicon-star"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-left" style="width:180px;">
|
||||
<div class="progress" style="height:9px; margin:8px 0;">
|
||||
<div id="progressBar2" class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="5"
|
||||
aria-valuemin="0" aria-valuemax="5">
|
||||
<span class="sr-only">2</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="nrOfVotes2" class="pull-right" style="margin-left:10px;">0</div>
|
||||
</div>
|
||||
<div class="pull-left">
|
||||
<div class="pull-left" style="width:35px; line-height:1;">
|
||||
<div style="height:9px; margin:5px 0;">1 <span class="glyphicon glyphicon-star"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pull-left" style="width:180px;">
|
||||
<div class="progress" style="height:9px; margin:8px 0;">
|
||||
<div id="progressBar1" class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="5"
|
||||
aria-valuemin="0" aria-valuemax="5">
|
||||
<span class="sr-only">4</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="nrOfVotes1" class="pull-right" style="margin-left:10px;">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-7">
|
||||
<hr/>
|
||||
<div id = "voteResultMsg" class="alert alert-dismissable" style="display: none;">
|
||||
</div>
|
||||
<div class="alert alert-info">
|
||||
Please login or register in order to vote (comments are disabled)
|
||||
</div>
|
||||
<div class="review-block">
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
<img src="images/user1.png" class="img-rounded"/>
|
||||
<div class="review-block-name"><a href="#">nktailor</a></div>
|
||||
<div class="review-block-date">August 22, 2017<br/>1 day ago</div>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<div class="review-block-rate">
|
||||
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning btn-xs"
|
||||
aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning btn-xs"
|
||||
aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="review-block-title">WebGoat rocks!</div>
|
||||
<div class="review-block-description">This is a great tool to learn about security
|
||||
and have some fun with a couple challenges.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
<img src="images/user3.png" class="img-rounded"/>
|
||||
<div class="review-block-name"><a href="#">Sarah</a></div>
|
||||
<div class="review-block-date">July 29, 2017<br/>12 day ago</div>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<div class="review-block-rate">
|
||||
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning btn-xs"
|
||||
aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-grey btn-xs"
|
||||
aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="review-block-title">Nice</div>
|
||||
<div class="review-block-description">I liked it and learned a couple of things.
|
||||
Still some bugs sometimes though.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
<img src="images/user2.png" class="img-rounded"/>
|
||||
<div class="review-block-name"><a href="#">Tom</a></div>
|
||||
<div class="review-block-date">January 27, 2017<br/>100 days ago</div>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<div class="review-block-rate">
|
||||
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning btn-xs" aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-grey btn-xs"
|
||||
aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-grey btn-xs"
|
||||
aria-label="Left Align">
|
||||
<span class="glyphicon glyphicon-star" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="review-block-title">WebGoat is great</div>
|
||||
<div class="review-block-description">WebGoat teaches you web security with some great
|
||||
lessons
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<form class="attack-form" method="POST" name="form" action="/WebGoat/challenge/flag">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon"><i class="fa fa-flag-checkered" aria-hidden="true"
|
||||
style="font-size:20px"></i></div>
|
||||
<input type="text" class="form-control" id="flag" name="flag"
|
||||
placeholder="a7179f89-906b-4fec-9d99-f15b796e7208"/>
|
||||
</div>
|
||||
<div class="input-group" style="margin-top: 10px">
|
||||
<button type="submit" class="btn btn-primary">Submit flag</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<br/>
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</html>
|
@ -0,0 +1,109 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:Challenge_9.adoc"></div>
|
||||
<script th:src="@{/lesson_js/challenge9.js}" language="JavaScript"></script>
|
||||
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h4 style="border-bottom: 1px solid #c5c5c5;">
|
||||
<i class="glyphicon glyphicon-user"></i>
|
||||
Account Access
|
||||
</h4>
|
||||
<div style="padding: 20px;" id="form-login">
|
||||
<form id="login-form" class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/challenge/9/login"
|
||||
enctype="application/json;charset=UTF-8" role="form">
|
||||
<fieldset>
|
||||
<div class="form-group input-group">
|
||||
<span class="input-group-addon"> @ </span>
|
||||
<input class="form-control" placeholder="Email" name="email" type="email"
|
||||
required="" autofocus=""/>
|
||||
</div>
|
||||
<div class="form-group input-group">
|
||||
<span class="input-group-addon">
|
||||
<i class="glyphicon glyphicon-lock">
|
||||
</i>
|
||||
</span>
|
||||
<input class="form-control" placeholder="Password" name="password" type="password"
|
||||
value="" required=""/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
Access
|
||||
</button>
|
||||
<p class="help-block">
|
||||
<a class="pull-right text-muted" href="#" id="login">
|
||||
<small>Forgot your password?</small>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<div style="display: none;" id="form-login">
|
||||
<h4 class="">
|
||||
Forgot your password?
|
||||
</h4>
|
||||
<form id="login-form" class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/challenge/9/create-password-reset-link"
|
||||
enctype="application/json;charset=UTF-8" role="form">
|
||||
<fieldset>
|
||||
<span class="help-block">
|
||||
Email address you use to log in to your account
|
||||
<br/>
|
||||
We'll send you an email with instructions to choose a new password.
|
||||
</span>
|
||||
<div class="form-group input-group">
|
||||
<span class="input-group-addon">
|
||||
@
|
||||
</span>
|
||||
<input class="form-control" placeholder="Email" name="email" type="email"
|
||||
required=""/>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block" id="btn-login">
|
||||
Continue
|
||||
</button>
|
||||
<p class="help-block">
|
||||
<a class="text-muted" href="#" id="forgot">
|
||||
<small>Account Access</small>
|
||||
</a>
|
||||
</p>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<form class="attack-form" method="POST" name="form" action="/WebGoat/challenge/flag">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon"><i class="fa fa-flag-checkered" aria-hidden="true"
|
||||
style="font-size:20px"></i></div>
|
||||
<input type="text" class="form-control" id="flag" name="flag"
|
||||
placeholder="a7179f89-906b-4fec-9d99-f15b796e7208"/>
|
||||
</div>
|
||||
<div class="input-group" style="margin-top: 10px">
|
||||
<button type="submit" class="btn btn-primary">Submit flag</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<br/>
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</html>
|
@ -5,8 +5,13 @@ challenge3.title=Photo comments
|
||||
challenge4.title=Voting
|
||||
challenge5.title=Without password
|
||||
challenge6.title=Creating a new account
|
||||
challenge7.title=Admin password reset
|
||||
challenge8.title=Without account
|
||||
challenge9.title=Changing password
|
||||
challenge.solved=Congratulations, you solved the challenge. Here is your flag: {0}
|
||||
challenge.close=This is not the correct password for tom, please try again.
|
||||
challenge.close=This is not the correct password for Larry, please try again.
|
||||
|
||||
email.send=An e-mail has been send to {0}
|
||||
|
||||
user.exists=User {0} already exists please try to register with a different username.
|
||||
user.created=User {0} created, please proceed to the login page.
|
||||
@ -15,4 +20,10 @@ input.invalid=Input for user, email and/or password is empty or too long, please
|
||||
challenge.flag.correct=Congratulations you have solved the challenge!!
|
||||
challenge.flag.incorrect=Sorry this is not the correct flag, please try again.
|
||||
|
||||
ip.address.unknown=IP address unknown, e-mail has been sent.
|
||||
ip.address.unknown=IP address unknown, e-mail has been sent.
|
||||
|
||||
login_failed=Login failed
|
||||
login_failed.tom=Sorry only Tom can login at the moment
|
||||
|
||||
required4=Missing username or password, please specify both.
|
||||
user.not.larry=Please try to log in as Larry not {0}.
|
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
BIN
webgoat-lessons/challenge/src/main/resources/images/user1.png
Normal file
BIN
webgoat-lessons/challenge/src/main/resources/images/user1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
webgoat-lessons/challenge/src/main/resources/images/user2.png
Normal file
BIN
webgoat-lessons/challenge/src/main/resources/images/user2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
webgoat-lessons/challenge/src/main/resources/images/user3.png
Normal file
BIN
webgoat-lessons/challenge/src/main/resources/images/user3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
@ -0,0 +1,57 @@
|
||||
$(document).ready(function () {
|
||||
loadVotes();
|
||||
average();
|
||||
})
|
||||
|
||||
function loadVotes() {
|
||||
$.get("challenge/8/votes/", function (votes) {
|
||||
var totalVotes = 0;
|
||||
for (var i = 1; i <= 5; i++) {
|
||||
totalVotes = totalVotes + votes[i];
|
||||
}
|
||||
console.log(totalVotes);
|
||||
for (var i = 1; i <= 5; i++) {
|
||||
var percent = votes[i] * 100 / totalVotes;
|
||||
console.log(percent);
|
||||
var progressBar = $('#progressBar' + i);
|
||||
progressBar.width(Math.round(percent) * 2 + '%');
|
||||
$("#nrOfVotes" + i).html(votes[i]);
|
||||
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function average() {
|
||||
$.get("challenge/8/votes/average", function (average) {
|
||||
for (var i = 1; i <= 5; i++) {
|
||||
var number = average["average"];
|
||||
$("#star" + i).removeClass('btn-warning');
|
||||
$("#star" + i).removeClass('btn-default');
|
||||
$("#star" + i).removeClass('btn-grey');
|
||||
|
||||
if (i <= number) {
|
||||
$("#star" + i).addClass('btn-warning');
|
||||
} else {
|
||||
$("#star" + i).addClass('btn-grey');
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function doVote(stars) {
|
||||
$("#voteResultMsg").hide();
|
||||
$.get("challenge/8/vote/" + stars, function (result) {
|
||||
if (result["error"]) {
|
||||
$("#voteResultMsg").addClass('alert-danger alert-dismissable');
|
||||
} else {
|
||||
$("#voteResultMsg").addClass('alert-success alert-dismissable');
|
||||
}
|
||||
$("#voteResultMsg").html(result["message"]);
|
||||
$("#voteResultMsg").show();
|
||||
})
|
||||
loadVotes();
|
||||
average();
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
$(document).ready(function() {
|
||||
$('#login').click(function(e) {
|
||||
e.preventDefault();
|
||||
$('div#form-login').toggle('500');
|
||||
});
|
||||
$('#forgot').click(function(e) {
|
||||
e.preventDefault();
|
||||
$('div#form-login').toggle('500');
|
||||
});
|
||||
});
|
@ -0,0 +1 @@
|
||||
Try to reset the password for admin.
|
@ -0,0 +1 @@
|
||||
Can you still vote?
|
@ -0,0 +1,3 @@
|
||||
Tom always resets his password immediately after receiving the email with the link.
|
||||
Try to reset the password of Tom (tom@webgoat-cloud.org) to your own choice and login as Tom with
|
||||
that password.
|
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/plugins/bootstrap/css/bootstrap.min.css}"/>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/font-awesome.min.css}"/>
|
||||
<script th:src="@{/plugins/bootstrap/js/bootstrap.min.js}"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="alert alert-danger">
|
||||
<h4>Password reset link is not valid please try again.</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/plugins/bootstrap/css/bootstrap.min.css}"/>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/font-awesome.min.css}"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-3">
|
||||
<form role="form" method="POST" action="/WebGoat/challenge/9/change-password" th:object="${form}">
|
||||
<h2 class="sign_up_title">Reset your password</h2>
|
||||
<!--<div class="form-group" th:classappend="${#fields.hasErrors('email')}? 'has-error'">-->
|
||||
<!--<div class="form-group">-->
|
||||
<!--<label for="email" class="control-label">Email</label>-->
|
||||
<!--<input autofocus="dummy_for_thymeleaf_parser" type="text" class="form-control"-->
|
||||
<!--th:field="*{email}"-->
|
||||
<!--id="email" placeholder="email" name='email'/>-->
|
||||
<!--<span th:if="${#fields.hasErrors('email')}" th:errors="*{email}">Email error</span>-->
|
||||
<!--</div>-->
|
||||
<div class="form-group" th:classappend="${#fields.hasErrors('password')}? 'has-error'">
|
||||
<input type="hidden" name="resetLink" th:field="*{resetLink}" />
|
||||
<label for="password" class="control-label" th:text="#{password}">Password</label>
|
||||
<input type="password" class="form-control" id="password" placeholder="Password"
|
||||
name='password' th:value="*{password}"/>
|
||||
<span th:if="${#fields.hasErrors('password')}" th:errors="*{password}">Password error</span>
|
||||
</div>
|
||||
|
||||
<!---->
|
||||
<!--<div class="form-group">-->
|
||||
<!--<input type="email" required="" autofocus="" name="email" id="email" class="form-control input-lg" placeholder="Email"-->
|
||||
<!--tabindex="4"/>-->
|
||||
<!--<input type="newPassword" required="" autofocus="" name="newPassword" id="newPassword" class="form-control input-lg" placeholder="New password"-->
|
||||
<!--tabindex="4"/>-->
|
||||
<!--</div>-->
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-12">
|
||||
<button type="submit" class="btn btn-success btn-block btn-lg">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/plugins/bootstrap/css/bootstrap.min.css}"/>
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/css/font-awesome.min.css}"/>
|
||||
<script th:src="@{/plugins/bootstrap/js/bootstrap.min.js}"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="alert alert-success">
|
||||
<h4>Password changed successfully, please login again with your new password</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
Reference in New Issue
Block a user