-
-
Notifications
You must be signed in to change notification settings - Fork 454
feat: add challenge 49 #1651
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
feat: add challenge 49 #1651
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
101 changes: 101 additions & 0 deletions
101
src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge49.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package org.owasp.wrongsecrets.challenges.docker; | ||
|
||
import static org.owasp.wrongsecrets.Challenges.ErrorResponses.DECRYPTION_ERROR; | ||
|
||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; | ||
import java.nio.charset.StandardCharsets; | ||
import java.security.MessageDigest; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.util.Base64; | ||
import javax.crypto.Cipher; | ||
import javax.crypto.SecretKey; | ||
import javax.crypto.spec.SecretKeySpec; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.owasp.wrongsecrets.challenges.Challenge; | ||
import org.owasp.wrongsecrets.challenges.Spoiler; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
|
||
/** This is a challenge based on using weak KDF to protect secrets. */ | ||
@Slf4j | ||
@Component | ||
public class Challenge49 implements Challenge { | ||
|
||
private final String cipherText; | ||
private final String pin; | ||
|
||
public Challenge49( | ||
@Value("${challenge49ciphertext}") String cipherText, | ||
@Value("${challenge49pin}") String pin) { | ||
this.cipherText = cipherText; | ||
this.pin = pin; | ||
} | ||
|
||
@Override | ||
public Spoiler spoiler() { | ||
return new Spoiler(base64Decode(pin)); | ||
} | ||
|
||
@Override | ||
public boolean answerCorrect(String answer) { | ||
String plainText = "the answer"; | ||
|
||
try { | ||
int enteredPin = Integer.parseInt(answer); | ||
if (enteredPin < 0 || enteredPin > 99999) { | ||
return false; | ||
} | ||
} catch (Exception e) { | ||
log.warn("given answer is not an integer", e); | ||
return false; | ||
} | ||
|
||
try { | ||
String md5Hash = hashWithMd5(answer); | ||
return decrypt(cipherText, md5Hash).equals(plainText); | ||
} catch (Exception e) { | ||
log.warn("there was an exception with hashing content in challenge49", e); | ||
return false; | ||
} | ||
} | ||
|
||
@SuppressFBWarnings( | ||
value = "WEAK_MESSAGE_DIGEST_MD5", | ||
justification = "This is to allow md5 hashing") | ||
private String hashWithMd5(String plainText) throws NoSuchAlgorithmException { | ||
MessageDigest md = MessageDigest.getInstance("MD5"); | ||
|
||
byte[] result = md.digest(plainText.getBytes(StandardCharsets.UTF_8)); | ||
StringBuilder hexString = new StringBuilder(); | ||
for (byte b : result) { | ||
hexString.append(String.format("%02x", b)); | ||
} | ||
return hexString.toString(); | ||
} | ||
|
||
@SuppressFBWarnings( | ||
value = {"CIPHER_INTEGRITY", "ECB_MODE"}, | ||
justification = "This is to allow ecb encryption") | ||
private String decrypt(String cipherText, String key) { | ||
try { | ||
byte[] decodedEncryptedText = Base64.getDecoder().decode(cipherText); | ||
|
||
SecretKey secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES"); | ||
|
||
Cipher cipher = Cipher.getInstance("AES"); | ||
cipher.init(Cipher.DECRYPT_MODE, secretKey); | ||
|
||
byte[] decryptedData = cipher.doFinal(decodedEncryptedText); | ||
|
||
return new String(decryptedData, StandardCharsets.UTF_8); | ||
} catch (Exception e) { | ||
log.warn("there was an exception with decrypting content in challenge49", e); | ||
return DECRYPTION_ERROR; | ||
} | ||
} | ||
|
||
private String base64Decode(String base64) { | ||
byte[] decodedBytes = Base64.getDecoder().decode(base64); | ||
return new String(decodedBytes, StandardCharsets.UTF_8); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
=== Cracking AES Encryption with a Weak MD5 Key | ||
|
||
Imagine you're a security analyst investigating a mobile app that handles sensitive information. You discover that the developer is using AES encryption to protect a secret, but instead of using a strong Key Derivation Function (KDF), they rely on the insecure MD5 algorithm to derive encryption keys from a simple numeric PIN. | ||
|
||
You’ve obtained an encrypted string: `k800mdwu8vlQoqeAgRMHDQ==`. You know that this string, when decrypted, reveals the text `the answer`. | ||
|
||
The key used for AES encryption is derived by taking the MD5 hash of a PIN, which is a number between 0 and 99999. Your task is to find the correct PIN that was used to derive the encryption key and decrypt the secret. | ||
|
||
Can you figure out the correct PIN and unlock the secret? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
The simplest way to crack the PIN in this scenario is to perform a brute-force attack due to the limited range of possible values (0 to 99,999). | ||
|
||
- Iterate over all possible PINs (from 0 to 99,999). | ||
- For each PIN, compute its MD5 hash to get the decryption key and try decrypting provided ciphertext. | ||
- If decrypted text is equal to `the answer`, you've found the correct PIN. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
*Why Using MD5 as a KDF is Bad* | ||
|
||
Protecting keys effectively is crucial, and this means using the right Key Derivation Functions (KDFs) with additional entropy and contextual binding, as emphasized in the https://mas.owasp.org/MASTG/0x04g-Testing-Cryptography/#weak-key-generation-functions[Mobile Security Testing Guide (MSTG).] | ||
|
||
MD5 is too fast and easy to compute, enabling attackers to quickly try a vast number of inputs (like PINs) to derive the key. Additionally (although a bit harder to exploit), the collision space is relatively small, meaning multiple different inputs can lead to the same md5 hash. | ||
|
||
Stronger KDFs are crucial when dealing with sensitive data, as they provide much-needed resistance against brute-force attacks and protect secrets even if the attacker gains access to partial information. | ||
|
||
The MSTG recommends using robust KDFs like PBKDF2, bcrypt, or Argon2, which incorporate additional entropy and enforce computational hardness, making brute-force attacks more costly. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge49Test.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package org.owasp.wrongsecrets.challenges.docker; | ||
|
||
import org.assertj.core.api.Assertions; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
|
||
@ExtendWith(MockitoExtension.class) | ||
class Challenge49Test { | ||
|
||
@Test | ||
void spoilerShouldGiveAnswer() { | ||
var challenge = new Challenge49("uz5cIFm0hW3LtWaqEX0S/Q==", "MTIzNDU="); | ||
Assertions.assertThat(challenge.spoiler().solution()).isEqualTo("12345"); | ||
} | ||
|
||
@Test | ||
void correctPinShouldSolveChallenge() { | ||
var challenge = new Challenge49("uz5cIFm0hW3LtWaqEX0S/Q==", "MTIzNDU="); | ||
Assertions.assertThat(challenge.answerCorrect("12345")).isTrue(); | ||
} | ||
|
||
@Test | ||
void nonIntegerPinShouldNotSolveChallenge() { | ||
var challenge = new Challenge49("uz5cIFm0hW3LtWaqEX0S/Q==", "MTIzNDU="); | ||
Assertions.assertThat(challenge.answerCorrect("abcde")).isFalse(); | ||
} | ||
|
||
@Test | ||
void incorrectPinShouldNotSolveChallenge() { | ||
var challenge = new Challenge49("uz5cIFm0hW3LtWaqEX0S/Q==", "MTIzNDU="); | ||
Assertions.assertThat(challenge.answerCorrect("1234")).isFalse(); | ||
} | ||
|
||
@Test | ||
void pinGreaterThan99999ShouldNotSolveChallenge() { | ||
var challenge = new Challenge49("uz5cIFm0hW3LtWaqEX0S/Q==", "MTIzNDU="); | ||
Assertions.assertThat(challenge.answerCorrect("123456")).isFalse(); | ||
} | ||
|
||
@Test | ||
void pinLesserThan0ShouldNotSolveChallenge() { | ||
var challenge = new Challenge49("uz5cIFm0hW3LtWaqEX0S/Q==", "MTIzNDU="); | ||
Assertions.assertThat(challenge.answerCorrect("-123456")).isFalse(); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.