picoCTF 2024 Cryptography
picoCTF 2024 Cryptography writeups covering encryption, decryption, and cryptanalysis challenges.

picoCTF 2024 - Cryptography

Cryptography Challenges
Solved: 4/5 challenges
- interencdec - 50 points
- Custom encryption - 100 points
- C3 - 200 points
- rsa_oracle - 300 points
interencdec
Tags: `Cryptography` `base64` `browser_webshell_solvable` `caesar`
Description
Can you get the real meaning from this file.
//enc_flag
YidkM0JxZGtwQlRYdHFhR3g2YUhsZmF6TnFlVGwzWVROclh6YzRNalV3YUcxcWZRPT0nCg==
Solution Strategy
import base64
encoded_base64 = "YidkM0JxZGtwQlRYdHFhR3g2YUhsZmF6TnFlVGwzWVROclh6YzRNalV3YUcxcWZRPT0nCg=="
print("Encoded base64 string:", encoded_base64)
decoded_bytes = base64.b64decode(encoded_base64)
decoded_string = decoded_bytes.decode("utf-8")
print("Decoded base64 string:", decoded_string)
import re
base64_pattern = "'(.*?)'"
base64_match = re.search(base64_pattern, decoded_string)
if base64_match:
base64_text = base64_match.group(1)
else:
raise ValueError("No base64 string found within single quotes")
print("Extracted base64 string:", base64_text)
decoded_text_bytes = base64.b64decode(base64_text)
decoded_text = decoded_text_bytes.decode("utf-8")
print("Decoded base64 text before shift:", decoded_text)
def caesar_cipher(text, shift):
result = ""
for char in text:
if char.isalpha():
shifted = ord(char) + shift
if char.islower():
if shifted > ord("z"):
shifted -= 26
elif shifted < ord("a"):
shifted += 26
elif char.isupper():
if shifted > ord("Z"):
shifted -= 26
elif shifted < ord("A"):
shifted += 26
result += chr(shifted)
else:
result += char
return result
shifted_text = caesar_cipher(decoded_text, 19)
print("Decoded and shifted base64 text:", shifted_text)
- The script decodes a base64-encoded string (
encoded_base64) using thebase64module and prints the decoded string. - It then extracts a substring from the decoded string using a regular expression pattern to find a base64 string within single quotes.
- After decoding this extracted base64 string, it applies a Caesar Cipher with a shift of 19 to the decoded text using a custom function (
caesar_cipher). - Finally, it prints the resulting text after applying the Caesar Cipher.
- The overall purpose seems to be to decode a base64-encoded string, extract another base64 string from it, decode it, and then apply a Caesar Cipher with a specific shift to it.
Flag

