diff --git a/chatgpt_dump.html b/chatgpt_dump.html new file mode 100644 index 0000000000..c61338f130 --- /dev/null +++ b/chatgpt_dump.html @@ -0,0 +1,11 @@ +ChatGPT - Generate Password
diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/ChallengeUI.java b/src/main/java/org/owasp/wrongsecrets/challenges/ChallengeUI.java index e5e23e933d..a4dfab283c 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/ChallengeUI.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/ChallengeUI.java @@ -86,6 +86,35 @@ public String getTech() { return challengeDefinition.category().category(); } + /** + * Returns an optimized "Look for" hint based on the challenge technology. + * + * @return string with the optimized hint. + */ + public String getLookForHint() { + return switch (getTech()) { + case "Intro" -> "Configuration files, source code, or documentation."; + case "Git" -> "Commit history, branches, or git configuration."; + case "Docker" -> "Dockerfiles, docker-compose files, or container environment variables."; + case "Configmaps", "Secrets", "Vault", "CSI-Driver" -> + "Kubernetes manifests, ConfigMaps, Secrets, or Vault configurations."; + case "Terraform", "IAM privilege escalation" -> + "Cloud infrastructure settings, Terraform files, or IAM policies."; + case "Logging" -> "Application logs or system output."; + case "CI/CD" -> "Pipeline configurations or build scripts."; + case "Password Manager" -> "Password manager entries or credentials."; + case "Cryptography" -> "Cryptographic keys, algorithms, or encoded strings."; + case "Binary" -> "Binary files, decompiled code, or executable strings."; + case "Front-end" -> "JavaScript code, HTML source, or browser storage."; + case "Web3" -> "Smart contracts or blockchain transactions."; + case "Documentation" -> "Project documentation or comments."; + case "AI" -> "AI prompts, model configurations, or chat history."; + default -> + "Configuration files, source code, environment variables, Docker files, or cloud" + + " infrastructure related to this challenge."; + }; + } + /** * Returns the number of the next challenge (e.g current+1). * diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge68.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge68.java new file mode 100644 index 0000000000..8071a5cb13 --- /dev/null +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge68.java @@ -0,0 +1,38 @@ +package org.owasp.wrongsecrets.challenges.docker; + +import static org.owasp.wrongsecrets.Challenges.ErrorResponses.DECRYPTION_ERROR; + +import java.nio.charset.StandardCharsets; +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.FixedAnswerChallenge; +import org.springframework.stereotype.Component; + +/** This challenge is about finding a secret shared in a ChatGPT chat link. */ +@Slf4j +@Component +public class Challenge68 extends FixedAnswerChallenge { + + private static final String CIPHERTEXT = "dagGnT4fdF7/z010sAHhhp1t6w2NsUQkHy9+Fx1LCG0="; + + @Override + public String getAnswer() { + try { + byte[] keyBytes = "SuperSecretKey12".getBytes(StandardCharsets.UTF_8); + byte[] ivBytes = "InitVector123456".getBytes(StandardCharsets.UTF_8); + byte[] cipherBytes = Base64.getDecoder().decode(CIPHERTEXT); + SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); + IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); + byte[] decrypted = cipher.doFinal(cipherBytes); + return new String(decrypted, StandardCharsets.UTF_8).trim(); + } catch (Exception e) { + log.error("Decryption failed", e); + return DECRYPTION_ERROR; + } + } +} diff --git a/src/main/resources/explanations/challenge68.adoc b/src/main/resources/explanations/challenge68.adoc new file mode 100644 index 0000000000..337e88f2a7 --- /dev/null +++ b/src/main/resources/explanations/challenge68.adoc @@ -0,0 +1,5 @@ +=== AI Chat Secret Leak + +Sharing chat sessions from AI tools like ChatGPT can be a great way to collaborate, but it's easy to forget that these shared links are public. If the chat contains sensitive information, anyone with the link can see it. + +Can you find the secret in this https://chatgpt.com/share/6a99237a-c2e0-83eb-9a09-a1cf76213ff5[shared ChatGPT session]? diff --git a/src/main/resources/explanations/challenge68_hint.adoc b/src/main/resources/explanations/challenge68_hint.adoc new file mode 100644 index 0000000000..8c20a4d86b --- /dev/null +++ b/src/main/resources/explanations/challenge68_hint.adoc @@ -0,0 +1 @@ +Follow the link to the shared ChatGPT session and look for a generated password. diff --git a/src/main/resources/explanations/challenge68_reason.adoc b/src/main/resources/explanations/challenge68_reason.adoc new file mode 100644 index 0000000000..fa0b852341 --- /dev/null +++ b/src/main/resources/explanations/challenge68_reason.adoc @@ -0,0 +1,8 @@ +=== Why sharing AI chats with secrets is dangerous + +AI models are often used to generate code, configuration, or even credentials. When a user shares a chat session, they are creating a public URL that anyone can access. If the session contains API keys, passwords, or proprietary logic, those secrets are now exposed. + +It is important to: +- Never provide real secrets to AI tools. +- Scrub any sensitive information from chats before sharing them. +- Use enterprise versions of AI tools that offer better data protection and sharing controls. diff --git a/src/main/resources/templates/challenge.html b/src/main/resources/templates/challenge.html index 5561fb0471..7d325a4ed0 100644 --- a/src/main/resources/templates/challenge.html +++ b/src/main/resources/templates/challenge.html @@ -12,7 +12,7 @@
diff --git a/src/main/resources/wrong-secrets-configuration.yaml b/src/main/resources/wrong-secrets-configuration.yaml index 6871933e11..cf1f17a182 100644 --- a/src/main/resources/wrong-secrets-configuration.yaml +++ b/src/main/resources/wrong-secrets-configuration.yaml @@ -944,7 +944,7 @@ configurations: reason: "explanations/challenge61_reason.adoc" environments: *all_envs difficulty: *normal - category: *secrets + category: *git ctf: enabled: true @@ -1013,3 +1013,16 @@ configurations: category: *bin ctf: enabled: true + + - name: Challenge 68 + short-name: "challenge-68" + sources: + - class-name: "org.owasp.wrongsecrets.challenges.docker.Challenge68" + explanation: "explanations/challenge68.adoc" + hint: "explanations/challenge68_hint.adoc" + reason: "explanations/challenge68_reason.adoc" + environments: *all_envs + difficulty: *easy + category: *ai + ctf: + enabled: true diff --git a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge68Test.java b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge68Test.java new file mode 100644 index 0000000000..6326946bcb --- /dev/null +++ b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge68Test.java @@ -0,0 +1,30 @@ +package org.owasp.wrongsecrets.challenges.docker; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.owasp.wrongsecrets.challenges.Spoiler; + +class Challenge68Test { + + @Test + void spoilerShouldRevealAnswer() { + var challenge = new Challenge68(); + + assertThat(challenge.spoiler()).isEqualTo(new Spoiler("Q7v!mR2#xL9@pT6$wN4&kZ8^cF3*Hs5")); + } + + @Test + void rightAnswerShouldSolveChallenge() { + var challenge = new Challenge68(); + + assertThat(challenge.answerCorrect("Q7v!mR2#xL9@pT6$wN4&kZ8^cF3*Hs5")).isTrue(); + } + + @Test + void incorrectAnswerShouldNotSolveChallenge() { + var challenge = new Challenge68(); + + assertThat(challenge.answerCorrect("wrong answer")).isFalse(); + } +}