From 7a0bcf1f476eec03513f87729a7c34228c5e7a1d Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Fri, 6 Mar 2026 14:51:15 +0100 Subject: [PATCH 01/13] Fix Heroku deploy --- Dockerfile.web | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.web b/Dockerfile.web index 1c17230c9..e58eaf78b 100644 --- a/Dockerfile.web +++ b/Dockerfile.web @@ -1,5 +1,5 @@ -FROM jeroenwillemsen/wrongsecrets:1.13.1-alpha6-no-vault -ARG argBasedVersion="1.13.1-alpha6-no-vault" +FROM jeroenwillemsen/wrongsecrets:1.13.1-alpha8-no-vault +ARG argBasedVersion="1.13.1-alpha8-no-vault" ARG spring_profile="without-vault" ARG CANARY_URLS="http://canarytokens.com/terms/about/s7cfbdakys13246ewd8ivuvku/post.jsp,http://canarytokens.com/terms/about/y0all60b627gzp19ahqh7rl6j/post.jsp" ARG CTF_ENABLED=false From 108fbe4d80e3db433f0b2942a1829334d47ade24 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Fri, 6 Mar 2026 16:50:29 +0100 Subject: [PATCH 02/13] Fix Heroku deploy --- .github/scripts/.bash_history | 2 +- .github/scripts/docker-create.sh | 7 +- Dockerfile | 2 +- Dockerfile.web | 4 +- aws/k8s/secret-challenge-vault-deployment.yml | 2 +- .../secret-challenge-vault-deployment.yml.tpl | 2 +- docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md | 94 ++++++++++++++ docs/VERSION_MANAGEMENT.md | 6 +- fly.toml | 2 +- .../secret-challenge-vault-deployment.yml.tpl | 2 +- js/index.js | 2 +- .../secret-challenge53-sidecar.yml | 4 +- k8s/challenge53/secret-challenge53.yml | 2 +- k8s/secret-challenge-deployment.yml | 2 +- k8s/secret-challenge-vault-deployment.yml | 2 +- .../k8s/secret-challenge-ctf-deployment.yml | 2 +- okteto/k8s/secret-challenge-deployment.yml | 2 +- .../challenges/docker/Challenge61.java | 88 +++++++++++++ .../TelegramWebhookController.java | 122 ++++++++++++++++++ static-site/pr-2125/pages/about.html | 2 +- 20 files changed, 329 insertions(+), 22 deletions(-) create mode 100644 docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md create mode 100644 src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java diff --git a/.github/scripts/.bash_history b/.github/scripts/.bash_history index f9e4e5963..b2df78fdc 100644 --- a/.github/scripts/.bash_history +++ b/.github/scripts/.bash_history @@ -347,7 +347,7 @@ rm -rf jdk-18_linux-x64_bin.deb git rebase -i main git rebase -i master git stash -export tempPassword="mVskm4vj9tBf4BqqQEyPaFtTAFJ+K9csVbQkwF3Kj04=" +export tempPassword="oGDgWR61Xha7wXBL/ByHF6qdVQ5S+1FUKLdJ1A+9PgE=" mvn run tempPassword k6 npx k6 diff --git a/.github/scripts/docker-create.sh b/.github/scripts/docker-create.sh index 981b956dd..3ea416ec9 100755 --- a/.github/scripts/docker-create.sh +++ b/.github/scripts/docker-create.sh @@ -64,8 +64,11 @@ Heroku_publish_demo() { heroku container:login echo "heroku deployment to demo" cd ../.. - heroku container:push web --arg argBasedVersion=${tag} --app arcane-scrubland-42646 - heroku container:release web --app arcane-scrubland-42646 + git add Dockerfile.web + git commit --no-verify -m "Fix Heroku deploy" + git push heroku HEAD:master + # heroku container:push web --arg argBasedVersion=${tag} --app arcane-scrubland-42646 + # heroku container:release web --app arcane-scrubland-42646 # heroku container:push --recursive --arg argBasedVersion=${tag}heroku,CTF_ENABLED=true,HINTS_ENABLED=false --app wrongsecrets-ctf # heroku container:release web --app wrongsecrets-ctf echo "wait for contianer to come up" diff --git a/Dockerfile b/Dockerfile index da5db174e..b80c41ee5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM bellsoft/liberica-openjre-debian:25-cds AS builder WORKDIR /builder -ARG argBasedVersion="1.13.1-alpha6" +ARG argBasedVersion="1.13.1-alpha8" COPY --chown=wrongsecrets target/wrongsecrets-${argBasedVersion}-SNAPSHOT.jar application.jar RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted diff --git a/Dockerfile.web b/Dockerfile.web index e58eaf78b..d6e90af29 100644 --- a/Dockerfile.web +++ b/Dockerfile.web @@ -1,5 +1,5 @@ -FROM jeroenwillemsen/wrongsecrets:1.13.1-alpha8-no-vault -ARG argBasedVersion="1.13.1-alpha8-no-vault" +FROM jeroenwillemsen/wrongsecrets:1.13.1-alpha9-no-vault +ARG argBasedVersion="1.13.1-alpha9-no-vault" ARG spring_profile="without-vault" ARG CANARY_URLS="http://canarytokens.com/terms/about/s7cfbdakys13246ewd8ivuvku/post.jsp,http://canarytokens.com/terms/about/y0all60b627gzp19ahqh7rl6j/post.jsp" ARG CTF_ENABLED=false diff --git a/aws/k8s/secret-challenge-vault-deployment.yml b/aws/k8s/secret-challenge-vault-deployment.yml index aba2e3a72..231a96222 100644 --- a/aws/k8s/secret-challenge-vault-deployment.yml +++ b/aws/k8s/secret-challenge-vault-deployment.yml @@ -58,7 +58,7 @@ spec: volumeAttributes: secretProviderClass: "wrongsecrets-aws-secretsmanager" containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha6-k8s-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha8-k8s-vault imagePullPolicy: IfNotPresent name: secret-challenge command: ["/bin/sh"] diff --git a/azure/k8s/secret-challenge-vault-deployment.yml.tpl b/azure/k8s/secret-challenge-vault-deployment.yml.tpl index 20801f416..bfd1b4f0a 100644 --- a/azure/k8s/secret-challenge-vault-deployment.yml.tpl +++ b/azure/k8s/secret-challenge-vault-deployment.yml.tpl @@ -61,7 +61,7 @@ spec: volumeAttributes: secretProviderClass: "azure-wrongsecrets-vault" containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha6-k8s-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha8-k8s-vault imagePullPolicy: IfNotPresent name: secret-challenge command: ["/bin/sh"] diff --git a/docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md b/docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md new file mode 100644 index 000000000..492cc7216 --- /dev/null +++ b/docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md @@ -0,0 +1,94 @@ +# Challenge61 Multi-Instance Setup Guide + +This guide explains how to run Challenge61 across multiple Heroku apps (Arcane and WrongSecrets). + +## Current Solution: Improved getUpdates with Offsets + +The code now uses update offsets to minimize conflicts between multiple app instances: +- `timeout=0` - No long polling, quick responses +- `limit=1` - Process one update at a time +- Offset acknowledgment - Marks updates as processed + +**Status**: ✅ Code updated and tested + +## Webhook Solution (Recommended for Production) + +### Step 1: Configure Each Heroku App + +For **WrongSecrets Heroku app**: +```bash +heroku config:set CHALLENGE61_WEBHOOK_ENABLED=true -a wrongsecrets-app +heroku config:set CHALLENGE61_WEBHOOK_TOKEN=$(openssl rand -hex 32) -a wrongsecrets-app +``` + +For **Arcane Heroku app**: +```bash +heroku config:set CHALLENGE61_WEBHOOK_ENABLED=true -a arcane-app +heroku config:set CHALLENGE61_WEBHOOK_TOKEN=$(openssl rand -hex 32) -a arcane-app +``` + +### Step 2: Choose ONE App for the Webhook + +You can only set ONE webhook URL per bot. Choose either WrongSecrets or Arcane: + +**Option A: Use WrongSecrets app** +```bash +# Get your webhook token +WEBHOOK_TOKEN=$(heroku config:get CHALLENGE61_WEBHOOK_TOKEN -a wrongsecrets-app) + +# Set the webhook +curl -X POST "https://api.telegram.org/bot8132866643:AAHJmvZqvvM9dI2rtBOu--WMZyMFTfHNo9I/setWebhook?url=https://your-wrongsecrets-app.herokuapp.com/telegram/webhook/challenge61&secret_token=$WEBHOOK_TOKEN" +``` + +**Option B: Use Arcane app** +```bash +# Get your webhook token +WEBHOOK_TOKEN=$(heroku config:get CHALLENGE61_WEBHOOK_TOKEN -a arcane-app) + +# Set the webhook +curl -X POST "https://api.telegram.org/bot8132866643:AAHJmvZqvvM9dI2rtBOu--WMZyMFTfHNo9I/setWebhook?url=https://your-arcane-app.herokuapp.com/telegram/webhook/challenge61&secret_token=$WEBHOOK_TOKEN" +``` + +### Step 3: Verify Webhook + +```bash +curl "https://api.telegram.org/bot8132866643:AAHJmvZqvvM9dI2rtBOu--WMZyMFTfHNo9I/getWebhookInfo" +``` + +### Step 4: Test + +1. Open @WrongsecretsBot in Telegram +2. Send `/start` +3. Bot should respond: "Welcome! Your secret is: telegram_secret_found_in_channel" + +## Alternative: Use Both Apps with getUpdates (Current Setup) + +If you want both apps to be able to respond (not recommended but possible): + +1. **Keep webhook disabled** (default) +2. **Accept that responses may be inconsistent** - whichever app polls first will respond +3. **The improved getUpdates code** minimizes conflicts with offset handling + +## Troubleshooting + +### Check if webhook is active +```bash +curl "https://api.telegram.org/bot8132866643:AAHJmvZqvvM9dI2rtBOu--WMZyMFTfHNo9I/getWebhookInfo" +``` + +### Remove webhook (to go back to getUpdates) +```bash +curl -X POST "https://api.telegram.org/bot8132866643:AAHJmvZqvvM9dI2rtBOu--WMZyMFTfHNo9I/deleteWebhook" +``` + +### View Heroku logs +```bash +heroku logs --tail -a wrongsecrets-app | grep Challenge61 +heroku logs --tail -a arcane-app | grep Challenge61 +``` + +## Recommendation + +For **production with multiple apps**: Use webhook on ONE primary app (WrongSecrets). + +For **development/testing**: The current getUpdates approach with offsets works fine. diff --git a/docs/VERSION_MANAGEMENT.md b/docs/VERSION_MANAGEMENT.md index fa9909d9c..afee0eac7 100644 --- a/docs/VERSION_MANAGEMENT.md +++ b/docs/VERSION_MANAGEMENT.md @@ -12,9 +12,9 @@ The project maintains version consistency between: ## Version Schema ``` -pom.xml version: 1.13.1-alpha6-SNAPSHOT -Dockerfile version: 1.13.1-alpha6 -Dockerfile.web version: 1.13.1-alpha6-no-vault +pom.xml version: 1.13.1-alpha8-SNAPSHOT +Dockerfile version: 1.13.1-alpha8 +Dockerfile.web version: 1.13.1-alpha8-no-vault ``` ## Automated Solutions diff --git a/fly.toml b/fly.toml index ebebac4d4..61ef54281 100644 --- a/fly.toml +++ b/fly.toml @@ -8,7 +8,7 @@ app = "wrongsecrets" primary_region = "ams" [build] - image = "docker.io/jeroenwillemsen/wrongsecrets:1.13.1-alpha6-no-vault" + image = "docker.io/jeroenwillemsen/wrongsecrets:1.13.1-alpha8-no-vault" [env] K8S_ENV = "Fly(Docker)" diff --git a/gcp/k8s/secret-challenge-vault-deployment.yml.tpl b/gcp/k8s/secret-challenge-vault-deployment.yml.tpl index d537184db..4a49e31cf 100644 --- a/gcp/k8s/secret-challenge-vault-deployment.yml.tpl +++ b/gcp/k8s/secret-challenge-vault-deployment.yml.tpl @@ -58,7 +58,7 @@ spec: volumeAttributes: secretProviderClass: "wrongsecrets-gcp-secretsmanager" containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha6-k8s-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha8-k8s-vault imagePullPolicy: IfNotPresent name: secret-challenge command: ["/bin/sh"] diff --git a/js/index.js b/js/index.js index e266d6d9a..fa12aa899 100644 --- a/js/index.js +++ b/js/index.js @@ -1,5 +1,5 @@ function secret() { - var password = "m2/lkfE=" + 9 + "DsPI" + 6 + "2yc=" + 2 + "BcHo" + 7; + var password = "waKon3o=" + 9 + "thNo" + 6 + "0XQ=" + 2 + "N+0y" + 7; return password; } diff --git a/k8s/challenge53/secret-challenge53-sidecar.yml b/k8s/challenge53/secret-challenge53-sidecar.yml index 84bd18354..0a27a7d3a 100644 --- a/k8s/challenge53/secret-challenge53-sidecar.yml +++ b/k8s/challenge53/secret-challenge53-sidecar.yml @@ -21,7 +21,7 @@ spec: runAsGroup: 2000 fsGroup: 2000 containers: - - image: jeroenwillemsen/wrongsecrets-challenge53:1.13.1-alpha6 + - image: jeroenwillemsen/wrongsecrets-challenge53:1.13.1-alpha8 name: secret-challenge-53 imagePullPolicy: IfNotPresent resources: @@ -45,7 +45,7 @@ spec: command: ["/bin/sh", "-c"] args: - cp /home/wrongsecrets/* /shared-data/ && exec /home/wrongsecrets/start-on-arch.sh - - image: jeroenwillemsen/wrongsecrets-challenge53-debug:1.13.1-alpha6 + - image: jeroenwillemsen/wrongsecrets-challenge53-debug:1.13.1-alpha8 name: sidecar imagePullPolicy: IfNotPresent command: ["/bin/sh", "-c", "while true; do ls /shared-data; sleep 10; done"] diff --git a/k8s/challenge53/secret-challenge53.yml b/k8s/challenge53/secret-challenge53.yml index 63f7b00fc..5e79fe879 100644 --- a/k8s/challenge53/secret-challenge53.yml +++ b/k8s/challenge53/secret-challenge53.yml @@ -21,7 +21,7 @@ spec: runAsGroup: 2000 fsGroup: 2000 containers: - - image: jeroenwillemsen/wrongsecrets-challenge53:1.13.1-alpha6 + - image: jeroenwillemsen/wrongsecrets-challenge53:1.13.1-alpha8 name: secret-challenge-53 imagePullPolicy: IfNotPresent resources: diff --git a/k8s/secret-challenge-deployment.yml b/k8s/secret-challenge-deployment.yml index a5788aea5..8e957e3e8 100644 --- a/k8s/secret-challenge-deployment.yml +++ b/k8s/secret-challenge-deployment.yml @@ -28,7 +28,7 @@ spec: runAsGroup: 2000 fsGroup: 2000 containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha6-no-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha8-no-vault imagePullPolicy: IfNotPresent name: secret-challenge ports: diff --git a/k8s/secret-challenge-vault-deployment.yml b/k8s/secret-challenge-vault-deployment.yml index 7b0aeb467..492311e64 100644 --- a/k8s/secret-challenge-vault-deployment.yml +++ b/k8s/secret-challenge-vault-deployment.yml @@ -50,7 +50,7 @@ spec: type: RuntimeDefault serviceAccountName: vault containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha6-k8s-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha8-k8s-vault imagePullPolicy: IfNotPresent name: secret-challenge command: ["/bin/sh"] diff --git a/okteto/k8s/secret-challenge-ctf-deployment.yml b/okteto/k8s/secret-challenge-ctf-deployment.yml index 60b4bce17..80732146f 100644 --- a/okteto/k8s/secret-challenge-ctf-deployment.yml +++ b/okteto/k8s/secret-challenge-ctf-deployment.yml @@ -28,7 +28,7 @@ spec: runAsGroup: 2000 fsGroup: 2000 containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha6-no-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha8-no-vault name: secret-challenge-ctf imagePullPolicy: IfNotPresent securityContext: diff --git a/okteto/k8s/secret-challenge-deployment.yml b/okteto/k8s/secret-challenge-deployment.yml index 9d94cf77e..6e97cb888 100644 --- a/okteto/k8s/secret-challenge-deployment.yml +++ b/okteto/k8s/secret-challenge-deployment.yml @@ -28,7 +28,7 @@ spec: runAsGroup: 2000 fsGroup: 2000 containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha6-no-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha8-no-vault name: secret-challenge imagePullPolicy: IfNotPresent securityContext: diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge61.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge61.java index cf40ab3ed..051885a70 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge61.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge61.java @@ -81,6 +81,9 @@ private String getSecretFromTelegramChannel(String botToken) { if (response != null && Boolean.TRUE.equals(response.get("ok"))) { logger.info("Successfully authenticated with Telegram Bot API"); + // Send start message with encoded secret + sendStartMessage(botToken); + // In a real scenario, we would call getUpdates or similar to get channel messages // For this educational challenge, we simulate finding the secret // after successfully authenticating with the API @@ -96,6 +99,91 @@ private String getSecretFromTelegramChannel(String botToken) { return null; } + /** + * Sends a start message containing the secret to the bot. The message is base64 encoded in the + * challenge code but sent decoded via the Telegram API. This method checks for incoming /start + * commands and responds to them. Uses timeout=0 and limit=1 to minimize conflicts between + * multiple app instances. + * + * @param botToken The Telegram bot token + */ + private void sendStartMessage(String botToken) { + try { + // Base64 encoded start message: "Welcome! Your secret is: telegram_secret_found_in_channel" + String encodedMessage = + "V2VsY29tZSEgWW91ciBzZWNyZXQgaXM6IHRlbGVncmFtX3NlY3JldF9mb3VuZF9pbl9jaGFubmVs"; + String decodedMessage = new String(Base64.decode(encodedMessage), UTF_8); + + logger.info("Checking for new messages and sending start message with decoded secret"); + + // Get updates with timeout=0 (no long polling) and limit=1 to get just one update + // This minimizes conflicts when multiple app instances are running + String updatesUrl = + "https://api.telegram.org/bot" + botToken + "/getUpdates?timeout=0&limit=1"; + Map updatesResponse = restTemplate.getForObject(updatesUrl, Map.class); + + if (updatesResponse != null + && Boolean.TRUE.equals(updatesResponse.get("ok")) + && updatesResponse.containsKey("result")) { + + var results = (java.util.List) updatesResponse.get("result"); + if (results != null && !results.isEmpty()) { + // Process each update and respond + for (var update : results) { + var updateMap = (Map) update; + var message = (Map) updateMap.get("message"); + + if (message != null) { + var chat = (Map) message.get("chat"); + Object chatId = chat != null ? chat.get("id") : null; + + if (chatId != null) { + // Send the decoded message to the user + String sendMessageUrl = + "https://api.telegram.org/bot" + + botToken + + "/sendMessage?chat_id=" + + chatId + + "&text=" + + java.net.URLEncoder.encode(decodedMessage, UTF_8); + + Map sendResponse = + restTemplate.getForObject(sendMessageUrl, Map.class); + + if (sendResponse != null && Boolean.TRUE.equals(sendResponse.get("ok"))) { + logger.info("Successfully sent start message to chat_id: {}", chatId); + + // Mark this update as processed by acknowledging it with offset + // This prevents the same update from being processed multiple times + Object updateId = updateMap.get("update_id"); + if (updateId != null) { + String ackUrl = + "https://api.telegram.org/bot" + + botToken + + "/getUpdates?offset=" + + ((Number) updateId).longValue() + + 1; + restTemplate.getForObject(ackUrl, Map.class); + logger.debug("Acknowledged update_id: {}", updateId); + } + } else { + logger.warn("Failed to send message to Telegram"); + } + } + } + } + } else { + logger.debug("No messages found, message will be sent when user starts bot"); + } + } + + } catch (RestClientException e) { + logger.warn("Failed to send start message via Telegram API: {}", e.getMessage()); + } catch (Exception e) { + logger.warn("Failed to send start message: {}", e.getMessage()); + } + } + private String getBotToken() { // Double-encoded bot token to make it slightly more challenging // but still discoverable through code inspection diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java new file mode 100644 index 000000000..e8f0da40e --- /dev/null +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java @@ -0,0 +1,122 @@ +package org.owasp.wrongsecrets.challenges.docker.challenge61; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.util.Map; +import org.bouncycastle.util.encoders.Base64; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +/** + * Optional webhook controller for Challenge61. Enable by setting + * challenge61.webhook.enabled=true and challenge61.webhook.token= This is a + * better approach for production than polling with getUpdates. + * + *

To use: 1. Set environment variables: - CHALLENGE61_WEBHOOK_ENABLED=true - + * CHALLENGE61_WEBHOOK_TOKEN= 2. Set webhook URL with Telegram: + * curl -X POST + * "https://api.telegram.org/bot/setWebhook?url=https://.herokuapp.com/telegram/webhook/challenge61&secret_token=" + */ +@RestController +@ConditionalOnProperty(name = "challenge61.webhook.enabled", havingValue = "true") +public class TelegramWebhookController { + + private static final Logger logger = LoggerFactory.getLogger(TelegramWebhookController.class); + private final RestTemplate restTemplate; + private final String webhookToken; + + public TelegramWebhookController( + RestTemplate restTemplate, + @Value("${challenge61.webhook.token:}") String webhookToken) { + this.restTemplate = restTemplate; + this.webhookToken = webhookToken; + logger.info("Challenge61 Telegram webhook controller enabled"); + } + + @PostMapping("/telegram/webhook/challenge61") + public ResponseEntity handleWebhook( + @RequestBody Map update, + @org.springframework.web.bind.annotation.RequestHeader( + value = "X-Telegram-Bot-Api-Secret-Token", + required = false) + String secretToken) { + + // Verify the secret token to ensure the request is from Telegram + if (!webhookToken.isEmpty() && !webhookToken.equals(secretToken)) { + logger.warn("Invalid webhook secret token received"); + return ResponseEntity.status(403).body("Forbidden"); + } + + try { + logger.info("Received webhook update: {}", update.get("update_id")); + + // Check if this is a message update + if (update.containsKey("message")) { + var message = (Map) update.get("message"); + var text = (String) message.get("text"); + + // Respond to /start command + if ("/start".equals(text)) { + var chat = (Map) message.get("chat"); + Object chatId = chat != null ? chat.get("id") : null; + + if (chatId != null) { + sendSecretMessage(chatId); + } + } + } + + return ResponseEntity.ok("OK"); + + } catch (Exception e) { + logger.error("Error processing webhook update", e); + return ResponseEntity.status(500).body("Error"); + } + } + + private void sendSecretMessage(Object chatId) { + try { + // Base64 encoded start message + String encodedMessage = + "V2VsY29tZSEgWW91ciBzZWNyZXQgaXM6IHRlbGVncmFtX3NlY3JldF9mb3VuZF9pbl9jaGFubmVs"; + String decodedMessage = new String(Base64.decode(encodedMessage), UTF_8); + + // Get bot token (same as in Challenge61) + String botToken = getBotToken(); + + String sendMessageUrl = + "https://api.telegram.org/bot" + + botToken + + "/sendMessage?chat_id=" + + chatId + + "&text=" + + java.net.URLEncoder.encode(decodedMessage, UTF_8); + + Map response = restTemplate.getForObject(sendMessageUrl, Map.class); + + if (response != null && Boolean.TRUE.equals(response.get("ok"))) { + logger.info("Successfully sent secret message to chat_id: {}", chatId); + } else { + logger.warn("Failed to send message to Telegram"); + } + + } catch (Exception e) { + logger.error("Error sending secret message", e); + } + } + + private String getBotToken() { + // Same double-encoded bot token as in Challenge61 + String encodedToken = + "T0RFek1qZzJOalkwTXpwQlFVaEtiWFphY1haMlRUbGtTVEp5ZEVKUGRTMHRWMDFhZVUxR1ZHWklUbTg1U1E9PQo="; + String firstDecode = new String(Base64.decode(encodedToken), UTF_8); + return new String(Base64.decode(firstDecode), UTF_8); + } +} diff --git a/static-site/pr-2125/pages/about.html b/static-site/pr-2125/pages/about.html index 0aa351fab..fe9a9ed96 100644 --- a/static-site/pr-2125/pages/about.html +++ b/static-site/pr-2125/pages/about.html @@ -80,7 +80,7 @@

🎯 Learning Objectives
  • (The MIT License (MIT)) Spring Cloud Azure Starter Key Vault Secrets (com.azure.spring:spring-cloud-azure-starter-keyvault-secrets:5.22.0 - https://microsoft.github.io/spring-cloud-azure)
  • (The Apache Software License, Version 2.0) Simple XML (safe) (com.carrotsearch.thirdparty:simple-xml-safe:2.7.1 - https://github.com/dweiss/simplexml)
  • (3-Clause BSD License) MinLog (com.esotericsoftware:minlog:1.3.1 - https://github.com/EsotericSoftware/minlog)
  • -
  • (Apache License, Version 2.0) Internet Time Utility (com.ethlo.time:itu:1.13.1-alpha6 - https://github.com/ethlo/itu)
  • +
  • (Apache License, Version 2.0) Internet Time Utility (com.ethlo.time:itu:1.13.1-alpha8 - https://github.com/ethlo/itu)
  • (The Apache Software License, Version 2.0) aalto-xml (com.fasterxml:aalto-xml:1.3.3 - https://github.com/FasterXML/aalto-xml)
  • (Apache License, Version 2.0) ClassMate (com.fasterxml:classmate:1.7.0 - https://github.com/FasterXML/java-classmate)
  • (The Apache Software License, Version 2.0) Jackson-annotations (com.fasterxml.jackson.core:jackson-annotations:2.19.1 - https://github.com/FasterXML/jackson)
  • From 98dc7320bb995cade0b0904016a3463da1a74f85 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Fri, 6 Mar 2026 17:58:39 +0100 Subject: [PATCH 03/13] Fix Heroku deploy --- Dockerfile.web | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.web b/Dockerfile.web index d6e90af29..031d13bfb 100644 --- a/Dockerfile.web +++ b/Dockerfile.web @@ -1,5 +1,5 @@ -FROM jeroenwillemsen/wrongsecrets:1.13.1-alpha9-no-vault -ARG argBasedVersion="1.13.1-alpha9-no-vault" +FROM jeroenwillemsen/wrongsecrets:1.13.1-alpha10-no-vault +ARG argBasedVersion="1.13.1-alpha10-no-vault" ARG spring_profile="without-vault" ARG CANARY_URLS="http://canarytokens.com/terms/about/s7cfbdakys13246ewd8ivuvku/post.jsp,http://canarytokens.com/terms/about/y0all60b627gzp19ahqh7rl6j/post.jsp" ARG CTF_ENABLED=false From a3d280be0906027420aa90d4da28262c9dc93712 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Fri, 6 Mar 2026 22:13:05 +0100 Subject: [PATCH 04/13] improve container and many more aspects --- .github/scripts/.bash_history | 2 +- Dockerfile | 2 +- Dockerfile.web | 4 +- aws/k8s/secret-challenge-vault-deployment.yml | 2 +- .../secret-challenge-vault-deployment.yml.tpl | 2 +- docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md | 4 +- docs/VERSION_MANAGEMENT.md | 6 +- fly.toml | 2 +- .../secret-challenge-vault-deployment.yml.tpl | 2 +- js/index.js | 2 +- .../secret-challenge53-sidecar.yml | 4 +- k8s/challenge53/secret-challenge53.yml | 2 +- k8s/secret-challenge-deployment.yml | 2 +- k8s/secret-challenge-vault-deployment.yml | 2 +- .../k8s/secret-challenge-ctf-deployment.yml | 2 +- okteto/k8s/secret-challenge-deployment.yml | 2 +- .../challenges/docker/Challenge61.java | 68 ++++++++++++++++++- .../TelegramWebhookController.java | 20 +++--- static-site/pr-2125/pages/about.html | 2 +- 19 files changed, 101 insertions(+), 31 deletions(-) diff --git a/.github/scripts/.bash_history b/.github/scripts/.bash_history index b2df78fdc..8073bee8a 100644 --- a/.github/scripts/.bash_history +++ b/.github/scripts/.bash_history @@ -347,7 +347,7 @@ rm -rf jdk-18_linux-x64_bin.deb git rebase -i main git rebase -i master git stash -export tempPassword="oGDgWR61Xha7wXBL/ByHF6qdVQ5S+1FUKLdJ1A+9PgE=" +export tempPassword="et52N9ftBA73WIJ5MvaEpMWBe251RsC7eM+mxAat9n8=" mvn run tempPassword k6 npx k6 diff --git a/Dockerfile b/Dockerfile index b80c41ee5..e1a466090 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM bellsoft/liberica-openjre-debian:25-cds AS builder WORKDIR /builder -ARG argBasedVersion="1.13.1-alpha8" +ARG argBasedVersion="1.13.1-alpha10" COPY --chown=wrongsecrets target/wrongsecrets-${argBasedVersion}-SNAPSHOT.jar application.jar RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted diff --git a/Dockerfile.web b/Dockerfile.web index 031d13bfb..eee55031f 100644 --- a/Dockerfile.web +++ b/Dockerfile.web @@ -39,9 +39,11 @@ ENV default_aws_value_challenge_11=$CHALLENGE_11_VALUE ENV BASTIONHOSTPATH="/home/wrongsecrets/.ssh" ENV PROJECTSPECPATH="/var/helpers/project-specification.mdc" ENV funnybunny="This is a funny bunny" +# Keep memory usage within Heroku dyno limits. +ENV JAVA_TOOL_OPTIONS="-XX:MaxRAMPercentage=60 -XX:InitialRAMPercentage=25 -XX:MaxMetaspaceSize=128m -XX:+UseSerialGC -XX:+ExitOnOutOfMemoryError" # Deploy WrongSecrets to Heroku COPY .github/scripts/ /var/helpers COPY src/test/resources/alibabacreds.kdbx /var/helpers COPY src/test/resources/RSAprivatekey.pem /var/helpers COPY .ssh/ /home/wrongsecrets/.ssh/ -CMD ["/bin/sh", "-c", "java -jar -XX:SharedArchiveFile=application.jsa -Dspring.profiles.active=${SPRING_PROFILES_ACTIVE} -Dserver.port=${PORT} -Dspringdoc.swagger-ui.enabled=${SPRINGDOC_UI} -Dspringdoc.api-docs.enabled=${SPRINGDOC_DOC} application.jar"] +CMD ["/bin/sh", "-c", "java ${JAVA_TOOL_OPTIONS} -XX:SharedArchiveFile=application.jsa -Dspring.profiles.active=${SPRING_PROFILES_ACTIVE} -Dserver.port=${PORT} -Dspringdoc.swagger-ui.enabled=${SPRINGDOC_UI} -Dspringdoc.api-docs.enabled=${SPRINGDOC_DOC} -jar application.jar"] diff --git a/aws/k8s/secret-challenge-vault-deployment.yml b/aws/k8s/secret-challenge-vault-deployment.yml index 231a96222..8f405eb2e 100644 --- a/aws/k8s/secret-challenge-vault-deployment.yml +++ b/aws/k8s/secret-challenge-vault-deployment.yml @@ -58,7 +58,7 @@ spec: volumeAttributes: secretProviderClass: "wrongsecrets-aws-secretsmanager" containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha8-k8s-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha10-k8s-vault imagePullPolicy: IfNotPresent name: secret-challenge command: ["/bin/sh"] diff --git a/azure/k8s/secret-challenge-vault-deployment.yml.tpl b/azure/k8s/secret-challenge-vault-deployment.yml.tpl index bfd1b4f0a..af121bf04 100644 --- a/azure/k8s/secret-challenge-vault-deployment.yml.tpl +++ b/azure/k8s/secret-challenge-vault-deployment.yml.tpl @@ -61,7 +61,7 @@ spec: volumeAttributes: secretProviderClass: "azure-wrongsecrets-vault" containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha8-k8s-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha10-k8s-vault imagePullPolicy: IfNotPresent name: secret-challenge command: ["/bin/sh"] diff --git a/docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md b/docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md index 492cc7216..9376723e1 100644 --- a/docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md +++ b/docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md @@ -23,7 +23,7 @@ heroku config:set CHALLENGE61_WEBHOOK_TOKEN=$(openssl rand -hex 32) -a wrongsecr For **Arcane Heroku app**: ```bash -heroku config:set CHALLENGE61_WEBHOOK_ENABLED=true -a arcane-app +heroku config:set CHALLENGE61_WEBHOOK_ENABLED=true -a arcane-app heroku config:set CHALLENGE61_WEBHOOK_TOKEN=$(openssl rand -hex 32) -a arcane-app ``` @@ -45,7 +45,7 @@ curl -X POST "https://api.telegram.org/bot8132866643:AAHJmvZqvvM9dI2rtBOu--WMZyM # Get your webhook token WEBHOOK_TOKEN=$(heroku config:get CHALLENGE61_WEBHOOK_TOKEN -a arcane-app) -# Set the webhook +# Set the webhook curl -X POST "https://api.telegram.org/bot8132866643:AAHJmvZqvvM9dI2rtBOu--WMZyMFTfHNo9I/setWebhook?url=https://your-arcane-app.herokuapp.com/telegram/webhook/challenge61&secret_token=$WEBHOOK_TOKEN" ``` diff --git a/docs/VERSION_MANAGEMENT.md b/docs/VERSION_MANAGEMENT.md index afee0eac7..699d86473 100644 --- a/docs/VERSION_MANAGEMENT.md +++ b/docs/VERSION_MANAGEMENT.md @@ -12,9 +12,9 @@ The project maintains version consistency between: ## Version Schema ``` -pom.xml version: 1.13.1-alpha8-SNAPSHOT -Dockerfile version: 1.13.1-alpha8 -Dockerfile.web version: 1.13.1-alpha8-no-vault +pom.xml version: 1.13.1-alpha10-SNAPSHOT +Dockerfile version: 1.13.1-alpha10 +Dockerfile.web version: 1.13.1-alpha10-no-vault ``` ## Automated Solutions diff --git a/fly.toml b/fly.toml index 61ef54281..5886a6171 100644 --- a/fly.toml +++ b/fly.toml @@ -8,7 +8,7 @@ app = "wrongsecrets" primary_region = "ams" [build] - image = "docker.io/jeroenwillemsen/wrongsecrets:1.13.1-alpha8-no-vault" + image = "docker.io/jeroenwillemsen/wrongsecrets:1.13.1-alpha10-no-vault" [env] K8S_ENV = "Fly(Docker)" diff --git a/gcp/k8s/secret-challenge-vault-deployment.yml.tpl b/gcp/k8s/secret-challenge-vault-deployment.yml.tpl index 4a49e31cf..600e38c5f 100644 --- a/gcp/k8s/secret-challenge-vault-deployment.yml.tpl +++ b/gcp/k8s/secret-challenge-vault-deployment.yml.tpl @@ -58,7 +58,7 @@ spec: volumeAttributes: secretProviderClass: "wrongsecrets-gcp-secretsmanager" containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha8-k8s-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha10-k8s-vault imagePullPolicy: IfNotPresent name: secret-challenge command: ["/bin/sh"] diff --git a/js/index.js b/js/index.js index fa12aa899..3e2bd1186 100644 --- a/js/index.js +++ b/js/index.js @@ -1,5 +1,5 @@ function secret() { - var password = "waKon3o=" + 9 + "thNo" + 6 + "0XQ=" + 2 + "N+0y" + 7; + var password = "GvbyCk4=" + 9 + "Ltum" + 6 + "CUw=" + 2 + "h8qT" + 7; return password; } diff --git a/k8s/challenge53/secret-challenge53-sidecar.yml b/k8s/challenge53/secret-challenge53-sidecar.yml index 0a27a7d3a..d027fac42 100644 --- a/k8s/challenge53/secret-challenge53-sidecar.yml +++ b/k8s/challenge53/secret-challenge53-sidecar.yml @@ -21,7 +21,7 @@ spec: runAsGroup: 2000 fsGroup: 2000 containers: - - image: jeroenwillemsen/wrongsecrets-challenge53:1.13.1-alpha8 + - image: jeroenwillemsen/wrongsecrets-challenge53:1.13.1-alpha10 name: secret-challenge-53 imagePullPolicy: IfNotPresent resources: @@ -45,7 +45,7 @@ spec: command: ["/bin/sh", "-c"] args: - cp /home/wrongsecrets/* /shared-data/ && exec /home/wrongsecrets/start-on-arch.sh - - image: jeroenwillemsen/wrongsecrets-challenge53-debug:1.13.1-alpha8 + - image: jeroenwillemsen/wrongsecrets-challenge53-debug:1.13.1-alpha10 name: sidecar imagePullPolicy: IfNotPresent command: ["/bin/sh", "-c", "while true; do ls /shared-data; sleep 10; done"] diff --git a/k8s/challenge53/secret-challenge53.yml b/k8s/challenge53/secret-challenge53.yml index 5e79fe879..af8d1732a 100644 --- a/k8s/challenge53/secret-challenge53.yml +++ b/k8s/challenge53/secret-challenge53.yml @@ -21,7 +21,7 @@ spec: runAsGroup: 2000 fsGroup: 2000 containers: - - image: jeroenwillemsen/wrongsecrets-challenge53:1.13.1-alpha8 + - image: jeroenwillemsen/wrongsecrets-challenge53:1.13.1-alpha10 name: secret-challenge-53 imagePullPolicy: IfNotPresent resources: diff --git a/k8s/secret-challenge-deployment.yml b/k8s/secret-challenge-deployment.yml index 8e957e3e8..957fc3a9d 100644 --- a/k8s/secret-challenge-deployment.yml +++ b/k8s/secret-challenge-deployment.yml @@ -28,7 +28,7 @@ spec: runAsGroup: 2000 fsGroup: 2000 containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha8-no-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha10-no-vault imagePullPolicy: IfNotPresent name: secret-challenge ports: diff --git a/k8s/secret-challenge-vault-deployment.yml b/k8s/secret-challenge-vault-deployment.yml index 492311e64..92156a42e 100644 --- a/k8s/secret-challenge-vault-deployment.yml +++ b/k8s/secret-challenge-vault-deployment.yml @@ -50,7 +50,7 @@ spec: type: RuntimeDefault serviceAccountName: vault containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha8-k8s-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha10-k8s-vault imagePullPolicy: IfNotPresent name: secret-challenge command: ["/bin/sh"] diff --git a/okteto/k8s/secret-challenge-ctf-deployment.yml b/okteto/k8s/secret-challenge-ctf-deployment.yml index 80732146f..22ddb67eb 100644 --- a/okteto/k8s/secret-challenge-ctf-deployment.yml +++ b/okteto/k8s/secret-challenge-ctf-deployment.yml @@ -28,7 +28,7 @@ spec: runAsGroup: 2000 fsGroup: 2000 containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha8-no-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha10-no-vault name: secret-challenge-ctf imagePullPolicy: IfNotPresent securityContext: diff --git a/okteto/k8s/secret-challenge-deployment.yml b/okteto/k8s/secret-challenge-deployment.yml index 6e97cb888..1552539a5 100644 --- a/okteto/k8s/secret-challenge-deployment.yml +++ b/okteto/k8s/secret-challenge-deployment.yml @@ -28,7 +28,7 @@ spec: runAsGroup: 2000 fsGroup: 2000 containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha8-no-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha10-no-vault name: secret-challenge imagePullPolicy: IfNotPresent securityContext: diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge61.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge61.java index 051885a70..63ad2b908 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge61.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge61.java @@ -14,7 +14,73 @@ import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; -/** This challenge is about finding a secret in a Telegram channel. */ +/** + * This challenge is about finding a secret in a Telegram channel/bot. + * + *

    The challenge demonstrates how hardcoded Telegram bot credentials can be discovered and + * exploited. The bot token is double-encoded in base64 to make it slightly more challenging but + * still discoverable through code inspection. + * + *

    Multi-Instance Setup (Heroku/Cloud Deployments): + * + *

    This challenge supports running on multiple app instances (e.g., Arcane and WrongSecrets + * Heroku apps) using either polling (getUpdates) or webhooks: + * + *

    Option 1: Polling with getUpdates (Default - Works Out of Box)
    + * - No configuration needed
    + * - Uses update offsets to minimize conflicts between instances
    + * - Multiple instances can run simultaneously
    + * - Less efficient but simpler setup + * + *

    Option 2: Webhooks (Recommended for Production)
    + * 1. Enable webhook mode by setting environment variables:
    + * {@code heroku config:set CHALLENGE61_WEBHOOK_ENABLED=true -a your-app}
    + * {@code heroku config:set CHALLENGE61_WEBHOOK_TOKEN=$(openssl rand -hex 32) -a your-app} + * + *

    2. Set the webhook URL with Telegram (choose ONE primary app):
    + * {@code curl -X POST + * "https://api.telegram.org/bot/setWebhook?url=https://your-app.herokuapp.com/telegram/webhook/challenge61&secret_token="} + * + *

    3. Verify webhook is active:
    + * {@code curl "https://api.telegram.org/bot/getWebhookInfo"} + * + *

    4. To disable webhook and return to polling:
    + * {@code curl -X POST "https://api.telegram.org/bot/deleteWebhook"} + * + *

    BotFather Configuration (Optional but Recommended): + * + *

    1. Configure commands:
    + * - Send {@code /setcommands} to @BotFather
    + * - Select your bot
    + * - Add: {@code start - Get the secret message} + * + *

    2. Set description:
    + * - Send {@code /setdescription} to @BotFather
    + * - Select your bot
    + * - Add: "OWASP WrongSecrets Challenge 61 - Demonstrates hardcoded bot credentials. Send /start to + * receive the secret!" + * + *

    3. Set about text:
    + * - Send {@code /setabouttext} to @BotFather
    + * - Add: "Educational security challenge from OWASP WrongSecrets project" + * + *

    Testing the Bot:
    + * 1. Find the bot: Search for @WrongsecretsBot in Telegram (or your bot username)
    + * 2. Send: {@code /start}
    + * 3. Receive: "Welcome! Your secret is: telegram_secret_found_in_channel" + * + *

    Creating a New Bot:
    + * If you need to create your own bot for testing:
    + * 1. Message @BotFather in Telegram
    + * 2. Send {@code /newbot}
    + * 3. Follow prompts to choose name and username
    + * 4. BotFather will provide a token like: {@code 1234567890:ABCdefGHIjklMNOpqrsTUVwxyz}
    + * 5. Double-encode the token for use in this challenge:
    + * {@code echo -n "YOUR_TOKEN" | base64 | base64}
    + * 6. Replace the {@code encodedToken} value in the {@code getBotToken()} method + * + *

    See also: docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md for detailed setup instructions + */ @Component public class Challenge61 implements Challenge { diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java index e8f0da40e..b7bba84c2 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java @@ -2,6 +2,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; +import java.time.Duration; import java.util.Map; import org.bouncycastle.util.encoders.Base64; import org.slf4j.Logger; @@ -9,19 +10,19 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.ResponseEntity; +import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; /** - * Optional webhook controller for Challenge61. Enable by setting - * challenge61.webhook.enabled=true and challenge61.webhook.token= This is a - * better approach for production than polling with getUpdates. + * Optional webhook controller for Challenge61. Enable by setting challenge61.webhook.enabled=true + * and challenge61.webhook.token= This is a better approach for production than + * polling with getUpdates. * *

    To use: 1. Set environment variables: - CHALLENGE61_WEBHOOK_ENABLED=true - - * CHALLENGE61_WEBHOOK_TOKEN= 2. Set webhook URL with Telegram: - * curl -X POST + * CHALLENGE61_WEBHOOK_TOKEN= 2. Set webhook URL with Telegram: curl -X POST * "https://api.telegram.org/bot/setWebhook?url=https://.herokuapp.com/telegram/webhook/challenge61&secret_token=" */ @RestController @@ -32,10 +33,11 @@ public class TelegramWebhookController { private final RestTemplate restTemplate; private final String webhookToken; - public TelegramWebhookController( - RestTemplate restTemplate, - @Value("${challenge61.webhook.token:}") String webhookToken) { - this.restTemplate = restTemplate; + public TelegramWebhookController(@Value("${challenge61.webhook.token:}") String webhookToken) { + var requestFactory = new SimpleClientHttpRequestFactory(); + requestFactory.setConnectTimeout(Duration.ofSeconds(5)); + requestFactory.setReadTimeout(Duration.ofSeconds(5)); + this.restTemplate = new RestTemplate(requestFactory); this.webhookToken = webhookToken; logger.info("Challenge61 Telegram webhook controller enabled"); } diff --git a/static-site/pr-2125/pages/about.html b/static-site/pr-2125/pages/about.html index fe9a9ed96..934d4d34c 100644 --- a/static-site/pr-2125/pages/about.html +++ b/static-site/pr-2125/pages/about.html @@ -80,7 +80,7 @@

    🎯 Learning Objectives
  • (The MIT License (MIT)) Spring Cloud Azure Starter Key Vault Secrets (com.azure.spring:spring-cloud-azure-starter-keyvault-secrets:5.22.0 - https://microsoft.github.io/spring-cloud-azure)
  • (The Apache Software License, Version 2.0) Simple XML (safe) (com.carrotsearch.thirdparty:simple-xml-safe:2.7.1 - https://github.com/dweiss/simplexml)
  • (3-Clause BSD License) MinLog (com.esotericsoftware:minlog:1.3.1 - https://github.com/EsotericSoftware/minlog)
  • -
  • (Apache License, Version 2.0) Internet Time Utility (com.ethlo.time:itu:1.13.1-alpha8 - https://github.com/ethlo/itu)
  • +
  • (Apache License, Version 2.0) Internet Time Utility (com.ethlo.time:itu:1.13.1-alpha10 - https://github.com/ethlo/itu)
  • (The Apache Software License, Version 2.0) aalto-xml (com.fasterxml:aalto-xml:1.3.3 - https://github.com/FasterXML/aalto-xml)
  • (Apache License, Version 2.0) ClassMate (com.fasterxml:classmate:1.7.0 - https://github.com/FasterXML/java-classmate)
  • (The Apache Software License, Version 2.0) Jackson-annotations (com.fasterxml.jackson.core:jackson-annotations:2.19.1 - https://github.com/FasterXML/jackson)
  • From 8543cda8fc566a5d9df9b060fdb408262940a826 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Fri, 6 Mar 2026 22:41:13 +0100 Subject: [PATCH 05/13] Fix Heroku deploy --- Dockerfile.web | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.web b/Dockerfile.web index eee55031f..fe35a4ab7 100644 --- a/Dockerfile.web +++ b/Dockerfile.web @@ -1,5 +1,5 @@ -FROM jeroenwillemsen/wrongsecrets:1.13.1-alpha10-no-vault -ARG argBasedVersion="1.13.1-alpha10-no-vault" +FROM jeroenwillemsen/wrongsecrets:1.13.1-alpha11-no-vault +ARG argBasedVersion="1.13.1-alpha11-no-vault" ARG spring_profile="without-vault" ARG CANARY_URLS="http://canarytokens.com/terms/about/s7cfbdakys13246ewd8ivuvku/post.jsp,http://canarytokens.com/terms/about/y0all60b627gzp19ahqh7rl6j/post.jsp" ARG CTF_ENABLED=false From 7dbd2ba557d17686c5435dd9882c6504b3a7b40b Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Fri, 6 Mar 2026 22:52:34 +0100 Subject: [PATCH 06/13] Fix Heroku deploy --- Dockerfile.web | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile.web b/Dockerfile.web index fe35a4ab7..d500fa204 100644 --- a/Dockerfile.web +++ b/Dockerfile.web @@ -39,8 +39,9 @@ ENV default_aws_value_challenge_11=$CHALLENGE_11_VALUE ENV BASTIONHOSTPATH="/home/wrongsecrets/.ssh" ENV PROJECTSPECPATH="/var/helpers/project-specification.mdc" ENV funnybunny="This is a funny bunny" -# Keep memory usage within Heroku dyno limits. -ENV JAVA_TOOL_OPTIONS="-XX:MaxRAMPercentage=60 -XX:InitialRAMPercentage=25 -XX:MaxMetaspaceSize=128m -XX:+UseSerialGC -XX:+ExitOnOutOfMemoryError" +# Keep memory usage within Heroku dyno limits (512MB dyno). +# Hard cap heap to 250M, metaspace to 60M, disable expensive GC, exit on OOM immediately. +ENV JAVA_TOOL_OPTIONS="-Xmx250M -Xms128M -XX:MetaspaceSize=40M -XX:MaxMetaspaceSize=60M -XX:CompressedClassSpaceSize=32M -XX:+UseG1GC -XX:MaxGCPauseMillis=50 -XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof" # Deploy WrongSecrets to Heroku COPY .github/scripts/ /var/helpers COPY src/test/resources/alibabacreds.kdbx /var/helpers From 3176323e617b5d3f63633b678bb77833bd065072 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Fri, 6 Mar 2026 22:58:46 +0100 Subject: [PATCH 07/13] Fixing arcane deployments --- .github/scripts/.bash_history | 2 +- Dockerfile | 2 +- Procfile | 1 + aws/k8s/secret-challenge-vault-deployment.yml | 2 +- azure/k8s/secret-challenge-vault-deployment.yml.tpl | 2 +- docs/VERSION_MANAGEMENT.md | 6 +++--- fly.toml | 2 +- gcp/k8s/secret-challenge-vault-deployment.yml.tpl | 2 +- js/index.js | 2 +- k8s/challenge53/secret-challenge53-sidecar.yml | 4 ++-- k8s/challenge53/secret-challenge53.yml | 2 +- k8s/secret-challenge-deployment.yml | 2 +- k8s/secret-challenge-vault-deployment.yml | 2 +- okteto/k8s/secret-challenge-ctf-deployment.yml | 2 +- okteto/k8s/secret-challenge-deployment.yml | 2 +- src/main/resources/application.properties | 3 ++- static-site/pr-2125/pages/about.html | 2 +- 17 files changed, 21 insertions(+), 19 deletions(-) create mode 100644 Procfile diff --git a/.github/scripts/.bash_history b/.github/scripts/.bash_history index 8073bee8a..ceba5fd33 100644 --- a/.github/scripts/.bash_history +++ b/.github/scripts/.bash_history @@ -347,7 +347,7 @@ rm -rf jdk-18_linux-x64_bin.deb git rebase -i main git rebase -i master git stash -export tempPassword="et52N9ftBA73WIJ5MvaEpMWBe251RsC7eM+mxAat9n8=" +export tempPassword="8S2PzZ7da3Jx9geda6JOqqfYlSDYzM7QbpUGyxM9umw=" mvn run tempPassword k6 npx k6 diff --git a/Dockerfile b/Dockerfile index e1a466090..2f65c0f14 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM bellsoft/liberica-openjre-debian:25-cds AS builder WORKDIR /builder -ARG argBasedVersion="1.13.1-alpha10" +ARG argBasedVersion="1.13.1-alpha11" COPY --chown=wrongsecrets target/wrongsecrets-${argBasedVersion}-SNAPSHOT.jar application.jar RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted diff --git a/Procfile b/Procfile new file mode 100644 index 000000000..b8a0b667c --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: java -Xmx200M -Xms100M -XX:MetaspaceSize=30M -XX:MaxMetaspaceSize=50M -XX:CompressedClassSpaceSize=24M -XX:+UseG1GC -XX:MaxGCPauseMillis=50 -XX:+ExitOnOutOfMemoryError -Dspring.profiles.active=${SPRING_PROFILES_ACTIVE} -Dserver.port=${PORT} -Dspringdoc.swagger-ui.enabled=${SPRINGDOC_UI} -Dspringdoc.api-docs.enabled=${SPRINGDOC_DOC} -jar target/application.jar diff --git a/aws/k8s/secret-challenge-vault-deployment.yml b/aws/k8s/secret-challenge-vault-deployment.yml index 8f405eb2e..810a174a2 100644 --- a/aws/k8s/secret-challenge-vault-deployment.yml +++ b/aws/k8s/secret-challenge-vault-deployment.yml @@ -58,7 +58,7 @@ spec: volumeAttributes: secretProviderClass: "wrongsecrets-aws-secretsmanager" containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha10-k8s-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha11-k8s-vault imagePullPolicy: IfNotPresent name: secret-challenge command: ["/bin/sh"] diff --git a/azure/k8s/secret-challenge-vault-deployment.yml.tpl b/azure/k8s/secret-challenge-vault-deployment.yml.tpl index af121bf04..0e9e1f7f4 100644 --- a/azure/k8s/secret-challenge-vault-deployment.yml.tpl +++ b/azure/k8s/secret-challenge-vault-deployment.yml.tpl @@ -61,7 +61,7 @@ spec: volumeAttributes: secretProviderClass: "azure-wrongsecrets-vault" containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha10-k8s-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha11-k8s-vault imagePullPolicy: IfNotPresent name: secret-challenge command: ["/bin/sh"] diff --git a/docs/VERSION_MANAGEMENT.md b/docs/VERSION_MANAGEMENT.md index 699d86473..35bd66306 100644 --- a/docs/VERSION_MANAGEMENT.md +++ b/docs/VERSION_MANAGEMENT.md @@ -12,9 +12,9 @@ The project maintains version consistency between: ## Version Schema ``` -pom.xml version: 1.13.1-alpha10-SNAPSHOT -Dockerfile version: 1.13.1-alpha10 -Dockerfile.web version: 1.13.1-alpha10-no-vault +pom.xml version: 1.13.1-alpha11-SNAPSHOT +Dockerfile version: 1.13.1-alpha11 +Dockerfile.web version: 1.13.1-alpha11-no-vault ``` ## Automated Solutions diff --git a/fly.toml b/fly.toml index 5886a6171..1a127058c 100644 --- a/fly.toml +++ b/fly.toml @@ -8,7 +8,7 @@ app = "wrongsecrets" primary_region = "ams" [build] - image = "docker.io/jeroenwillemsen/wrongsecrets:1.13.1-alpha10-no-vault" + image = "docker.io/jeroenwillemsen/wrongsecrets:1.13.1-alpha11-no-vault" [env] K8S_ENV = "Fly(Docker)" diff --git a/gcp/k8s/secret-challenge-vault-deployment.yml.tpl b/gcp/k8s/secret-challenge-vault-deployment.yml.tpl index 600e38c5f..ea7052446 100644 --- a/gcp/k8s/secret-challenge-vault-deployment.yml.tpl +++ b/gcp/k8s/secret-challenge-vault-deployment.yml.tpl @@ -58,7 +58,7 @@ spec: volumeAttributes: secretProviderClass: "wrongsecrets-gcp-secretsmanager" containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha10-k8s-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha11-k8s-vault imagePullPolicy: IfNotPresent name: secret-challenge command: ["/bin/sh"] diff --git a/js/index.js b/js/index.js index 3e2bd1186..d2f3b8e80 100644 --- a/js/index.js +++ b/js/index.js @@ -1,5 +1,5 @@ function secret() { - var password = "GvbyCk4=" + 9 + "Ltum" + 6 + "CUw=" + 2 + "h8qT" + 7; + var password = "UIz8ASo=" + 9 + "vCx1" + 6 + "DXw=" + 2 + "XaN4" + 7; return password; } diff --git a/k8s/challenge53/secret-challenge53-sidecar.yml b/k8s/challenge53/secret-challenge53-sidecar.yml index d027fac42..14180fc8b 100644 --- a/k8s/challenge53/secret-challenge53-sidecar.yml +++ b/k8s/challenge53/secret-challenge53-sidecar.yml @@ -21,7 +21,7 @@ spec: runAsGroup: 2000 fsGroup: 2000 containers: - - image: jeroenwillemsen/wrongsecrets-challenge53:1.13.1-alpha10 + - image: jeroenwillemsen/wrongsecrets-challenge53:1.13.1-alpha11 name: secret-challenge-53 imagePullPolicy: IfNotPresent resources: @@ -45,7 +45,7 @@ spec: command: ["/bin/sh", "-c"] args: - cp /home/wrongsecrets/* /shared-data/ && exec /home/wrongsecrets/start-on-arch.sh - - image: jeroenwillemsen/wrongsecrets-challenge53-debug:1.13.1-alpha10 + - image: jeroenwillemsen/wrongsecrets-challenge53-debug:1.13.1-alpha11 name: sidecar imagePullPolicy: IfNotPresent command: ["/bin/sh", "-c", "while true; do ls /shared-data; sleep 10; done"] diff --git a/k8s/challenge53/secret-challenge53.yml b/k8s/challenge53/secret-challenge53.yml index af8d1732a..e3e581a9f 100644 --- a/k8s/challenge53/secret-challenge53.yml +++ b/k8s/challenge53/secret-challenge53.yml @@ -21,7 +21,7 @@ spec: runAsGroup: 2000 fsGroup: 2000 containers: - - image: jeroenwillemsen/wrongsecrets-challenge53:1.13.1-alpha10 + - image: jeroenwillemsen/wrongsecrets-challenge53:1.13.1-alpha11 name: secret-challenge-53 imagePullPolicy: IfNotPresent resources: diff --git a/k8s/secret-challenge-deployment.yml b/k8s/secret-challenge-deployment.yml index 957fc3a9d..06c7c0c17 100644 --- a/k8s/secret-challenge-deployment.yml +++ b/k8s/secret-challenge-deployment.yml @@ -28,7 +28,7 @@ spec: runAsGroup: 2000 fsGroup: 2000 containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha10-no-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha11-no-vault imagePullPolicy: IfNotPresent name: secret-challenge ports: diff --git a/k8s/secret-challenge-vault-deployment.yml b/k8s/secret-challenge-vault-deployment.yml index 92156a42e..0b4635637 100644 --- a/k8s/secret-challenge-vault-deployment.yml +++ b/k8s/secret-challenge-vault-deployment.yml @@ -50,7 +50,7 @@ spec: type: RuntimeDefault serviceAccountName: vault containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha10-k8s-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha11-k8s-vault imagePullPolicy: IfNotPresent name: secret-challenge command: ["/bin/sh"] diff --git a/okteto/k8s/secret-challenge-ctf-deployment.yml b/okteto/k8s/secret-challenge-ctf-deployment.yml index 22ddb67eb..a4d888f1a 100644 --- a/okteto/k8s/secret-challenge-ctf-deployment.yml +++ b/okteto/k8s/secret-challenge-ctf-deployment.yml @@ -28,7 +28,7 @@ spec: runAsGroup: 2000 fsGroup: 2000 containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha10-no-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha11-no-vault name: secret-challenge-ctf imagePullPolicy: IfNotPresent securityContext: diff --git a/okteto/k8s/secret-challenge-deployment.yml b/okteto/k8s/secret-challenge-deployment.yml index 1552539a5..dc703cf3f 100644 --- a/okteto/k8s/secret-challenge-deployment.yml +++ b/okteto/k8s/secret-challenge-deployment.yml @@ -28,7 +28,7 @@ spec: runAsGroup: 2000 fsGroup: 2000 containers: - - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha10-no-vault + - image: jeroenwillemsen/wrongsecrets:1.13.1-alpha11-no-vault name: secret-challenge imagePullPolicy: IfNotPresent securityContext: diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 9298c94dc..7f17a2839 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -5,7 +5,8 @@ spring.web.resources.cache.period=PT2H server.compression.enabled=true spring.config.import=classpath:/wrong-secrets-configuration.yaml -password=ThisEnvironmentIsAnotherPlaceToHide +# Challenge61: Disable webhook by default (memory intensive on Heroku). Enable in profile if needed. +challenge61.webhook.enabled=false SPECIAL_K8S_SECRET=if_you_see_this_please_use_k8s SPECIAL_SPECIAL_K8S_SECRET=if_you_see_this_please_use_k8s SEALED_SECRET_ANSWER=if_you_see_this_please_use_k8s diff --git a/static-site/pr-2125/pages/about.html b/static-site/pr-2125/pages/about.html index 934d4d34c..7014bc81f 100644 --- a/static-site/pr-2125/pages/about.html +++ b/static-site/pr-2125/pages/about.html @@ -80,7 +80,7 @@
    🎯 Learning Objectives
  • (The MIT License (MIT)) Spring Cloud Azure Starter Key Vault Secrets (com.azure.spring:spring-cloud-azure-starter-keyvault-secrets:5.22.0 - https://microsoft.github.io/spring-cloud-azure)
  • (The Apache Software License, Version 2.0) Simple XML (safe) (com.carrotsearch.thirdparty:simple-xml-safe:2.7.1 - https://github.com/dweiss/simplexml)
  • (3-Clause BSD License) MinLog (com.esotericsoftware:minlog:1.3.1 - https://github.com/EsotericSoftware/minlog)
  • -
  • (Apache License, Version 2.0) Internet Time Utility (com.ethlo.time:itu:1.13.1-alpha10 - https://github.com/ethlo/itu)
  • +
  • (Apache License, Version 2.0) Internet Time Utility (com.ethlo.time:itu:1.13.1-alpha11 - https://github.com/ethlo/itu)
  • (The Apache Software License, Version 2.0) aalto-xml (com.fasterxml:aalto-xml:1.3.3 - https://github.com/FasterXML/aalto-xml)
  • (Apache License, Version 2.0) ClassMate (com.fasterxml:classmate:1.7.0 - https://github.com/FasterXML/java-classmate)
  • (The Apache Software License, Version 2.0) Jackson-annotations (com.fasterxml.jackson.core:jackson-annotations:2.19.1 - https://github.com/FasterXML/jackson)
  • From 8328577781d97eb280ccae9222094e0a4d7b3b42 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 04:06:01 +0000 Subject: [PATCH 08/13] Initial plan From d5fb3d83c7a0770b2a24ca33947b7e2f8faf1504 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 04:12:08 +0000 Subject: [PATCH 09/13] Fix CI failures: restore password property, fix CRLF injection, fix Dockerfile versions Co-authored-by: commjoen <1457214+commjoen@users.noreply.github.com> --- Dockerfile | 2 +- Dockerfile.web | 4 ++-- .../docker/challenge61/TelegramWebhookController.java | 11 +++++++++-- src/main/resources/application.properties | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2f65c0f14..da5db174e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM bellsoft/liberica-openjre-debian:25-cds AS builder WORKDIR /builder -ARG argBasedVersion="1.13.1-alpha11" +ARG argBasedVersion="1.13.1-alpha6" COPY --chown=wrongsecrets target/wrongsecrets-${argBasedVersion}-SNAPSHOT.jar application.jar RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted diff --git a/Dockerfile.web b/Dockerfile.web index d500fa204..ab9a85148 100644 --- a/Dockerfile.web +++ b/Dockerfile.web @@ -1,5 +1,5 @@ -FROM jeroenwillemsen/wrongsecrets:1.13.1-alpha11-no-vault -ARG argBasedVersion="1.13.1-alpha11-no-vault" +FROM jeroenwillemsen/wrongsecrets:1.13.1-alpha6-no-vault +ARG argBasedVersion="1.13.1-alpha6-no-vault" ARG spring_profile="without-vault" ARG CANARY_URLS="http://canarytokens.com/terms/about/s7cfbdakys13246ewd8ivuvku/post.jsp,http://canarytokens.com/terms/about/y0all60b627gzp19ahqh7rl6j/post.jsp" ARG CTF_ENABLED=false diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java index b7bba84c2..fd28cd155 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java @@ -57,7 +57,7 @@ public ResponseEntity handleWebhook( } try { - logger.info("Received webhook update: {}", update.get("update_id")); + logger.info("Received webhook update: {}", sanitizeForLog(String.valueOf(update.get("update_id")))); // Check if this is a message update if (update.containsKey("message")) { @@ -104,7 +104,7 @@ private void sendSecretMessage(Object chatId) { Map response = restTemplate.getForObject(sendMessageUrl, Map.class); if (response != null && Boolean.TRUE.equals(response.get("ok"))) { - logger.info("Successfully sent secret message to chat_id: {}", chatId); + logger.info("Successfully sent secret message to chat_id: {}", sanitizeForLog(String.valueOf(chatId))); } else { logger.warn("Failed to send message to Telegram"); } @@ -114,6 +114,13 @@ private void sendSecretMessage(Object chatId) { } } + private String sanitizeForLog(String value) { + if (value == null) { + return "null"; + } + return value.replaceAll("[\r\n]", "_"); + } + private String getBotToken() { // Same double-encoded bot token as in Challenge61 String encodedToken = diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 7f17a2839..4962e40c0 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -5,7 +5,7 @@ spring.web.resources.cache.period=PT2H server.compression.enabled=true spring.config.import=classpath:/wrong-secrets-configuration.yaml -# Challenge61: Disable webhook by default (memory intensive on Heroku). Enable in profile if needed. +password=ThisEnvironmentIsAnotherPlaceToHide challenge61.webhook.enabled=false SPECIAL_K8S_SECRET=if_you_see_this_please_use_k8s SPECIAL_SPECIAL_K8S_SECRET=if_you_see_this_please_use_k8s From ede317f7e482365c0940bc0b1509518a7990baad Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Sat, 7 Mar 2026 04:17:48 +0000 Subject: [PATCH 10/13] [pre-commit.ci lite] apply automatic fixes --- .../docker/challenge61/TelegramWebhookController.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java index fd28cd155..0dc972d91 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java @@ -57,7 +57,8 @@ public ResponseEntity handleWebhook( } try { - logger.info("Received webhook update: {}", sanitizeForLog(String.valueOf(update.get("update_id")))); + logger.info( + "Received webhook update: {}", sanitizeForLog(String.valueOf(update.get("update_id")))); // Check if this is a message update if (update.containsKey("message")) { @@ -104,7 +105,9 @@ private void sendSecretMessage(Object chatId) { Map response = restTemplate.getForObject(sendMessageUrl, Map.class); if (response != null && Boolean.TRUE.equals(response.get("ok"))) { - logger.info("Successfully sent secret message to chat_id: {}", sanitizeForLog(String.valueOf(chatId))); + logger.info( + "Successfully sent secret message to chat_id: {}", + sanitizeForLog(String.valueOf(chatId))); } else { logger.warn("Failed to send message to Telegram"); } From 2201590be6d6d8701db1f362e0fd18c0189c40ba Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Sat, 7 Mar 2026 05:17:56 +0100 Subject: [PATCH 11/13] fix versions --- Dockerfile | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index da5db174e..2f65c0f14 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM bellsoft/liberica-openjre-debian:25-cds AS builder WORKDIR /builder -ARG argBasedVersion="1.13.1-alpha6" +ARG argBasedVersion="1.13.1-alpha11" COPY --chown=wrongsecrets target/wrongsecrets-${argBasedVersion}-SNAPSHOT.jar application.jar RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted diff --git a/pom.xml b/pom.xml index 146ebea2f..d0fec6eba 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.owasp wrongsecrets - 1.13.1-alpha6-SNAPSHOT + 1.13.1-alpha11-SNAPSHOT OWASP WrongSecrets Examples with how to not use secrets From c415a4d203acd3a0f8c723900645ea675ca00488 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Sat, 7 Mar 2026 05:25:43 +0100 Subject: [PATCH 12/13] fix missing versions --- Dockerfile.web | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.web b/Dockerfile.web index ab9a85148..d500fa204 100644 --- a/Dockerfile.web +++ b/Dockerfile.web @@ -1,5 +1,5 @@ -FROM jeroenwillemsen/wrongsecrets:1.13.1-alpha6-no-vault -ARG argBasedVersion="1.13.1-alpha6-no-vault" +FROM jeroenwillemsen/wrongsecrets:1.13.1-alpha11-no-vault +ARG argBasedVersion="1.13.1-alpha11-no-vault" ARG spring_profile="without-vault" ARG CANARY_URLS="http://canarytokens.com/terms/about/s7cfbdakys13246ewd8ivuvku/post.jsp,http://canarytokens.com/terms/about/y0all60b627gzp19ahqh7rl6j/post.jsp" ARG CTF_ENABLED=false From 9aab93d5d62a2bb4cff4deb3137f732bf0f13061 Mon Sep 17 00:00:00 2001 From: Jeroen Willemsen Date: Sat, 7 Mar 2026 05:49:24 +0100 Subject: [PATCH 13/13] update javadoc --- docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md | 55 ++++++++++++-- .../challenges/docker/Challenge61.java | 71 +++---------------- .../TelegramWebhookController.java | 11 ++- 3 files changed, 65 insertions(+), 72 deletions(-) diff --git a/docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md b/docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md index 9376723e1..44369d466 100644 --- a/docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md +++ b/docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md @@ -1,17 +1,25 @@ # Challenge61 Multi-Instance Setup Guide -This guide explains how to run Challenge61 across multiple Heroku apps (Arcane and WrongSecrets). +This guide explains how to configure and run Challenge61, which demonstrates how hardcoded Telegram bot credentials can be discovered and exploited. The bot token is double-encoded in base64 to make it slightly more challenging but still discoverable through code inspection. -## Current Solution: Improved getUpdates with Offsets +## Overview -The code now uses update offsets to minimize conflicts between multiple app instances: +This challenge supports running on multiple app instances (e.g., Arcane and WrongSecrets Heroku apps) using either polling (getUpdates) or webhooks. + +## Option 1: Polling with getUpdates (Default - Works Out of Box) + +The code uses update offsets to minimize conflicts between multiple app instances: +- No configuration needed +- Uses update offsets to minimize conflicts between instances +- Multiple instances can run simultaneously +- Less efficient but simpler setup - `timeout=0` - No long polling, quick responses - `limit=1` - Process one update at a time - Offset acknowledgment - Marks updates as processed **Status**: ✅ Code updated and tested -## Webhook Solution (Recommended for Production) +## Option 2: Webhook Solution (Recommended for Production) ### Step 1: Configure Each Heroku App @@ -92,3 +100,42 @@ heroku logs --tail -a arcane-app | grep Challenge61 For **production with multiple apps**: Use webhook on ONE primary app (WrongSecrets). For **development/testing**: The current getUpdates approach with offsets works fine. + +## BotFather Configuration (Optional but Recommended) + +### 1. Configure Commands + +- Send `/setcommands` to @BotFather +- Select your bot +- Add: `start - Get the secret message` + +### 2. Set Description + +- Send `/setdescription` to @BotFather +- Select your bot +- Add: "OWASP WrongSecrets Challenge 61 - Demonstrates hardcoded bot credentials. Send /start to receive the secret!" + +### 3. Set About Text + +- Send `/setabouttext` to @BotFather +- Add: "Educational security challenge from OWASP WrongSecrets project" + +## Testing the Bot + +1. Find the bot: Search for @WrongsecretsBot in Telegram (or your bot username) +2. Send: `/start` +3. Receive: "Welcome! Your secret is: telegram_secret_found_in_channel" + +## Creating a New Bot + +If you need to create your own bot for testing: + +1. Message @BotFather in Telegram +2. Send `/newbot` +3. Follow prompts to choose name and username +4. BotFather will provide a token like: `1234567890:ABCdefGHIjklMNOpqrsTUVwxyz` +5. Double-encode the token for use in this challenge: + ```bash + echo -n "YOUR_TOKEN" | base64 | base64 + ``` +6. Replace the `encodedToken` value in the `getBotToken()` method in Challenge61.java diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge61.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge61.java index 63ad2b908..803f855e5 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge61.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge61.java @@ -15,71 +15,18 @@ import org.springframework.web.client.RestTemplate; /** - * This challenge is about finding a secret in a Telegram channel/bot. + * This challenge demonstrates how hardcoded Telegram bot credentials can be discovered and + * exploited. * - *

    The challenge demonstrates how hardcoded Telegram bot credentials can be discovered and - * exploited. The bot token is double-encoded in base64 to make it slightly more challenging but - * still discoverable through code inspection. + *

    The bot token is double-encoded in base64 to make it slightly more challenging but still + * discoverable through code inspection. * - *

    Multi-Instance Setup (Heroku/Cloud Deployments): + *

    This challenge supports running on multiple app instances using either polling (getUpdates) or + * webhooks. For detailed setup instructions including BotFather configuration, webhook setup, and + * creating a new bot, see: {@code docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md} * - *

    This challenge supports running on multiple app instances (e.g., Arcane and WrongSecrets - * Heroku apps) using either polling (getUpdates) or webhooks: - * - *

    Option 1: Polling with getUpdates (Default - Works Out of Box)
    - * - No configuration needed
    - * - Uses update offsets to minimize conflicts between instances
    - * - Multiple instances can run simultaneously
    - * - Less efficient but simpler setup - * - *

    Option 2: Webhooks (Recommended for Production)
    - * 1. Enable webhook mode by setting environment variables:
    - * {@code heroku config:set CHALLENGE61_WEBHOOK_ENABLED=true -a your-app}
    - * {@code heroku config:set CHALLENGE61_WEBHOOK_TOKEN=$(openssl rand -hex 32) -a your-app} - * - *

    2. Set the webhook URL with Telegram (choose ONE primary app):
    - * {@code curl -X POST - * "https://api.telegram.org/bot/setWebhook?url=https://your-app.herokuapp.com/telegram/webhook/challenge61&secret_token="} - * - *

    3. Verify webhook is active:
    - * {@code curl "https://api.telegram.org/bot/getWebhookInfo"} - * - *

    4. To disable webhook and return to polling:
    - * {@code curl -X POST "https://api.telegram.org/bot/deleteWebhook"} - * - *

    BotFather Configuration (Optional but Recommended): - * - *

    1. Configure commands:
    - * - Send {@code /setcommands} to @BotFather
    - * - Select your bot
    - * - Add: {@code start - Get the secret message} - * - *

    2. Set description:
    - * - Send {@code /setdescription} to @BotFather
    - * - Select your bot
    - * - Add: "OWASP WrongSecrets Challenge 61 - Demonstrates hardcoded bot credentials. Send /start to - * receive the secret!" - * - *

    3. Set about text:
    - * - Send {@code /setabouttext} to @BotFather
    - * - Add: "Educational security challenge from OWASP WrongSecrets project" - * - *

    Testing the Bot:
    - * 1. Find the bot: Search for @WrongsecretsBot in Telegram (or your bot username)
    - * 2. Send: {@code /start}
    - * 3. Receive: "Welcome! Your secret is: telegram_secret_found_in_channel" - * - *

    Creating a New Bot:
    - * If you need to create your own bot for testing:
    - * 1. Message @BotFather in Telegram
    - * 2. Send {@code /newbot}
    - * 3. Follow prompts to choose name and username
    - * 4. BotFather will provide a token like: {@code 1234567890:ABCdefGHIjklMNOpqrsTUVwxyz}
    - * 5. Double-encode the token for use in this challenge:
    - * {@code echo -n "YOUR_TOKEN" | base64 | base64}
    - * 6. Replace the {@code encodedToken} value in the {@code getBotToken()} method - * - *

    See also: docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md for detailed setup instructions + *

    Quick Start: Search for @WrongsecretsBot in Telegram and send {@code /start} to receive + * the secret. */ @Component public class Challenge61 implements Challenge { diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java index 0dc972d91..d333f70c1 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/challenge61/TelegramWebhookController.java @@ -17,13 +17,12 @@ import org.springframework.web.client.RestTemplate; /** - * Optional webhook controller for Challenge61. Enable by setting challenge61.webhook.enabled=true - * and challenge61.webhook.token= This is a better approach for production than - * polling with getUpdates. + * Optional webhook controller for Challenge61. Enable by setting {@code + * challenge61.webhook.enabled=true} and {@code challenge61.webhook.token} properties. * - *

    To use: 1. Set environment variables: - CHALLENGE61_WEBHOOK_ENABLED=true - - * CHALLENGE61_WEBHOOK_TOKEN= 2. Set webhook URL with Telegram: curl -X POST - * "https://api.telegram.org/bot/setWebhook?url=https://.herokuapp.com/telegram/webhook/challenge61&secret_token=" + *

    Webhooks are recommended for production deployments to replace polling with getUpdates. For + * detailed setup instructions including environment variables and webhook configuration, see: + * {@code docs/CHALLENGE61_MULTI_INSTANCE_SETUP.md} */ @RestController @ConditionalOnProperty(name = "challenge61.webhook.enabled", havingValue = "true")