diff --git a/cha-cha-slide/Dockerfile b/cha-cha-slide/Dockerfile new file mode 100644 index 0000000..47da7ce --- /dev/null +++ b/cha-cha-slide/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.12-bookworm + +WORKDIR /app + +RUN wget -O ynetd.c \ + https://raw.githubusercontent.com/johnsonjh/ynetd/e6fd08f8f5d0c6b8c18d645957e30ce012536ed4/ynetd.c \ + && echo "ec7509dec7737da54f8b18e1b5ba935d657f9f016c36cfc9ac08f9952373226f ynetd.c" | sha256sum -c \ + && gcc -o ynetd ynetd.c + +COPY ./flag.txt . + +WORKDIR /app/server + +COPY ./server . + +RUN pip install -r requirements.txt + +ENTRYPOINT ["../ynetd", "-p", "3000", "python3 ./server.py"] diff --git a/cha-cha-slide/chall.yaml b/cha-cha-slide/chall.yaml new file mode 100644 index 0000000..45d3691 --- /dev/null +++ b/cha-cha-slide/chall.yaml @@ -0,0 +1,19 @@ +name: Cha-Cha Slide +categories: + - crypto +value: 75 +flag: + file: ./flag.txt +description: |- + I made this cool service that lets you protect your secrets + with state-of-the-art encryption. It's so secure that we don't + even tell you the key we used to encrypt your message! +files: + - src: ./server/server.py +deploy: + nc: + build: . + expose: 3000/tcp +authors: + - Thomas +visible: true diff --git a/cha-cha-slide/flag.txt b/cha-cha-slide/flag.txt new file mode 100644 index 0000000..0a3c299 --- /dev/null +++ b/cha-cha-slide/flag.txt @@ -0,0 +1 @@ +bcactf{b3_C4rEFu1_wItH_crypT0Gr4phy_7d12be3b} \ No newline at end of file diff --git a/cha-cha-slide/server/requirements.txt b/cha-cha-slide/server/requirements.txt new file mode 100644 index 0000000..b05066e --- /dev/null +++ b/cha-cha-slide/server/requirements.txt @@ -0,0 +1 @@ +pycryptodome==3.19.0 diff --git a/cha-cha-slide/server/server.py b/cha-cha-slide/server/server.py new file mode 100644 index 0000000..032bab6 --- /dev/null +++ b/cha-cha-slide/server/server.py @@ -0,0 +1,36 @@ +from Crypto.Cipher import ChaCha20 + +from os import urandom + +key = urandom(32) +nonce = urandom(12) + +secret_msg = urandom(16).hex() + +def encrypt_msg(plaintext): + cipher = ChaCha20.new(key=key, nonce=nonce) + return cipher.encrypt(plaintext.encode()).hex() + +print('Secret message:') +print(encrypt_msg(secret_msg)) + +print('\nEnter your message:') +user_msg = input() + +if len(user_msg) > 256: + print('\nToo long!') + exit() + +print('\nEncrypted:') +print(encrypt_msg(user_msg)) + +print('\nEnter decrypted secret message:') +decrypted_secret_msg = input() + +if len(decrypted_secret_msg) == len(secret_msg): + if decrypted_secret_msg == secret_msg: + with open('../flag.txt') as file: + print('\n' + file.read()) + exit() + +print('\nIncorrect!') diff --git a/cha-cha-slide/solve.ts b/cha-cha-slide/solve.ts new file mode 100755 index 0000000..5276b8d --- /dev/null +++ b/cha-cha-slide/solve.ts @@ -0,0 +1,24 @@ +#!/usr/bin/env -S deno run + +// ChaCha20 works by XORing the plaintext with a stream generated from +// the (key, nonce) pair. Since this pair is reused for encrypting the +// secret message and the user's message, the following operations can be +// performed to reveal the secret plaintext given the secret ciphertext: +// 1. user_ciphertext = user_plaintext ⊕ stream +// 2. stream = user_plaintext ⊕ user_ciphertext +// 3. secret_plaintext = stream ⊕ secret_ciphertext + +const fromHex = (hex: string) => hex.match(/.{2}/g)!.map(byte => parseInt(byte, 16)) + +const secretMsg = fromHex(prompt('Enter secret message:')!) + +console.log('Enter this as your message: ' + '1'.repeat(secretMsg.length)) + +const encryptedUserMsg = fromHex(prompt('Enter encrypted user message:')!) + +const decryptedSecretMsg = secretMsg + .map((v, i) => v ^ encryptedUserMsg[i]) + .map(v => String.fromCharCode(v ^ '1'.charCodeAt(0))) + .join('') + +console.log('Decrypted secret message: ' + decryptedSecretMsg)