Custom encryption
Tags: `Cryptography` `ASCII_encoding` `browser_webshell_solvable` `XOR`
Description
Can you make sense of this code file and write the function that will decode the given encrypted file content. Find the encrypted file here flag_info and code file might be good to analyze and get the flag.
//custom_encryption.py
from random import randint
import sys
def generator(g, x, p):
return pow(g, x) % p
def encrypt(plaintext, key):
cipher = []
for char in plaintext:
cipher.append(((ord(char) * key*311)))
return cipher
def is_prime(p):
v = 0
for i in range(2, p + 1):
if p % i == 0:
v = v + 1
if v > 1:
return False
else:
return True
def dynamic_xor_encrypt(plaintext, text_key):
cipher_text = ""
key_length = len(text_key)
for i, char in enumerate(plaintext[::-1]):
key_char = text_key[i % key_length]
encrypted_char = chr(ord(char) ^ ord(key_char))
cipher_text += encrypted_char
return cipher_text
def test(plain_text, text_key):
p = 97
g = 31
if not is_prime(p) and not is_prime(g):
print("Enter prime numbers")
return
a = randint(p-10, p)
b = randint(g-10, g)
print(f"a = {a}")
print(f"b = {b}")
u = generator(g, a, p)
v = generator(g, b, p)
key = generator(v, a, p)
b_key = generator(u, b, p)
shared_key = None
if key == b_key:
shared_key = key
else:
print("Invalid key")
return
semi_cipher = dynamic_xor_encrypt(plain_text, text_key)
cipher = encrypt(semi_cipher, shared_key)
print(f'cipher is: {cipher}')
if __name__ == "__main__":
message = sys.argv[1]
test(message, "trudeau")
//enc_flag
a = 94
b = 21
cipher is: [131553, 993956, 964722, 1359381, 43851, 1169360, 950105, 321574, 1081658, 613914, 0, 1213211, 306957, 73085, 993956, 0, 321574, 1257062, 14617, 906254, 350808, 394659, 87702, 87702, 248489, 87702, 380042, 745467, 467744, 716233, 380042, 102319, 175404, 248489]
Solution Strategy
def decrypt(ciphertext, key):
plaintext = ""
for num in ciphertext:
decrypted_num = num // (key * 311)
plaintext += chr(decrypted_num)
return plaintext
def dynamic_xor_decrypt(ciphertext, text_key):
decrypted_text = ""
key_length = len(text_key)
for i, char in enumerate(ciphertext):
key_char = text_key[i % key_length]
decrypted_char = chr(ord(char) ^ ord(key_char))
decrypted_text += decrypted_char
return decrypted_text
def find_shared_key(p, g, a, b):
u = pow(g, a, p)
v = pow(g, b, p)
shared_key = pow(v, a, p)
b_key = pow(u, b, p)
if shared_key == b_key:
return shared_key
else:
return None
def main():
p = 97
g = 31
a = 94
b = 21
shared_key = find_shared_key(p, g, a, b)
if shared_key is None:
print("Invalid key")
return
ciphertext = [131553, 993956, 964722, 1359381, 43851, 1169360, 950105, 321574, 1081658, 613914, 0, 1213211, 306957, 73085, 993956, 0, 321574, 1257062, 14617, 906254, 350808, 394659, 87702, 87702, 248489, 87702, 380042, 745467, 467744, 716233, 380042, 102319, 175404, 248489]
decrypted_text = decrypt(ciphertext, shared_key)
decrypted_text = dynamic_xor_decrypt(decrypted_text, "trudeau")
decrypted_text = decrypted_text[::-1]
print("Decrypted text:", decrypted_text)
if __name__ == "__main__":
main()
- The
decryptfunction takes a list of ciphertext numbers and a key as input, then decrypts the ciphertext by dividing each number by the product of the key and a constant (311), converting the decrypted numbers to characters, and appending them to form the plaintext. - The
dynamic_xor_decryptfunction performs a dynamic XOR decryption on a ciphertext using a text-based key. It XORs each character of the ciphertext with the corresponding character from the key, repeating the key cyclically if necessary, and builds the decrypted text. - The
find_shared_keyfunction calculates a shared key using Diffie-Hellman key exchange algorithm parameters (p,g) and private keys (a,b). It computes intermediate valuesuandv, then derives the shared key using modular exponentiation. - In the
mainfunction, it initializes parameters for Diffie-Hellman key exchange (p,g,a,b) and calculates the shared key usingfind_shared_key. - It then decrypts a ciphertext list using the shared key and the
decryptfunction, followed by a dynamic XOR decryption using thedynamic_xor_decryptfunction with the key"trudeau". - Finally, it reverses the decrypted text and prints the result.
Flag

