Crypto lesson (#712)

* crypto lesson added

* signing assignment

* integration test added for signing assignment

* added more hints

* corrections after rebase

* added some explanation

* added security defaults assignment
This commit is contained in:
René Zubcevic 2019-11-23 21:52:14 +01:00 committed by GitHub
parent 9c0b7f8233
commit b5e5dd1d13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1151 additions and 1 deletions

View File

@ -52,6 +52,7 @@
<li>Jason White (Developer)</li> <li>Jason White (Developer)</li>
<li>Doug Morato (Developer &amp; CI)</li> <li>Doug Morato (Developer &amp; CI)</li>
<li>Bruce Mayhew (Developer)</li> <li>Bruce Mayhew (Developer)</li>
<li>Ren&eacute; Zubcevic (Developer)</li>
</ul> </ul>
</p> </p>
</div> </div>

View File

@ -0,0 +1,134 @@
package org.owasp.webgoat;
import static org.junit.Assert.fail;
import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.DatatypeConverter;
import org.junit.Test;
import org.owasp.webgoat.crypto.CryptoUtil;
import org.owasp.webgoat.crypto.HashingAssignment;
import io.restassured.RestAssured;
public class CryptoTest extends IntegrationTest {
@Test
public void runTests() {
startLesson("Crypto");
checkAssignment2();
checkAssignment3();
// Assignment 4
try {
checkAssignment4();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
fail();
}
try {
checkAssignmentSigning();
} catch (Exception e) {
e.printStackTrace();
fail();
}
checkAssignmentDefaults();
checkResults("/crypto");
}
private void checkAssignment2() {
String basicEncoding = RestAssured.given().when().relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()).get(url("/crypto/encoding/basic")).then().extract()
.asString();
basicEncoding = basicEncoding.substring("Authorization: Basic ".length());
String decodedString = new String(Base64.getDecoder().decode(basicEncoding.getBytes()));
String answer_user = decodedString.split(":")[0];
String answer_pwd = decodedString.split(":")[1];
Map<String, Object> params = new HashMap<>();
params.clear();
params.put("answer_user", answer_user);
params.put("answer_pwd", answer_pwd);
checkAssignment(url("/crypto/encoding/basic-auth"), params, true);
}
private void checkAssignment3() {
String answer_1 = "databasepassword";
Map<String, Object> params = new HashMap<>();
params.clear();
params.put("answer_pwd1", answer_1);
checkAssignment(url("/crypto/encoding/xor"), params, true);
}
private void checkAssignment4() throws NoSuchAlgorithmException {
String md5Hash = RestAssured.given().when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie())
.get(url("/crypto/hashing/md5")).then().extract().asString();
String sha256Hash = RestAssured.given().when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie())
.get(url("/crypto/hashing/sha256")).then().extract().asString();
String answer_1 = "unknown";
String answer_2 = "unknown";
for (String secret : HashingAssignment.SECRETS) {
if (md5Hash.equals(HashingAssignment.getHash(secret, "MD5"))) {
answer_1 = secret;
}
if (sha256Hash.equals(HashingAssignment.getHash(secret, "SHA-256"))) {
answer_2 = secret;
}
}
Map<String, Object> params = new HashMap<>();
params.clear();
params.put("answer_pwd1", answer_1);
params.put("answer_pwd2", answer_2);
checkAssignment(url("/WebGoat/crypto/hashing"), params, true);
}
private void checkAssignmentSigning() throws NoSuchAlgorithmException, InvalidKeySpecException {
String privatePEM = RestAssured.given()
.when()
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.get(url("/crypto/signing/getprivate"))
.then()
.extract().asString();
PrivateKey privateKey = CryptoUtil.getPrivateKeyFromPEM(privatePEM);
RSAPrivateKey privk = (RSAPrivateKey) privateKey;
String modulus = DatatypeConverter.printHexBinary(privk.getModulus().toByteArray());
String signature = CryptoUtil.signMessage(modulus, privateKey);
Map<String, Object> params = new HashMap<>();
params.clear();
params.put("modulus", modulus);
params.put("signature", signature);
checkAssignment(url("/crypto/signing/verify"), params, true);
}
private void checkAssignmentDefaults() {
String text = new String(Base64.getDecoder().decode("TGVhdmluZyBwYXNzd29yZHMgaW4gZG9ja2VyIGltYWdlcyBpcyBub3Qgc28gc2VjdXJl".getBytes(Charset.forName("UTF-8"))));
Map<String, Object> params = new HashMap<>();
params.clear();
params.put("secretText", text);
params.put("secretFileName", "default_secret");
checkAssignment(url("/crypto/secure/defaults"), params, true);
}
}

