picoCTF 2025 writeup (Crypto)

picoCTF 2025 の Crypto 問題の Writeup (medium 問題のみ)

Guess My Cheese (Part1)

Affine 暗号とは

$$E(x) \equiv (a \cdot x + b) \mod m$$

$$x = \equiv a^{-1} \cdot (E(x) - b) \mod m$$

Exploit

from pwn import *
from Crypto.Util.number import inverse

def affine_guess(enc1: int, enc2: int, plain1: int, plain2: int, m: int=26):
    a = inverse(plain1 - plain2, m) * (enc1 - enc2) % m
    b = (enc1 - a*plain1) % m
    return a, b

def affine_dec(a: int, b: int, enc: str, m: int=26):
    plain = ""
    for i in range(len(enc)):
        plain += chr(((ord(enc[i]) - ord("A") - b) * inverse(a, m)) % m + ord("a"))
    
    return plain
        
host = "verbal-sleep.picoctf.net"
port = 61921

cheese1 = "cheddar"

sh = remote(host, port)

prompt = sh.recvuntil("guess it:")
print(prompt.decode())

cipher = sh.recvline().decode().strip()
print("cipher: ", cipher)

prompt = sh.recvuntil("like to do?")
print(prompt.decode())

sh.sendline("e".encode())

prompt = sh.recvuntil("encrypt?")
print(prompt.decode())

sh.sendline(cheese1.encode())

prompt = sh.recvuntil("cheese:")
print(prompt.decode())

enc_cheese = sh.recvline().decode().strip()

a, b = affine_guess(enc1=ord(enc_cheese[0]) - ord("A"), enc2=ord(enc_cheese[1]) - ord("A"), plain1=ord(cheese1[0]) - ord("a"), plain2=ord(cheese1[1]) - ord("a"))
key = affine_dec(a, b, cipher)
print("key:", key)

prompt = sh.recvuntil("like to do?")
print(prompt.decode())

sh.sendline("g".encode())

prompt = sh.recvuntil("cheese?")
print(prompt.decode())

sh.sendline(key.encode())

sh.interactive()
        

Guess My Cheese (Part2)

from pwn import *
import hashlib

rainbow = {}
with open("./cheese_list.txt", "r") as f:
    cheese_list = f.readlines()
    for cheese in cheese_list:
        for i in range(0x100):
            salty = cheese.strip().lower().encode() + bytes([i])
            rainbow[hashlib.sha256(salty).hexdigest()] = salty.strip()
            salty = bytes([i]) + cheese.strip().lower().encode()
            rainbow[hashlib.sha256(salty).hexdigest()] = salty.strip()

host = "verbal-sleep.picoctf.net"
port = 57464
sh = remote(host, port)

prompt = sh.recvuntil("guess it:")
print(prompt.decode())

cipher = sh.recvline().decode().strip()
print("cipher:", cipher)

print("hit:", rainbow[cipher])

prompt = sh.recvuntil("like to do?")
print(prompt.decode())

sh.sendline("g".encode())

prompt = sh.recvuntil("my cheese?")
print(prompt.decode())

sh.sendline(rainbow[cipher][:-1])

prompt = sh.recvuntil("my salt?")
print(prompt.decode())

print("key:", rainbow[cipher])
salt_int = int(rainbow[cipher][-1])
salt_hex = f"{salt_int:02x}"
print("salt hex", salt_hex)

sh.sendline(salt_hex.encode())

sh.interactive()