diff --git a/.github/scripts/docker-create.sh b/.github/scripts/docker-create.sh index 3ea416ec9..afe2ee529 100755 --- a/.github/scripts/docker-create.sh +++ b/.github/scripts/docker-create.sh @@ -354,12 +354,51 @@ build_update_pom() { cd ../.. && ./mvnw clean && ./mvnw --batch-mode release:update-versions -DdevelopmentVersion=${tag}-SNAPSHOT && ./mvnw spotless:apply && ./mvnw install -DskipTests cd .github/scripts echo "Removing unnecessary binaries from the jar file" + # macOS / non-Linux binaries (never used in the Alpine Docker container) zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-golang zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-golang-arm + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-c + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-c-arm + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-advanced-c + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-advanced-c-arm + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-advanced-c-arm-stripped + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-advanced-c-stripped + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-cplus + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-cplus-arm + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-challenge52-c + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-challenge52-c-arm + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-challenge53-c + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-challenge53-c-arm + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-rust + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-rust-arm zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-dotnet zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-dotnet-arm + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-swift + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-swift-arm + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-swift-ctf + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-swift-arm-ctf + # Linux glibc (non-musl) binaries (Alpine uses musl; golang uses glibc linux binary intentionally) + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-c-linux + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-c-linux-arm + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-advanced-c-linux + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-advanced-c-linux-arm + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-advanced-c-linux-arm-stripped + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-advanced-c-linux-stripped + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-cplus-linux + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-cplus-linux-arm + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-challenge52-c-linux + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-challenge52-c-linux-arm + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-challenge53-c-linux + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-challenge53-c-linux-arm + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-rust-linux + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-rust-linux-arm zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-dotnet-linux zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-dotnet-linux-arm + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-swift-linux + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-swift-linux-arm + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-swift-linux-ctf + zip -d ../../target/*.jar BOOT-INF/classes/executables/wrongsecrets-swift-linux-arm-ctf + # Windows binaries zip -d ../../target/*.jar BOOT-INF/classes/executables/*.exe docker buildx create --name mybuilder docker buildx use mybuilder diff --git a/.github/workflows/container_test.yml b/.github/workflows/container_test.yml index f269a3a27..723130226 100644 --- a/.github/workflows/container_test.yml +++ b/.github/workflows/container_test.yml @@ -39,3 +39,10 @@ jobs: - uses: actions/checkout@v5 - name: Run compose and print out service run: export DOCKER_BUILDKIT=1 && cd src/main/resources/challenges/challenge-51 && docker compose -f challenge51docker-compose.yml build && docker compose -f challenge51docker-compose.yml run myservice + challenge_63_swift_test: + name: Challenge 63 Swift binary test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Build and run Swift binary in Alpine container + run: export DOCKER_BUILDKIT=1 && docker build -f src/main/resources/challenges/challenge-63/Dockerfile_challenge63 -t challenge63-test . && docker run --rm challenge63-test diff --git a/Dockerfile b/Dockerfile index 30384b83b..1de6587cd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,6 +45,7 @@ COPY --chown=wrongsecrets .github/scripts/ /var/tmp/helpers COPY --chown=wrongsecrets .github/scripts/.bash_history /home/wrongsecrets/ COPY --chown=wrongsecrets src/main/resources/executables/wrongsecrets*linux-musl* /home/wrongsecrets/ COPY --chown=wrongsecrets src/main/resources/executables/wrongsecrets-golang-linux /home/wrongsecrets/ +COPY --chown=wrongsecrets src/main/resources/executables/wrongsecrets-golang-linux-arm /home/wrongsecrets/ COPY --chown=wrongsecrets src/test/resources/alibabacreds.kdbx /var/tmp/helpers COPY --chown=wrongsecrets src/test/resources/RSAprivatekey.pem /var/tmp/helpers/ COPY --chown=wrongsecrets .ssh/ /home/wrongsecrets/.ssh/ diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge63.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge63.java new file mode 100644 index 000000000..1f6257723 --- /dev/null +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge63.java @@ -0,0 +1,20 @@ +package org.owasp.wrongsecrets.challenges.docker; + +import lombok.extern.slf4j.Slf4j; +import org.owasp.wrongsecrets.challenges.FixedAnswerChallenge; +import org.owasp.wrongsecrets.challenges.docker.binaryexecution.BinaryExecutionHelper; +import org.owasp.wrongsecrets.challenges.docker.binaryexecution.MuslDetectorImpl; +import org.springframework.stereotype.Component; + +/** This challenge is about finding a secret hardcoded in a Swift binary. */ +@Slf4j +@Component +public class Challenge63 extends FixedAnswerChallenge { + + @Override + public String getAnswer() { + BinaryExecutionHelper binaryExecutionHelper = + new BinaryExecutionHelper(63, new MuslDetectorImpl()); + return binaryExecutionHelper.executeCommand("", "wrongsecrets-swift"); + } +} diff --git a/src/main/java/org/owasp/wrongsecrets/challenges/docker/binaryexecution/BinaryExecutionHelper.java b/src/main/java/org/owasp/wrongsecrets/challenges/docker/binaryexecution/BinaryExecutionHelper.java index fa7ccaa88..9a6b80294 100644 --- a/src/main/java/org/owasp/wrongsecrets/challenges/docker/binaryexecution/BinaryExecutionHelper.java +++ b/src/main/java/org/owasp/wrongsecrets/challenges/docker/binaryexecution/BinaryExecutionHelper.java @@ -8,6 +8,8 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.*; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; @@ -22,6 +24,10 @@ private enum BinaryInstructionForFile { Guess } + private static final String[] SWIFT_LIB_PATHS = { + "/usr/share/swift/usr/lib/swift/linux", "/usr/lib/swift/linux", "/usr/local/lib/swift/linux" + }; + public static final String ERROR_EXECUTION = EXECUTION_ERROR; private final int challengeNumber; @@ -128,6 +134,9 @@ private String executeCommand( } } ps.redirectErrorStream(true); + if (execFile.getPath().contains("swift")) { + configureSwiftLibraryPath(ps); + } Process pr = ps.start(); try (BufferedReader in = new BufferedReader(new InputStreamReader(pr.getInputStream(), StandardCharsets.UTF_8))) { @@ -272,4 +281,24 @@ private void deleteFile(File execFile) { log.info("Deleting the file {} failed...", execFile.getPath()); } } + + private void configureSwiftLibraryPath(ProcessBuilder ps) { + List existingPaths = new ArrayList<>(); + String currentLdPath = ps.environment().get("LD_LIBRARY_PATH"); + if (!Strings.isNullOrEmpty(currentLdPath)) { + existingPaths.add(currentLdPath); + } + for (String path : SWIFT_LIB_PATHS) { + File dir = new File(path); + if (dir.exists() && dir.isDirectory()) { + log.info("Found Swift library path: {}", path); + existingPaths.add(path); + } + } + if (!existingPaths.isEmpty()) { + String ldPath = String.join(":", existingPaths); + log.info("Setting LD_LIBRARY_PATH for Swift binary: {}", ldPath); + ps.environment().put("LD_LIBRARY_PATH", ldPath); + } + } } diff --git a/src/main/resources/challenges/challenge-63/Dockerfile_challenge63 b/src/main/resources/challenges/challenge-63/Dockerfile_challenge63 new file mode 100644 index 000000000..df0d4e4a4 --- /dev/null +++ b/src/main/resources/challenges/challenge-63/Dockerfile_challenge63 @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1.21 +# Test that the wrongsecrets-swift musl binary runs correctly on Alpine. +# The musl binaries are fully statically linked (Swift runtime embedded), so they +# run natively on Alpine's musl libc without any extra shared libraries. + +FROM alpine:3.21 + +# Copy the Linux musl x86_64 binary (build context is the repo root) +COPY src/main/resources/executables/wrongsecrets-swift-linux-musl /wrongsecrets-swift +RUN chmod +x /wrongsecrets-swift + +# Run the binary and verify it produces non-empty output (the hardcoded secret) +CMD ["/bin/sh", "-c", "OUTPUT=$(/wrongsecrets-swift) && [ -n \"$OUTPUT\" ] && echo \"Swift binary works: $OUTPUT\" || (echo 'Swift binary failed or produced no output' && exit 1)"] diff --git a/src/main/resources/executables/wrongsecrets-swift b/src/main/resources/executables/wrongsecrets-swift new file mode 100755 index 000000000..3d7f60e11 Binary files /dev/null and b/src/main/resources/executables/wrongsecrets-swift differ diff --git a/src/main/resources/executables/wrongsecrets-swift-arm b/src/main/resources/executables/wrongsecrets-swift-arm new file mode 100755 index 000000000..3d7f60e11 Binary files /dev/null and b/src/main/resources/executables/wrongsecrets-swift-arm differ diff --git a/src/main/resources/executables/wrongsecrets-swift-arm-ctf b/src/main/resources/executables/wrongsecrets-swift-arm-ctf new file mode 100755 index 000000000..3d7f60e11 Binary files /dev/null and b/src/main/resources/executables/wrongsecrets-swift-arm-ctf differ diff --git a/src/main/resources/executables/wrongsecrets-swift-ctf b/src/main/resources/executables/wrongsecrets-swift-ctf new file mode 100755 index 000000000..3d7f60e11 Binary files /dev/null and b/src/main/resources/executables/wrongsecrets-swift-ctf differ diff --git a/src/main/resources/executables/wrongsecrets-swift-linux b/src/main/resources/executables/wrongsecrets-swift-linux new file mode 100755 index 000000000..403d32bf1 Binary files /dev/null and b/src/main/resources/executables/wrongsecrets-swift-linux differ diff --git a/src/main/resources/executables/wrongsecrets-swift-linux-arm b/src/main/resources/executables/wrongsecrets-swift-linux-arm new file mode 100755 index 000000000..1b280684a Binary files /dev/null and b/src/main/resources/executables/wrongsecrets-swift-linux-arm differ diff --git a/src/main/resources/executables/wrongsecrets-swift-linux-arm-ctf b/src/main/resources/executables/wrongsecrets-swift-linux-arm-ctf new file mode 100755 index 000000000..1b280684a Binary files /dev/null and b/src/main/resources/executables/wrongsecrets-swift-linux-arm-ctf differ diff --git a/src/main/resources/executables/wrongsecrets-swift-linux-ctf b/src/main/resources/executables/wrongsecrets-swift-linux-ctf new file mode 100755 index 000000000..403d32bf1 Binary files /dev/null and b/src/main/resources/executables/wrongsecrets-swift-linux-ctf differ diff --git a/src/main/resources/executables/wrongsecrets-swift-linux-musl b/src/main/resources/executables/wrongsecrets-swift-linux-musl new file mode 100755 index 000000000..aa2012cae Binary files /dev/null and b/src/main/resources/executables/wrongsecrets-swift-linux-musl differ diff --git a/src/main/resources/executables/wrongsecrets-swift-linux-musl-arm b/src/main/resources/executables/wrongsecrets-swift-linux-musl-arm new file mode 100755 index 000000000..463ad3d02 Binary files /dev/null and b/src/main/resources/executables/wrongsecrets-swift-linux-musl-arm differ diff --git a/src/main/resources/executables/wrongsecrets-swift-linux-musl-arm-ctf b/src/main/resources/executables/wrongsecrets-swift-linux-musl-arm-ctf new file mode 100755 index 000000000..463ad3d02 Binary files /dev/null and b/src/main/resources/executables/wrongsecrets-swift-linux-musl-arm-ctf differ diff --git a/src/main/resources/executables/wrongsecrets-swift-linux-musl-ctf b/src/main/resources/executables/wrongsecrets-swift-linux-musl-ctf new file mode 100755 index 000000000..aa2012cae Binary files /dev/null and b/src/main/resources/executables/wrongsecrets-swift-linux-musl-ctf differ diff --git a/src/main/resources/explanations/challenge63.adoc b/src/main/resources/explanations/challenge63.adoc new file mode 100644 index 000000000..a69d5f300 --- /dev/null +++ b/src/main/resources/explanations/challenge63.adoc @@ -0,0 +1,7 @@ +=== Hiding in binaries part 5: the Swift binary + +Similar to hiding secrets in an application written in C, you can do this in Swift! Swift is Apple's modern compiled language used across Apple platforms and Linux. Can you find the secret in our binary? + +Let's debunk the "secrets are hard to find in native compiled applications" myth for Swift: can you find the secret in https://github.com/OWASP/wrongsecrets/tree/master/src/main/resources/executables/wrongsecrets-swift[wrongsecrets-swift] (or https://github.com/OWASP/wrongsecrets/tree/master/src/main/resources/executables/wrongsecrets-swift-arm[wrongsecrets-swift-arm], https://github.com/OWASP/wrongsecrets/tree/master/src/main/resources/executables/wrongsecrets-swift-linux[wrongsecrets-swift-linux])? + +Try downloading the binary and run it locally (e.g. `./wrongsecrets-swift `). diff --git a/src/main/resources/explanations/challenge63_hint.adoc b/src/main/resources/explanations/challenge63_hint.adoc new file mode 100644 index 000000000..ca2f3fa53 --- /dev/null +++ b/src/main/resources/explanations/challenge63_hint.adoc @@ -0,0 +1,25 @@ +This challenge is specifically looking at a secret stored in a Swift binary as an array of characters. + +You can solve this challenge using the following alternative solutions: + +1. Find the secrets with https://ghidra-sre.org/[Ghidra]. +- Install https://ghidra-sre.org/[Ghidra]. +- Start it with `ghidraRun`. +- Load the application `wrongsecrets-swift` into Ghidra by choosing a new project, then import the file and then double-click on it. +- Allow Ghidra to analyze the application. +- Look for the `getSecret` function - you can search for it in the Symbol Tree. +- In the decompiled code, find the array of characters that make up the secret. +- Alternatively: on macOS, use `nm -gUj wrongsecrets-swift | grep getSecret` and then `swift-demangle --expand "s5swift9getSecretSSyF"` to find the function. + +2. Find the secrets with https://www.radare.org[radare2]. +- Install https://www.radare.org[radare2] with either `brew install radare2` on Mac or follow these steps: `git clone https://github.com/radareorg/radare2; cd radare2 ; sys/install.sh` +- Launch r2 analysis with `$ r2 -AAA wrongsecrets-swift` +- Search for strings with `iz | grep -i secret` or `iz | grep -i "This"`. +- Alternatively, look for the `getSecret` function: `afl | grep getSecret` and then print the function with `pdf @ `. +- Examine the character array construction in the decompiled or disassembled code. + +3. Find the secrets with https://binary.ninja[Binary Ninja] or similar tools: +- Load the binary and navigate to the Swift mangled function `s5swift9getSecretSSyF`. +- The secret is constructed from an array of characters. + +Don't want to install the tools? check the https://github.com/OWASP/wrongsecrets/tree/master?tab=readme-ov-file#want-to-play-but-are-not-allowed-to-install-the-tools[WrongSecrets Desktop container]! diff --git a/src/main/resources/explanations/challenge63_reason.adoc b/src/main/resources/explanations/challenge63_reason.adoc new file mode 100644 index 000000000..31f5b33d0 --- /dev/null +++ b/src/main/resources/explanations/challenge63_reason.adoc @@ -0,0 +1,9 @@ +*Why Using binaries to hide a secret will only delay an attacker.* + +With beautiful free Reverse engineering applications as Ghidra, not a lot of things remain safe. Anyone who can load the executable in Ghidra or Radare2 can easily start doing a reconnaissance and find secrets within your Swift binary. + +Swift binaries, even though they use name mangling (you can use `swift-demangle` to demangle Swift symbol names), are still susceptible to reverse engineering. The secrets stored as character arrays or string literals can be found in the binary's data section. + +Encrypting the secret with a key embedded in the binary, and other funny puzzles do delay an attacker and just make it fun finding the secret. Be aware that, if the secret needs to be used by the executable, it eventually needs to be in memory ready to be executed. + +Still need to have a secret in the binary? Make sure it can only be retrieved remotely after authenticating against a server. diff --git a/src/main/resources/wrong-secrets-configuration.yaml b/src/main/resources/wrong-secrets-configuration.yaml index 976e6f087..af63c9315 100644 --- a/src/main/resources/wrong-secrets-configuration.yaml +++ b/src/main/resources/wrong-secrets-configuration.yaml @@ -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" + explanation: "explanations/challenge63.adoc" + hint: "explanations/challenge63_hint.adoc" + reason: "explanations/challenge63_reason.adoc" + environments: *all_envs + difficulty: *master + category: *bin + ctf: + enabled: true diff --git a/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge63Test.java b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge63Test.java new file mode 100644 index 000000000..1f33fb10d --- /dev/null +++ b/src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge63Test.java @@ -0,0 +1,25 @@ +package org.owasp.wrongsecrets.challenges.docker; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.owasp.wrongsecrets.Challenges.ErrorResponses.EXECUTION_ERROR; + +import org.junit.jupiter.api.Test; +import org.owasp.wrongsecrets.challenges.Spoiler; + +class Challenge63Test { + + @Test + void spoilerShouldNotCrash() { + var challenge = new Challenge63(); + + assertThat(challenge.spoiler()).isNotEqualTo(new Spoiler(EXECUTION_ERROR)); + assertThat(challenge.answerCorrect(challenge.spoiler().solution())).isTrue(); + } + + @Test + void incorrectAnswerShouldNotSolveChallenge() { + var challenge = new Challenge63(); + + assertThat(challenge.answerCorrect("wrong answer")).isFalse(); + } +}