C3
Tags: `Cryptography` `browser_webshell_solvable`
Description
This is the Custom Cyclical Cipher! Download the ciphertext here. Download the encoder here. Enclose the flag in our wrapper for submission. If the flag was "example" you would submit "picoCTF{example}".
//convert.py
import sys
chars = ""
from fileinput import input
for line in input():
chars += line
lookup1 = "\n \"#()*+/1:=[]abcdefghijklmnopqrstuvwxyz"
lookup2 = "ABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrst"
out = ""
prev = 0
for char in chars:
cur = lookup1.index(char)
out += lookup2[(cur - prev) % 40]
prev = cur
sys.stdout.write(out)
//ciphertext
DLSeGAGDgBNJDQJDCFSFnRBIDjgHoDFCFtHDgJpiHtGDmMAQFnRBJKkBAsTMrsPSDDnEFCFtIbEDtDCIbFCFtHTJDKerFldbFObFCFtLBFkBAAAPFnRBJGEkerFlcPgKkImHnIlATJDKbTbFOkdNnsgbnJRMFnRBNAFkBAAAbrcbTKAkOgFpOgFpOpkBAAAAAAAiClFGIPFnRBaKliCgClFGtIBAAAAAAAOgGEkImHnIl
Solution Strategy
ciphertext = "DLSeGAGDgBNJDQJDCFSFnRBIDjgHoDFCFtHDgJpiHtGDmMAQFnRBJKkBAsTMrsPSDDnEFCFtIbEDtDCIbFCFtHTJDKerFldbFObFCFtLBFkBAAAPFnRBJGEkerFlcPgKkImHnIlATJDKbTbFOkdNnsgbnJRMFnRBNAFkBAAAbrcbTKAkOgFpOgFpOpkBAAAAAAAiClFGIPFnRBaKliCgClFGtIBAAAAAAAOgGEkImHnIl"
lookup1 = "\n \"#()*+/1:=[]abcdefghijklmnopqrstuvwxyz"
lookup2 = "ABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrst"
out = ""
prev = 0
for char in ciphertext:
cur = lookup2.index(char) + prev
cur_mod = cur % len(lookup1)
out += lookup1[cur_mod]
prev = cur_mod
print(out)
- Initialization: The ciphertext is defined as a string of characters. Two lookup tables,
lookup1andlookup2, contain a mix of symbols, numbers, and letters. An empty stringoutis initialized to hold the decrypted output, and a variableprevis set to0to track the position from the previous iteration. - Decryption process:
- The script iterates over each character in the ciphertext.
- For each character, it finds the character's index in
lookup2and adds this index toprev(initially0). - The sum is then modulo-divided by the length of
lookup1to ensure the resulting index is within the bounds oflookup1. This result is stored incur_mod. - The character at the
cur_modindex oflookup1is appended to theoutstring, building the decrypted message character by character. - The
prevvariable is updated tocur_modfor use in the next iteration.
- Output: After iterating through the entire ciphertext, the decrypted message is stored in
out, which is then printed to the console.
#This is output from solve.py
#asciiorder
#fortychars
#selfinput
#pythontwo
chars = ""
from fileinput import input
for line in input():
chars += line
b = 1 / 1
for i in range(len(chars)):
if i == b * b * b:
print chars[i] #prints
b += 1 / 1
python solve.py > decode.txt
Flag
# Change Python2 --> Python3
#asciiorder
#fortychars
#selfinput
#pythontwo
chars = ""
from fileinput import input
for line in input():
chars += line
b = 1
for i in range(len(chars)):
if i == b * b * b:
print(chars[i]) # prints
b += 1
python flag.py < decode.txt

