PicoCTF 2025 Cryptography—Guess My Cheese Part 1 Writeup

Glad to have y’all back to my WriteUp! In today’s post, I’ll walk you through a detailed, step-by-step breakdown of how I successfully tackled the Guess My Cheese Part 1 challenge from picoCTF 2025. Get ready for an in-depth look at my approach, techniques, and insights into solving this exciting puzzle. Let’s dive in!

Analyzing the hints provided

  • Remember that cipher we devised together Squeexy? The one that incorporates your affinity for linear equations???

This is the challenge

Alright, so we’re given a “Secret Cheese,” which is actually the ciphertext. There are two available commands: one lets us guess what the secret cheese is, and the other allows us to encrypt a cheese of our choice.

Now let's encrypt a cheese, let's try cheddar

So the ciphertext for "Cheddar" is JMFUUNS.

If we revisit the hint and examine it closely, it subtly points toward the idea of an affine transformation, which is essentially a type of linear equation. This strongly suggests that the encryption method being used here is likely the Affine Cipher.

The Affine Cipher uses a mathematical formula to transform each letter in the plaintext:

E(x) = (a * x + b) mod 26

Where:

  • x is the position of the letter in the alphabet (A=0 to Z=25),

  • a and b are keys (with a being coprime with 26),

  • and the result gives us the encrypted letter.

So our task now is to determine the correct values for a and b that transform "CHEDDAR" into "JMFUUNS". Once we identify those keys, we can use them to decrypt the original secret cheese message.

To solve this manually, we can use letter-position mappings to help us. Let's convert "CHEDDAR" and "JMFUUNS" into their respective indices:

Letter
Plain Index
Cipher Index

C

2

J → 9

H

7

M → 12

E

4

F → 5

D

3

U → 20

D

3

U → 20

A

0

N → 13

R

17

S → 18

Now, using one of the letter pairs, say:

  • x = 2 (C), E(x) = 9 (J)

We can plug into the Affine equation:

( a * 2 + b ) % 26 = 9

Try another point:

  • x = 7 (H), E(x) = 12 (M)

( a * 7 + b ) % 26 = 12

Now solve the system of equations:

  1. 2a + b ≡ 9 (mod 26)

  2. 7a + b ≡ 12 (mod 26)

Subtract (1) from (2):

(7a + b) - (2a + b) ≡ 12 - 9
5a ≡ 3 (mod 26)

Now solve for a:

Multiply both sides by the modular inverse of 5 mod 26. The inverse of 5 mod 26 is 21, since 5 * 21 ≡ 1 mod 26.

So:

a ≡ 21 * 3 ≡ 63 ≡ 11 (mod 26)

Now plug a = 11 into one equation:

2a + b ≡ 92*11 + b ≡ 922 + b ≡ 9b ≡ 9 - 22 ≡ -13 ≡ 13 (mod 26)

Final Keys:

  • a = 11

  • b = 13

So the encryption is:

E(x) = (11 * x + 13) mod 26

To decrypt, use:

D(y) = a⁻¹ * (y - b) mod 26

Where a⁻¹ is the modular inverse of 11 mod 26, which is 19.

Here's the script I've made to automate this process

from string import ascii_uppercase

def mod_inverse(a, m):
    for i in range(1, m):
        if (a * i) % m == 1:
            return i
    raise ValueError("No modular inverse exists")

def affine_encrypt(plaintext, a, b):
    m = 26
    encrypted = ""
    for char in plaintext.upper():
        if char in ascii_uppercase:
            x = ascii_uppercase.index(char)
            encrypted += ascii_uppercase[(a * x + b) % m]
        else:
            encrypted += char
    return encrypted

def affine_decrypt(ciphertext, a, b):
    m = 26
    a_inv = mod_inverse(a, m)

    decrypted = ""
    for char in ciphertext:
        if char in ascii_uppercase:
            x = ascii_uppercase.index(char)
            decrypted += ascii_uppercase[(a_inv * (x - b)) % m]
        else:
            decrypted += char
    return decrypted

# Known plaintext and ciphertext
plaintext = "CHEDDAR"
ciphertext = "JMFUUNS"

# Valid a values (coprime with 26)
valid_a_values = [1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25]

# Brute-force to find the key
for a in valid_a_values:
    for b in range(26):
        test = affine_encrypt(plaintext, a, b)
        if test == ciphertext:
            print(f"Found keys: a = {a}, b = {b}")
            # Decrypt the secret cheese
            secret_ciphertext = "UNSNENBINCDTRKTA"
            decrypted_secret = affine_decrypt(secret_ciphertext, a, b)
            print("Decrypted Secret Cheese:", decrypted_secret)
            exit()

Run and we should get the value of a, b, and the decrypted text

11 and 13, same value when we solve it manually

Let's choose the guess option and paste the decrypted secret cheese then we should get the flag!

FLAG

This challenge highlights the use of the Affine Cipher and demonstrates how it can be broken. I hope this write-up helps you understand how the Affine Cipher works, and shows you both manual and programmatic methods for breaking it.^^

Last updated