Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.owasp.wrongsecrets.challenges.docker.challenge63;

import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
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.stereotype.Component;

/**
* Challenge demonstrating bad encryption practices: hardcoding both the encryption key and IV
* directly in source code. Even though the secret is encrypted, the key is right here in the code,
* making the encryption completely ineffective.
*/
@SuppressWarnings("java:S5542")
@Slf4j
Comment thread
commjoen marked this conversation as resolved.
@Component
public class Challenge63 implements Challenge {

private static final String HARDCODED_KEY = "SuperSecretKey12";
private static final String HARDCODED_IV = "InitVector123456";
private static final String CIPHERTEXT = "TDPwOvcLsbCWV5erlk6OHFnlFoXNtdQOt2JQeq+i4Ho=";

public Challenge63() {
// explicit constructor required
}

@Override
public Spoiler spoiler() {
return new Spoiler(getAnswer());
}

@Override
public boolean answerCorrect(String answer) {
return getAnswer().equals(answer);
}

private String getAnswer() {
try {
byte[] keyBytes = HARDCODED_KEY.getBytes("UTF-8");
byte[] ivBytes = HARDCODED_IV.getBytes("UTF-8");
byte[] cipherBytes = Base64.getDecoder().decode(CIPHERTEXT);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
// Intentionally using CBC mode to demonstrate padding oracle vulnerability
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
Comment thread
commjoen marked this conversation as resolved.
Dismissed
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] decrypted = cipher.doFinal(cipherBytes);
Comment thread
commjoen marked this conversation as resolved.
return new String(decrypted, "UTF-8").trim();
} catch (Exception e) {
log.error("Decryption failed", e);
return "decryption-error";
Comment thread
commjoen marked this conversation as resolved.
Outdated
}
}
}
24 changes: 24 additions & 0 deletions src/main/resources/explanations/challenge63.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
=== Hardcoded Encryption Key Challenge
This challenge demonstrates a common but dangerous mistake: encrypting a secret while hardcoding the encryption key in the same source file.

A developer has attempted to protect a secret by encrypting it with AES. However, both the encryption key and the initialization vector (IV) are hardcoded directly in the Java source code alongside the ciphertext. This makes the encryption completely ineffective — anyone with access to the source code can decrypt the secret trivially.

**Your goal:**
1. **Find the Java source file** for this challenge in the codebase
2. **Locate the hardcoded AES key and IV** in the source code
3. **Decrypt the ciphertext** using the key and IV you found
4. **Submit the plaintext** as your answer

**How to decrypt:**
[source,bash]
----
echo "TDPwOvcLsbCWV5erlk6OHFnlFoXNtdQOt2JQeq+i4Ho=" | openssl enc -d -aes-128-cbc \
-K $(echo -n "SuperSecretKey12" | xxd -p) \
-iv $(echo -n "InitVector123456" | xxd -p) \
-base64
----

****
*Note:*
The key insight here is that encryption does NOT protect a secret if the key is stored alongside the ciphertext. This is equivalent to locking a box and taping the key to the outside.
****
Comment thread
commjoen marked this conversation as resolved.
Outdated
26 changes: 26 additions & 0 deletions src/main/resources/explanations/challenge63_hint.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
=== Hint for Challenge 63
This challenge demonstrates bad encryption practices — specifically hardcoding an AES key in source code.
Comment thread
commjoen marked this conversation as resolved.
Outdated

**Where to look:**
1. **Find the Challenge63 Java source file** in the `challenges/docker/challenge63/` directory
2. **Look for static final String constants** near the top of the class
3. **You will find the AES key, IV, and ciphertext** all in the same file

**How to decrypt once you have the key:**
[source,python]
----
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import base64

key = b'SuperSecretKey12'
iv = b'InitVector123456'
ciphertext = base64.b64decode("TDPwOvcLsbCWV5erlk6OHFnlFoXNtdQOt2JQeq+i4Ho=")

cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
plaintext = decryptor.update(ciphertext) + decryptor.finalize()
print(plaintext.strip())
----
Comment thread
commjoen marked this conversation as resolved.

**Remember:** If an attacker can read your source code, hardcoded keys offer zero protection.
24 changes: 24 additions & 0 deletions src/main/resources/explanations/challenge63_reason.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
=== Reason for Challenge 63
This challenge highlights a widespread mistake in software development: using encryption while storing the key in the same location as the ciphertext.

**Why this is dangerous:**
- Encryption is only as strong as the secrecy of the key
- Hardcoding the key in source code means anyone with repository access can decrypt the secret
- Source code is often more widely accessible than developers realize — through Git history, leaked repos, or insider access
- Many secret scanning tools will detect both the key and ciphertext patterns

**The correct approach:**
- Store encryption keys in a dedicated secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.)
- Never commit keys to version control
- Use environment variables for keys, not source code constants
- Consider whether encryption is even necessary if the key must live near the data

**Real world examples:**
This exact pattern has been found in numerous data breaches where developers believed their secrets were "safe" because they were encrypted, not realizing the key was equally exposed.

Additionally, this challenge uses AES in CBC mode which is vulnerable to
padding oracle attacks. Production code should use AES/GCM instead.

**References:**
- https://owasp.org/www-project-top-ten/[OWASP Top 10]
- https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html[OWASP Secrets Management Cheat Sheet]
13 changes: 13 additions & 0 deletions src/main/resources/wrong-secrets-configuration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -961,3 +961,16 @@ configurations:
category: *ai
ctf:
enabled: true

- name: Challenge 63
short-name: "challenge-63"
sources:
- class-name: "org.owasp.wrongsecrets.challenges.docker.challenge63.Challenge63"
explanation: "explanations/challenge63.adoc"
hint: "explanations/challenge63_hint.adoc"
reason: "explanations/challenge63_reason.adoc"
environments: *all_envs
difficulty: *normal
category: *crypto
ctf:
enabled: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.owasp.wrongsecrets.challenges.docker;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;
import org.owasp.wrongsecrets.challenges.docker.challenge63.Challenge63;

class Challenge63Test {

@Test
void testAnswerIsCorrect() {
Challenge63 challenge = new Challenge63();
assertTrue(challenge.answerCorrect("wh0sp1ay1ngwrongs3cr3ts"));
Comment thread
commjoen marked this conversation as resolved.
Outdated
}

@Test
void testWrongAnswerIsRejected() {
Challenge63 challenge = new Challenge63();
assertFalse(challenge.answerCorrect("wronganswer"));
}

@Test
void testSpoilerRevealsAnswer() {
Challenge63 challenge = new Challenge63();
assertEquals("wh0sp1ay1ngwrongs3cr3ts", challenge.spoiler().solution());
Comment thread
commjoen marked this conversation as resolved.
Outdated
}
}
Loading