picoCTF{adlibs}
rsa_oracle
Tags: `Cryptography` `browser_webshell_solvable`
Description
Can you abuse the oracle? An attacker was able to intercept communications between a bank and a fintech company. They managed to get the message (ciphertext) and the password that was used to encrypt the message. After some intensive reconnaissance they found out that the bank has an oracle that was used to encrypt the password and can be found here nc titan.picoctf.net 52816. Decrypt the password and use it to decrypt the message. The oracle can decrypt anything except the password.
//secret.enc
Salted__ÿò(Ñè-év\rAj6<Cž·µ ^&šÕËò€±q¯\ª/KÓÃÎ:ÆÊL€õ3'~
//pwd.enc
1634668422544022562287275254811184478161245548888973650857381112077711852144181630709254123963471597994127621183174673720047559236204808750789430675058597
Solution Strategy
#!/usr/bin/env python
from pwn import *
import subprocess
IP = 'titan.picoctf.net'
PORT = 52816
def get_target():
return remote(IP, PORT)
with open('pwd.enc', 'r') as f:
pas = f.read()
ipas = int(pas, 10)
t = get_target()
t.sendlineafter(b'decrypt.', b'E')
t.sendlineafter(':', p8(2))
t.recvuntil(b'(m ^ e mod n)')
c_a = int(t.recvline().strip())
t.sendlineafter(b'decrypt.', b'D')
t.sendlineafter(b':', str(c_a * ipas).encode())
t.recvuntil(b'(c ^ d mod n): ')
oracle = int(t.recvline().strip(), 0x10)
password = bytes.fromhex(hex(oracle // 2)[2:].rstrip("L")).decode()
print(f"Decrypted Password: {password}")
t.close()
command = ["openssl", "enc", "-aes-256-cbc", "-d", "-in", "secret.enc", "-k", password]
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = process.communicate()
if process.returncode == 0:
print("Decrypted Content:\n", stdout.decode())
else:
print("Decryption failed:", stderr.decode())
- Import and Setup
- The script begins by importing necessary libraries:
pwntoolsfor network communication andsubprocessfor executing external commands. - The target encryption service is defined with its IP (
titan.picoctf.net) and port (52816), indicating the script is designed to interact with a specific remote service.
- The script begins by importing necessary libraries:
- Reading Encrypted Password
- An encrypted password is read from a file named
pwd.enc. This file contains the password needed to decrypt another file, but in encrypted form. The script reads this encrypted password and converts it to an integer (ipas), preparing it for further operations.
- An encrypted password is read from a file named
- Establishing Connection
- Utilizing
pwntools, the script establishes a network connection to the remote encryption service. This connection is used to send commands to and receive responses from the service, facilitating remote exploitation.
- Utilizing
- Exploiting Encryption
- Encryption Request: The script first instructs the remote service to encrypt the number
2. This step is crucial because it uses the service's own encryption functionality against it. The response,c_a, is the encrypted form of2according to the service's encryption scheme. - Crafting Decryption Request: Next, the script sends a decryption request, but with a twist. Instead of asking to decrypt a straightforward message, it requests the decryption of the product of
c_a(the encrypted2) andipas(the encrypted password, now as an integer). This crafted request exploits specific vulnerabilities in the encryption algorithm's implementation, leveraging mathematical properties of RSA encryption to manipulate the service into performing operations that ultimately aid in reversing the encryption.
- Encryption Request: The script first instructs the remote service to encrypt the number
- Decrypting the Password
- Processing the Decryption Response: The service responds with
oracle, the result of the decryption request. The script processes this response to extract the original password. By dividingoracleby2and converting it back from hexadecimal, the script effectively reverses the encryption applied to the original password. This step cleverly uses the properties of RSA encryption, where specific manipulations of encrypted data can yield useful decrypted outcomes.
- Processing the Decryption Response: The service responds with
- File Decryption
- Decrypting
secret.enc: With the decrypted password now available, the script proceeds to decryptsecret.encusing OpenSSL. This step is straightforward: it runs an OpenSSL command with the AES-256-CBC decryption option, using the decrypted password as the key. The success of this operation hinges on correctly extracting the password in the previous steps.
- Decrypting
- Output Handling
- The script evaluates the success of the decryption operation by checking the return code of the OpenSSL command. A return code of
0indicates success, prompting the script to print the decrypted contents ofsecret.enc. If the operation fails, an error message is displayed instead.
- The script evaluates the success of the decryption operation by checking the return code of the OpenSSL command. A return code of
Flag