View File

@ -0,0 +1,12 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>crypto</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>webgoat-lessons-parent</artifactId>
<version>v8.0.0-SNAPSHOT</version>
</parent>
</project>

View File

@ -0,0 +1,41 @@
/*
* 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.crypto;
import org.owasp.webgoat.lessons.Category;
import org.owasp.webgoat.lessons.Lesson;
import org.springframework.stereotype.Component;
@Component
public class Crypto extends Lesson {
@Override
public Category getDefaultCategory() {
return Category.GENERAL;
}
@Override
public String getTitle() {
return "6.crypto.title";//first lesson in general
}
}

View File

@ -0,0 +1,134 @@
package org.owasp.webgoat.crypto;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAKeyGenParameterSpec;
import java.util.Base64;
import java.util.Random;
import javax.xml.bind.DatatypeConverter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class CryptoUtil {
private static final BigInteger[] FERMAT_PRIMES =
{ BigInteger.valueOf(3),
BigInteger.valueOf(5),
BigInteger.valueOf(17),
BigInteger.valueOf(257),
BigInteger.valueOf(65537) };
public static KeyPair generateKeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
RSAKeyGenParameterSpec kpgSpec = new RSAKeyGenParameterSpec(2048, FERMAT_PRIMES[new SecureRandom().nextInt(FERMAT_PRIMES.length)]);
keyPairGenerator.initialize(kpgSpec);
//keyPairGenerator.initialize(2048);
return keyPairGenerator.generateKeyPair();
}
public static String getPrivateKeyInPEM(KeyPair keyPair) {
String encodedString = "-----BEGIN PRIVATE KEY-----\n";
encodedString = encodedString+new String(Base64.getEncoder().encode(keyPair.getPrivate().getEncoded()),Charset.forName("UTF-8"))+"\n";
encodedString = encodedString+"-----END PRIVATE KEY-----\n";
return encodedString;
}
public static String signMessage(String message, PrivateKey privateKey) {
log.debug("start signMessage");
String signature = null;
try {
//Initiate signature verification
Signature instance = Signature.getInstance("SHA256withRSA");
instance.initSign(privateKey);
instance.update(message.getBytes("UTF-8"));
//actual verification against signature
signature = new String(Base64.getEncoder().encode(instance.sign()), Charset.forName("UTF-8"));
log.info("signe the signature with result: {}", signature);
} catch (Exception e) {
log.error("Signature signing failed", e);
}
log.debug("end signMessage");
return signature;
}
public static boolean verifyMessage(String message, String base64EncSignature,
PublicKey publicKey) {
log.debug("start verifyMessage");
//get raw signature from base64 encrypted string in header
byte[] decodedSignature = Base64.getDecoder().decode(base64EncSignature);
boolean result = false;
try {
//Initiate signature verification
Signature instance = Signature.getInstance("SHA256withRSA");
instance.initVerify(publicKey);
instance.update(message.getBytes("UTF-8"));
//actual verification against signature
result = instance.verify(decodedSignature);
log.info("Verified the signature with result: {}", result);
} catch (Exception e) {
log.error("Signature verification failed", e);
}
log.debug("end verifyMessage");
return result;
}
public static boolean verifyAssignment(String modulus, String signature, PublicKey publicKey) {
/* first check if the signature is correct, i.e. right private key and right hash */
boolean result = false;
if (modulus != null && signature != null) {
result = verifyMessage(modulus, signature, publicKey);
/*
* next check if the submitted modulus is the correct modulus of the public key
*/
RSAPublicKey rsaPubKey = (RSAPublicKey) publicKey;
if (modulus.length()==512) {
modulus = "00".concat(modulus);
}
result = result && (DatatypeConverter.printHexBinary(rsaPubKey.getModulus().toByteArray()).equals(modulus.toUpperCase()));
}
return result;
}
public static PrivateKey getPrivateKeyFromPEM(String privateKeyPem) throws NoSuchAlgorithmException, InvalidKeySpecException {
privateKeyPem = privateKeyPem.replace("-----BEGIN PRIVATE KEY-----", "");
privateKeyPem = privateKeyPem.replace("-----END PRIVATE KEY-----", "");
privateKeyPem = privateKeyPem.replace("\n", "").replace("\r", "");
byte [] decoded = Base64.getDecoder().decode(privateKeyPem);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.crypto;
import java.util.Base64;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AttackResult;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
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;
@RestController
public class EncodingAssignment extends AssignmentEndpoint {
public static String getBasicAuth(String username, String password) {
return Base64.getEncoder().encodeToString(username.concat(":").concat(password).getBytes());
}
@GetMapping(path="/crypto/encoding/basic",produces=MediaType.TEXT_HTML_VALUE)
@ResponseBody
public String getBasicAuth(HttpServletRequest request) {
String basicAuth = (String) request.getSession().getAttribute("basicAuth");
String username = request.getUserPrincipal().getName();
if (basicAuth == null) {
String password = HashingAssignment.SECRETS[new Random().nextInt(HashingAssignment.SECRETS.length)];
basicAuth = getBasicAuth(username, password);
request.getSession().setAttribute("basicAuth", basicAuth);
}
return "Authorization: Basic ".concat(basicAuth);
}
@PostMapping("/crypto/encoding/basic-auth")
@ResponseBody
public AttackResult completed(HttpServletRequest request, @RequestParam String answer_user, @RequestParam String answer_pwd) {
String basicAuth = (String) request.getSession().getAttribute("basicAuth");
if (basicAuth !=null && answer_user!=null && answer_pwd !=null
&& basicAuth.equals(getBasicAuth(answer_user,answer_pwd)))
{
return success(this)
.feedback("crypto-encoding.success")
.build();
} else {
return failed(this).feedback("crypto-encoding.empty").build();
}
}
}

View File

@ -0,0 +1,111 @@
/*
* 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.crypto;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AttackResult;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@AssignmentHints({"crypto-hashing.hints.1","crypto-hashing.hints.2"})
public class HashingAssignment extends AssignmentEndpoint {
public static final String[] SECRETS = {"secret","admin","password", "123456", "passw0rd"};
@RequestMapping(path="/crypto/hashing/md5",produces=MediaType.TEXT_HTML_VALUE)
@ResponseBody
public String getMd5(HttpServletRequest request) throws NoSuchAlgorithmException {
String md5Hash = (String) request.getSession().getAttribute("md5Hash");
if (md5Hash == null) {
String secret = SECRETS[new Random().nextInt(SECRETS.length)];
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(secret.getBytes());
byte[] digest = md.digest();
md5Hash = DatatypeConverter
.printHexBinary(digest).toUpperCase();
request.getSession().setAttribute("md5Hash", md5Hash);
request.getSession().setAttribute("md5Secret", secret);
}
return md5Hash;
}
@RequestMapping(path="/crypto/hashing/sha256",produces=MediaType.TEXT_HTML_VALUE)
@ResponseBody
public String getSha256(HttpServletRequest request) throws NoSuchAlgorithmException {
String sha256 = (String) request.getSession().getAttribute("sha256");
if (sha256 == null) {
String secret = SECRETS[new Random().nextInt(SECRETS.length)];
sha256 = getHash(secret, "SHA-256");
request.getSession().setAttribute("sha256Hash", sha256);
request.getSession().setAttribute("sha256Secret", secret);
}
return sha256;
}
@PostMapping("/crypto/hashing")
@ResponseBody
public AttackResult completed(HttpServletRequest request, @RequestParam String answer_pwd1, @RequestParam String answer_pwd2) {
String md5Secret = (String) request.getSession().getAttribute("md5Secret");
String sha256Secret = (String) request.getSession().getAttribute("sha256Secret");
if (answer_pwd1!=null && answer_pwd2 !=null) {
if (answer_pwd1.equals(md5Secret)
&& answer_pwd2.equals(sha256Secret)) {
return success(this)
.feedback("crypto-hashing.success")
.build();
} else if (answer_pwd1.equals(md5Secret)
|| answer_pwd2.equals(sha256Secret)) {
return failed(this).feedback("crypto-hashing.oneok").build();
}
}
return failed(this).feedback("crypto-hashing.empty").build();
}
public static String getHash(String secret, String algorithm) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance(algorithm);
md.update(secret.getBytes());
byte[] digest = md.digest();
return DatatypeConverter
.printHexBinary(digest).toUpperCase();
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.crypto;
import java.security.NoSuchAlgorithmException;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AttackResult;
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;
@RestController
@AssignmentHints({"crypto-secure-defaults.hints.1", "crypto-secure-defaults.hints.2", "crypto-secure-defaults.hints.3"})
public class SecureDefaultsAssignment extends AssignmentEndpoint {
@PostMapping("/crypto/secure/defaults")
@ResponseBody
public AttackResult completed(@RequestParam String secretFileName, @RequestParam String secretText) throws NoSuchAlgorithmException {
if (secretFileName!=null && secretFileName.equals("default_secret")) {
if (secretText!=null && HashingAssignment.getHash(secretText, "SHA-256").equalsIgnoreCase("34de66e5caf2cb69ff2bebdc1f3091ecf6296852446c718e38ebfa60e4aa75d2")) {
return success(this)
.feedback("crypto-secure-defaults.success")
.build();
} else {
return failed(this).feedback("crypto-secure-defaults.messagenotok").build();
}
}
return failed(this).feedback("crypto-secure-defaults.notok").build();
}
}

View 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.crypto;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AttackResult;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.slf4j.Slf4j;
@RestController
@AssignmentHints({"crypto-signing.hints.1","crypto-signing.hints.2", "crypto-signing.hints.3", "crypto-signing.hints.4"})
@Slf4j
public class SigningAssignment extends AssignmentEndpoint {
@RequestMapping(path="/crypto/signing/getprivate",produces=MediaType.TEXT_HTML_VALUE)
@ResponseBody
public String getPrivateKey(HttpServletRequest request) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
String privateKey = (String) request.getSession().getAttribute("privateKeyString");
if (privateKey == null) {
KeyPair keyPair = CryptoUtil.generateKeyPair();
privateKey = CryptoUtil.getPrivateKeyInPEM(keyPair);
request.getSession().setAttribute("privateKeyString", privateKey);
request.getSession().setAttribute("keyPair", keyPair);
}
return privateKey;
}
@PostMapping("/crypto/signing/verify")
@ResponseBody
public AttackResult completed(HttpServletRequest request, @RequestParam String modulus, @RequestParam String signature) {
String tempModulus = modulus;/* used to validate the modulus of the public key but might need to be corrected */
KeyPair keyPair = (KeyPair) request.getSession().getAttribute("keyPair");
RSAPublicKey rsaPubKey = (RSAPublicKey) keyPair.getPublic();
if (tempModulus.length() == 512) {
tempModulus = "00".concat(tempModulus);
}
if (!DatatypeConverter.printHexBinary(rsaPubKey.getModulus().toByteArray()).equals(tempModulus.toUpperCase())) {
log.warn("modulus {} incorrect", modulus);
return failed(this).feedback("crypto-signing.modulusnotok").build();
}
/* orginal modulus must be used otherwise the signature would be invalid */
if (CryptoUtil.verifyMessage(modulus, signature, keyPair.getPublic())) {
return success(this).feedback("crypto-signing.success").build();
} else {
log.warn("signature incorrect");
return failed(this).feedback("crypto-signing.notok").build();
}
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.crypto;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AttackResult;
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;
@RestController
@AssignmentHints({"crypto-encoding-xor.hints.1"})
public class XOREncodingAssignment extends AssignmentEndpoint {
@PostMapping("/crypto/encoding/xor")
@ResponseBody
public AttackResult completed(@RequestParam String answer_pwd1) {
if (answer_pwd1!=null && answer_pwd1.equals("databasepassword")) {
return success(this)
.feedback("crypto-encoding-xor.success")
.build();
}
return failed(this).feedback("crypto-encoding-xor.empty").build();
}
}

View File

@ -0,0 +1,129 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<header>
<script>
/**
* JavaScript to load initial assignment tokens
*/
function initialise() {
$("#sha256token").load('/WebGoat/crypto/hashing/sha256');
$("#md5token").load('/WebGoat/crypto/hashing/md5');
$("#basicauthtoken").load('/WebGoat/crypto/encoding/basic');
$("#privatekey").load('/WebGoat/crypto/signing/getprivate');
}
$(document).ready(initialise);
</script>
</header>
<body>
<!-- 1. overview -->
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:Crypto_plan.adoc"></div>
</div>
<!-- 2. encoding -->
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:encoding_plan.adoc"></div>
<!-- 2. assignment -->
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
Now suppose you have intercepted the following header:<br/>
<div id="basicauthtoken" ></div><br/>
<form class="attack-form" method="POST" name="form" action="/WebGoat/crypto/encoding/basic-auth">
Then what was the username
<input name="answer_user" value="" type="TEXT"/>
and what was the password:
<input name="answer_pwd" value="" type="TEXT"/>
<input name="SUBMIT" value="post the answer" type="SUBMIT"/>
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
<!-- 3. encoding xor -->
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:encoding_plan2.adoc"></div>
<!-- 3. assignment xor -->
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" method="POST" name="form" action="/WebGoat/crypto/encoding/xor">
Suppose you found the database password encoded as {xor}Oz4rPj0+LDovPiwsKDAtOw==<br/>
What would be the actual password
<input name="answer_pwd1" value="" type="TEXT"/><br/>
<input name="SUBMIT" value="post the answer" type="SUBMIT"/>
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
<!-- 4. hashing -->
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:hashing_plan.adoc"></div>
<!-- 4. weak hashing exercise -->
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" method="POST" name="form" action="/WebGoat/crypto/hashing">
Which password belongs to this hash: <div id="md5token" ></div>
<input name="answer_pwd1" value="" type="TEXT"/><br/>
Which password belongs to this hash: <div id="sha256token" ></div>
<input name="answer_pwd2" value="" type="TEXT"/>
<input name="SUBMIT" value="post the answer" type="SUBMIT"/>
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
<!-- 5. encryption -->
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:encryption.adoc"></div>
</div>
<!-- 6. signing -->
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:signing.adoc"></div>
<!-- 6. assignment -->
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
Now suppose you have the following private key:<br/>
<pre><div id="privatekey" ></div></pre><br/>
<form class="attack-form" method="POST" name="form" action="/WebGoat/crypto/signing/verify">
Then what was the modulus of the public key
<input name="modulus" value="" type="TEXT"/>
and now provide a signature for us based on that modulus
<input name="signature" value="" type="TEXT"/>
<input name="SUBMIT" value="post the answer" type="SUBMIT"/>
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
<!-- 7. keystores -->
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:keystores.adoc"></div>
</div>
<!-- 8. security defaults -->
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:defaults.adoc"></div>
<!-- 8. assignment -->
<div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" method="POST" name="form" action="/WebGoat/crypto/secure/defaults">
What is the unencrypted message<br/>
<input name="secretText" value="" type="TEXT"/><br/>
and what is the name of the file that stored the password <br/>
<input name="secretFileName" value="" type="TEXT"/>
<input name="SUBMIT" value="post the answer" type="SUBMIT"/>
</form>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div>
</div>
<!-- 9. postquantum -->
<div class="lesson-page-wrapper">
<div class="adoc-content" th:replace="doc:postquantum.adoc"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,32 @@
6.crypto.title=Crypto Basics
crypto-encoding.empty=Try again, did you decode it properly?
crypto-encoding.success=Congratulations. That was easy, right?
crypto-hashing.empty=Try again.
crypto-hashing.oneok=Try again. You got 1 right.
crypto-hashing.success=Congratulations. You found it!
crypto-hashing.hints.1=Guess the type of hashing from the length of the hash.
crypto-hashing.hints.2=Find a online hash database or just google on the hash itself.
crypto-signing.hints.1=Use openssl to get the public key from the private key. Apparently both private and public key information are stored.
crypto-signing.hints.2=Use the private key to sign the "modulus" value of the public key.
crypto-signing.hints.3=Actually the "modulus" of the public key is the same as the private key. You could use openssl -in test.key -pubout > test.pub and then openssl -in test.pub -pubin -modulus or other components.
crypto-signing.hints.4=Make sure that you do not take hidden characters into account. You might want to use echo -n "00AE89..." | openssl dgst -sign ...
crypto-signing.notok=The signature does not match the data (modulus)
crypto-signing.modulusnotok=The modulus is not correct
crypto-signing.success=Congratulations. You found it!
crypto-encoding-xor.empty=Try again. This is not right
crypto-encoding-xor.success=Congratulations.
crypto-encoding-xor.hints.1=Did you look for online decoders for WebSphere encoded password?
crypto-secure-defaults.hints.1=After starting the docker container enter the container using docker exec -ti _dockerid_ /bin/bash
crypto-secure-defaults.hints.2=Try to gain access to /root. Try to become user root by su -
crypto-secure-defaults.hints.3=Try to change the /etc/shadow file using docker cp
crypto-secure-defaults.success=Congratulations, you did it!
crypto-secure-defaults.messagenotok=The unencrypted message is not correct, try again. The filename of the password file is correct.
crypto-secure-defaults.notok=Try again or read some of the hints.

View File

@ -0,0 +1,29 @@
= Cryptography Basics
== Concept
This lesson explains different types of cryptography techniques that are commonly used in web applications.
== Goals
The goal is to get familiar with the following forms of techniques:
* link:start.mvc#lesson/Crypto.lesson/1[Encoding]
* link:start.mvc#lesson/Crypto.lesson/3[Hashing]
* link:start.mvc#lesson/Crypto.lesson/4[Encryption]
* link:start.mvc#lesson/Crypto.lesson/5[Signing]
* link:start.mvc#lesson/Crypto.lesson/6[Keystores]
* link:start.mvc#lesson/Crypto.lesson/7[Security defaults]
* link:start.mvc#lesson/Crypto.lesson/8[Post quantum crypto]
=== Assignments
After the explanation of an item there will be several assignments.

View File

@ -0,0 +1,33 @@
= Security defaults
A big problem in all kinds of systems is the use of default configurations.
E.g. default username/passwords in routers, default passwords for keystores, default unencrypted mode, etc.
== Java cacerts
Did you ever *_changeit_*? Putting a password on the cacerts file has some implications. It is important when the trusted certificate authorities need to be protected and an unknown self signed certificate authority cannot be added too easily.
== Protecting your id_rsa private key
Are you using an ssh key for GitHub and or other sites and are you leaving it unencrypted on your disk? Or even on your cloud drive? By default, the generation of an ssh key pair leaves the private key unencrypted. Which makes it easy to use and if stored in a place where only you can go, it offers sufficient protection. However, it is better to encrypt the key. When you want to use the key, you would have to provide the password again.
== SSH username/password to your server
When you are getting a virtual server from some hosting provider, there are usually a lot of not so secure defaults. One of which is that ssh to the server runs on the default port 22 and allows username/password attempts. One of the first things you should do, is to change the configuration that you cannot ssh as user root, and you cannot ssh using username/password, but only with a valid and strong ssh key. If not, then you will notice continuous brute force attempts to login to your server.
== Assignment
In this exercise you need to retrieve a secret that has accidentally been left inside a docker container image. With this secret, you can decrypt the following message: *U2FsdGVkX199jgh5oANElFdtCxIEvdEvciLi+v+5loE+VCuy6Ii0b+5byb5DXp32RPmT02Ek1pf55ctQN+DHbwCPiVRfFQamDmbHBUpD7as=*.
You can decrypt the message by logging in to the running container (docker exec ...) and getting access to the password file located in /root. Then use the openssl command inside the container (for portability issues in openssl on Windows/Mac/Linux)
You can find the secret in the following docker image, which you can start as:
docker run -d renezubcevic/webgoat:findthesecret
[source]
----
echo "U2FsdGVkX199jgh5oANElFdtCxIEvdEvciLi+v+5loE+VCuy6Ii0b+5byb5DXp32RPmT02Ek1pf55ctQN+DHbwCPiVRfFQamDmbHBUpD7as=" | openssl enc -aes-256-cbc -d -a -kfile ....
----

View File

@ -0,0 +1,26 @@
= Cryptography Basics
== Base64 Encoding
Encoding is not realy cryptography, but it is used a lot in all kinds of standards around cryptographic functions. Especially Base64 encoding.
Base64 encoding is a technique used to transform all kinds of bytes to a specific range of bytes. This specific range is the ASCII readable bytes.
This way you can transfer binary data such as secret or private keys more easily. You could even print these out or write them down.
Encoding is also reversible. So if you have the encoded version, you can create the original version.
On wikipedia you can find more details. Basically it goes through all the bytes and transforms each set of 6 bits into a readable byte (8 bits). The result is that the size of the encoded bytes is increased with about 33%.
Hello ==> SGVsbG8=
0x4d 0x61 ==> TWE=
=== Basic Authentication
Basic authentication is sometimes used by web applications. This uses base64 encoding. Therefore, it is important to at least use Transport Layer Security (TLS or more commonly known as https) to protect others from reading the username password that is sent to the server.
$echo -n "myuser:mypassword" | base64
bXl1c2VyOm15cGFzc3dvcmQ=
The HTTP header will look like:
Authorization: Basic bXl1c2VyOm15cGFzc3dvcmQ=

View File

@ -0,0 +1,21 @@
= Cryptography Basics
== Other Encoding
Also other encodings are used.
=== HTML encoding
HTML encoding is used a lot when sending form data and request parameters to the server. Since spaces are not allowed in a URL, this is then replaced by %20.
=== UUEncode
The Unix-2-Unix encoding has been used to send email attachments.
=== XOR encoding
Sometimes encoding is used as a first and simple obfuscation technique for storing passwords. IBM WebSphere Application Server e.g. uses a specific implementation of XOR encoding to store passwords in configuration files. IBM recommends to protect access to these files and to replace the default XOR encoding by your own custom encryption. However when these recommendations are not followed, these defaults can become a vulnerability.
== Assignment
Now let's see if you are able to find out the original password from this default XOR encoded string.

View File

@ -0,0 +1,32 @@
= Encryption
== Symmetric encryption
Symmetric encryption is based on a shared secret that is used for both encryption as well as decryption. Both parties involved in exchanging secrets therefore share the same key.
Example protocols are:
* AES
* 3DES
== Asymmetric encryption
Asymmetric encryption is based on mathematical principals that consist of a key pair. The two keys are usually called a private key and a public key. The private key needs to be protected very well and is only known to one party. All others can freely use the public key. Something encrypted with the private key can be decrypted by all that have the public key, and something encryted with the public key can only be decrypted with the private key.
Example protocols are:
* RSA
* DSA
== HTTPS uses both symmetric and asymmetric keys
Here is a short description of what happens if you open your browser and go to an https site.
* Your browser connects to the server and gets the webserver certificate
* Your browser checks if it trusts the certificate issuer by checking if the issuer certificate is in its trust store. This trust store is managed by operating and browser updates. And on some coroporate networks it is managed by the company. From the certificate the browser obtains the public key.
* The browser now generates random bytes to be used to generate a symmetric key and encrypts this the public key of the server. So only the server can decrypt it.
* At the end of this process both the browser and the webserver will use the exchanged symmetric key (in the asymmetric key exchange process) to encrypt and decrypt messages that are send back and forth between the browser and the webserver.
Symmetric keys are used because it can be used more safely for large amounts of data.

View File

@ -0,0 +1,15 @@
= Cryptography Basics
== Hashing
Hashing is a type of cryptography which is mostly used to detect if the original data has been changed. A hash is generated from the original data. It is based on irreversible cryptographic techniques.
If the original data is changed by even one byte, the resulting hash is also different.
So in a way it looks like a secure technique. However, it is NOT and even NEVER a good solution when using it for passwords. The problem here is that you can generate passwords from dictionaries and calculate all kinds of variants from these passwords. For each password you can calculate a hash. This can all be stored in large databases. So whenever you find a hash that could be a password, you just look up the hash in the database and find out the password.
Some hashing algorithms should no longer be used: MD5, SHA-1
For these hashes it is possible to change the payload in such a way that it still results in the same hash. This takes a lot of computing power, but is still a feasible option.
== Assignment
Now let's see if you can find what passwords matches which hashes.

View File

@ -0,0 +1,35 @@
= Keystores &amp; Truststores
A keystore is a place where you store keys. Besides *_keystore_* the term *_truststore_* is also used frequently. A truststore is the same thing as a keystore. Only it usually contains only the certificates (so basically only public keys and issuer information) of trusted certificates or certificate authorities.
== File based keystores
A file based keystore is something that in the end has the keys on a file system.
Storing public certificates in a file based keystore is very common
== Database keystores
Keys and especially public certificates can of course also be stored in a database.
== Hardware keystore
A hardware keystore is a system that has some sort of hardware which contain the actual keys.
This is typically done in high end security environments where the private key is really private.
In comparison with file based or database keystores, it is impossible to make a copy of the keystore to send it to some unknown and untrusted environment.
Some certificate authorities that are used to provide you with a server certificate for your website, also create the private keys for you (as-a-service). However, it is by definition no longer considered a private key. For all keystore types, you should keep the private key private and use a certificate signing request to order your signing or server certificates.
== Managed keystores in operating system, browser and other applications
When you visit a website and your browser says that the certificates are fine, it means that the certificate used for the website is issued by a trusted certificate authority. But this list of trusted certificate authorites is managed. Some CA's might be revoked or removed. These updates happen in the background when browser updates are installed.
Not only the browser maitains a list of trusted certificate authorities, the operation system does so as well. And the Java runtime also has its own list which is kept in the cacerts file. Updates of the OS and Java JRE keep this list up to date. In coporate environments, these are usually maintained by the company and also contain company root certificates.
== Extra check for website certificates using DNS CAA records
Some companies inspect all or most internet traffic. Even the ones were you think you have an end-2-end secured connection. This works as follows. An employee opens a browser and googles some information. The browser will use https and go to the site of google. The link looks real and the lock is shown in the browser. However, if you would inspect the certificate, you might notice that it has been issued by one of your companies root CA's! So you have established an end-2-end secure connection with a server of your company, and that server has the secure connection with google.
In order to prevent such man in the middle connections to your server, modern browsers now will also check the DNS CAA records to see whether or not a certain issuer is allowed for a certain website.
More information: https://en.wikipedia.org/wiki/DNS_Certification_Authority_Authorization[Wiki DNS CAA,window=_blank]
== Free certificates from Let's encrypt
https://letsencrypt.org[Let's encrypt,,window=_blank] is a free, automated and open Certificate Authority. It allows you to create valid certificates for the websites that you control. By following and implementing a certain protocol, your identity is checked and a certificate will be issued. The certificates are free of charge and this is done to stimulate the use of authorised certificates and to lower the use of self-signed certificates on the internet. Certificates are valid for 90 days, so they need to be automatically renewed. (Which makes sure that the proof of identity/ownership also takes place frequently)

View File

@ -0,0 +1,7 @@
= Post Quantum
== Post quantum cryptography
Quantum computers are here and getting more power in available qubits each year. Quantum computers are and will be capable of decrypting information that was encrypted with algorithms that were thought to be safe. For some years now, a lot of encrypted communicatation using quantum vulnerable cryptoraphy is being recorded. This information will be decrypted when the quantum computers are powerful enough. Even tough the information may be old, it still could contain valuable information that can be misused. Besides the fact that some private information will be known to parties it was not intended for.
Mathematics has answers for the post quantum era. New cryptography is already available and should be used NOW in order to minimize threads. You can read more on this on Wikipedia https://en.wikipedia.org/wiki/Post-quantum_cryptography[Post quatum on Wikipedia,window=_blank]

View File

@ -0,0 +1,40 @@
= Signing
A signature is something that can be used to check the validity of some data. The signature can be supplied seperately from the data is undersigns or in case of CMS or SOAP be included in the same file. (Where parts of that file contain the data and parts contain the signature or other).
Signing is used when integrity is important. It is meant to be a garantee that data send from A to B was not altered. So A signs the data by calculating the hash of the data and encrypting that hash using an assymmetric private key. B can then verify the data by calculating the hash of the data and decrypting the signature to compare if both hashes are the same.
== RAW signatures
A raw signature is usually calculates as follows:
* create a hash of the data (e.g. SHA-256 hash)
* encrypt the hash using a assymmetric private key (e.g. RSA 2048 bit key)
* (optionally) encode the binary encrypted hash using base64 encoding
B will have to get the certificate with the public key as well. This might have been exchanged before. So at least 3 files are involved. The data, the signature and the certificate.
== CMS signatures
A CMS signature is a standardized way to send data + signature + certificate with the public key all in one file from A to B. As long as the certificate is valid and not revoked, B can use the supplied public key to verify the signature.
== SOAP signatures
A SOAP signature also contains data and the signature and optionally the certificate. All in one XML payload. There are special steps involved in calculating the hash of the data. This has to do with the fact that the SOAP XML send from system to system might introduce extra elements or timestamps.
Also, SOAP Signing offers the posibility to sign different parts of the message by different parties.
== Email signatures
Sending emails is not very difficult. You have to fill in some data and send it to a server that forwards it and finally it will end up at its destination. However, it is possible to send out emails with a FROM field that is not your own email address. In order to garantee to your receiver that you really sent this email, you can sign your email. A trusted third party will check your identity and issue a email signing certificate. You install the private key in your email application and configure it to sign emails that you send out. The certificate is issued on a specific email address and all others that receive this email will see a picture that the sender is verified, because their tools will verify the signature using the public certificate that was issued by the trusted third party.
== PDF or Word or other signatures.
Adobe PDF documents and Microsoft Word documents are also examples of things that support signing. The signature is also inside the same document as the data so there is some description on what is part of the data and what is part of the meta data.
Governments usually send official documents with a PDF that contains a certificate.
== Assignment
Here is a simple assignment. A private key is sent to you. With this key you should send the server a payload with a matching valid signature. As the payload you should determine the RSA key modulus and pass these hexstrings as the modulus. And calculate the signature over this string.

View File

@ -0,0 +1,5 @@
= Crypto Basics
== Solution
Solution goes here

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div class="lesson-page-wrapper">
<!-- reuse this block for each 'page' of content -->
<!-- include content here ... will be first page/tab multiple -->
<div class="adoc-content" th:replace="doc:crypto_solution.adoc"></div>
</div>
</html>

View File

@ -0,0 +1,32 @@
package org.owasp.webgoat.crypto;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.interfaces.RSAPublicKey;
import javax.xml.bind.DatatypeConverter;
import org.junit.jupiter.api.Test;
public class CryptoUtilTest {
@Test
public void testSigningAssignment() {
try {
KeyPair keyPair = CryptoUtil.generateKeyPair();
RSAPublicKey rsaPubKey = (RSAPublicKey) keyPair.getPublic();
PrivateKey privateKey = CryptoUtil.getPrivateKeyFromPEM(CryptoUtil.getPrivateKeyInPEM(keyPair));
String modulus = DatatypeConverter.printHexBinary(rsaPubKey.getModulus().toByteArray());
String signature = CryptoUtil.signMessage(modulus, privateKey);
System.out.println(rsaPubKey.getPublicExponent());
assertTrue(CryptoUtil.verifyAssignment(modulus, signature, keyPair.getPublic()));
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
}

View File

@ -39,6 +39,7 @@
<module>ssrf</module> <module>ssrf</module>
<module>secure-passwords</module> <module>secure-passwords</module>
<module>webgoat-lesson-template</module> <module>webgoat-lesson-template</module>
<module>crypto</module>
</modules> </modules>
<dependencies> <dependencies>

View File

@ -144,7 +144,11 @@
<artifactId>webgoat-lesson-template</artifactId> <artifactId>webgoat-lesson-template</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.owasp.webgoat.lesson</groupId>
<artifactId>crypto</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId> <artifactId>spring-boot-devtools</artifactId>