Description: A new modular challenge! Take each number mod 41 and find the modular inverse for the result. Then map to the following character set: 1-26 are the alphabet, 27-36 are the decimal digits, and 37 is an underscore. Wrap your decrypted message in the picoCTF flag format (i.e. picoCTF{decrypted_message})
Difficulty: Medium
Author: Will Hong
Summary
In this challenge, we decrypt a message by performing modular arithmetic operations. Each number must be reduced modulo 41, then we find its modular multiplicative inverse. The resulting values are mapped to a custom character set where 1-26 represent letters A-Z, 27-36 represent digits 0-9, and 37 represents an underscore.
Analysis
We are provided with a file named message.txt:
$ file message.txtmessage.txt: ASCII text, with no line terminatorsIts content:
$ cat message.txt104 372 110 436 262 173 354 393 351 297 241 86 262 359 256 441 124 154 165 165 219 288 42So according to the challenge description, we need to:
- Take each number mod 41
- Find the modular inverse for the result
- Then map to the following character set: 1-26 are the alphabet, 27-36 are the decimal digits, and 37 is an underscore.
What Does βmod 41β Mean?
Taking a number mod 41 means:
Divide the number by 41 and keep the remainder
Examples:
104 mod 41 = 22372 mod 41 = 3110 mod 41 = 28
This reduces all large numbers into a small range between 0 and 40.
What is Modular Inverse?
The modular inverse of a number answers this question:
βWhat number do I multiply by this value to get 1 (mod 41)?"
Mathematically, it is written as:
This means:
- Multiply
abyx - Take the result mod 41
- If the answer is 1, then
xis the modular inverse ofa
You can think of a modular inverse as the undo button for multiplication (when working modulo 41).
Example
The modular inverse of 3 mod 11 is 4, because:
Why inverses always exist here
- 41 is a prime number
- This means every number from 1 to 40 has a modular inverse modulo 41
- So we are guaranteed that an inverse exists (except when the value is 0)
Character Mapping
After finding the modular inverse, we convert it into a character using this mapping:
- 1-26: A-Z (alphabet)
- 27-36: 0-9 (digits)
- 37: _ (underscore)
Solution
Implementing the decryption
Hereβs the Python solution:
def decrypt_basic_mod2(ciphertext): # Character mapping # 1-26: a-z, 27-36: 0-9, 37: _ char_map = { 1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g', 8: 'h', 9: 'i', 10: 'j', 11: 'k', 12: 'l', 13: 'm', 14: 'n', 15: 'o', 16: 'p', 17: 'q', 18: 'r', 19: 's', 20: 't', 21: 'u', 22: 'v', 23: 'w', 24: 'x', 25: 'y', 26: 'z', 27: '0', 28: '1', 29: '2', 30: '3', 31: '4', 32: '5', 33: '6', 34: '7', 35: '8', 36: '9', 37: '_' }
plaintext = []
for num in ciphertext: # Step 1: Take mod 41 mod_result = num % 41
# we use Python 3.8+ built-in: pow(a, -1, 41) if mod_result == 0: # Edge case: if mod_result is 0, there's no inverse continue
inverse = pow(mod_result, -1, 41)
# Step 3: Map to character if inverse in char_map: plaintext.append(char_map[inverse])
return ''.join(plaintext)
# Parse the ciphertextciphertext = [104, 372, 110, 436, 262, 173, 354, 393, 351, 297, 241, 86, 262, 359, 256, 441, 124, 154, 165, 165, 219, 288, 42]
# Decryptmessage = decrypt_basic_mod2(ciphertext)print(f"Decrypted message: {message}")print(f"Flag: picoCTF{{{message}}}")Running the solution
The decrypted message is:
1nv3r53ly_h4rd_dadaacaaβ‘ Raikiriπ Flag pwned!
$ python solve.pyDecrypted message: 1nv3r53ly_h4rd_dadaacaaFlag: picoCTF{1nv3r53ly_h4rd_dadaacaa}Flag: picoCTF{br1nv3r53ly_h4rd_dadaacaa}
π‘ TL;DR / Lesson Learned
Modular inverse: Understanding that the modular multiplicative inverse is the key operation for decryption. Since 41 is prime, every non-zero number has an inverse.
Character mapping: The custom alphabet (1-26 = a-z, 27-36 = 0-9, 37 = _) is crucial for converting the numeric result back to characters.
Modular arithmetic: Taking mod 41 first reduces large numbers to a manageable range (0-40), making the inverse operation efficient.
Pythonβs pow() function: The three-argument
pow(a, -1, m)efficiently computes modular inverses (available in Python 3.8+), making the solution elegant and fast.