Description
A list of integers is provided along with instructions to take each number mod 37 and map it onto a custom alphabet (A-Z, digits, underscore). Implement the mapping to decode the hidden flag.
Setup
Grab the message file and convert the space-separated numbers into a Python list.
Apply modulo 37 to each entry, then treat 0-25 as A-Z, 26-35 as digits, and 36 as an underscore.
Concatenate the recovered characters and wrap them with picoCTF{...}.
wget https://artifacts.picoctf.net/c/128/message.txtcat message.txtsed -e "s/^/[/" -e 's/ *$//' -e "s/$/]/" -e "s/ /, /g" message.txtpython3 mod1.pySolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Normalize the inputObservationI noticed message.txt contains a long line of space-separated integers, which suggested parsing it programmatically via sed and Python's split rather than transcribing by hand, to avoid silent transcription errors.Wrap the space-separated numbers as a Python list with one sed invocation, then iterate.What didn't work first
Tried: Manually copying the numbers from the file and typing them into a Python script by hand.
This works for small inputs but is error-prone and slow when the list has dozens of entries. A missed digit or an extra space breaks the decode silently. Using wget plus sed or Python's open/read/split pipeline is faster and eliminates transcription errors.
Tried: Pasting the raw space-separated string directly as a Python variable without any conversion.
Python treats a bare string of space-separated digits as a string, not a list of integers. Calling int() on the whole string fails. You need .split() to break it into tokens and int() on each token before doing arithmetic.
Learn more
One
sedinvocation, four-eexpressions applied in order to the same stream:-e "s/^/[/"prepends[,-e 's/ *$//'trims trailing spaces,-e "s/$/]/"appends], and-e "s/ /, /g"rewrites every space as,. The result is a valid Python list literal in one pass.You could just open the file in Python and call
.split()- the sed trick is a habit, not a requirement. Useful when you want a single shell-pipeline solution.Step 2
Map each valueObservationI noticed the problem statement explicitly specifies modulo 37 and an alphabet of exactly 37 characters (A-Z, 0-9, underscore), which indicated that taking each number mod 37 and indexing into that ordered character set would decode every integer to its corresponding letter or digit.For every numbern, computen % 37to keep it inside the alphabet range, then index into the character setABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.What didn't work first
Tried: Mapping the raw numbers directly onto the alphabet without applying mod 37 first.
The numbers in message.txt are much larger than 36, so direct indexing into a 37-character string raises an IndexError or produces garbage characters. The mod step is what squashes the value into the valid range 0-36 before the lookup.
Tried: Building the alphabet as '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_' (digits first, then letters).
The challenge specifies letters first (A=0 through Z=25), then digits (0=26 through 9=35), then underscore (=36). Reversing digits and letters causes every decoded character to be wrong. Always match the order defined in the problem statement.
Learn more
Modular arithmetic reduces a number to the remainder after dividing by a modulus. Here the modulus is 37 because the custom alphabet has exactly 37 characters (26 letters + 10 digits + 1 underscore). Any integer, no matter how large, maps to a value in
0..36after% 37.The encoding rule is a simple positional table:
index 0..25 -> 'A'..'Z' index 26..35 -> '0'..'9' index 36 -> '_' Worked example for n = 306: 306 / 37 = 8.27... (integer quotient = 8) 8 * 37 = 296 306 - 296 = 10 (remainder) alphabet[10] = 'K' More: 617 % 37 = 25 -> 'Z' (16 * 37 = 592, 617 - 592 = 25) 28 % 37 = 28 -> '2' (digit slot: 26 + 2)37 is prime, which means every non-zero value has a multiplicative inverse modulo 37 - a property the follow-up challenge basic-mod2 leans on.
Python's
%follows the divisor's sign: with a positive modulus the result is always in[0, m), even for negative dividends. C does not guarantee this; in C the sign can carry from the dividend, so-1 % 37may give -1 instead of 36. A single comprehension -[alphabet[n % 37] for n in numbers]- is enough here.Step 3
Assemble the flagObservationI noticed the decoded characters form a plaintext inner string, which suggested joining them and wrapping the result in the standard picoCTF{} format that the platform's checker requires.Build a string starting withpicoCTF{...}to produce the final answer.What didn't work first
Tried: Submitting just the decoded inner text without the picoCTF{} wrapper.
The platform's flag checker expects the full picoCTF{...} format. Submitting only the inner characters is rejected even though the decode is correct. Always wrap the result in the standard prefix before submitting.
Learn more
Once every integer is decoded into its character, joining the list with
''.join(chars)and wrapping it in the standardpicoCTF{}format gives the submission-ready flag. This final assembly step is trivial but teaches an important habit: always verify the structure of your output matches what the challenge expects before submitting.The
picoCTF{}wrapper is a flag format convention. Most CTF platforms use a consistent prefix (likeflag{},CTF{}, or competition-specific variants) so automated checkers can validate submissions. Recognizing these patterns helps you quickly confirm you decoded something correctly even before you know what the inner text means.
Interactive tools
- Cipher Identifier & Auto-DecoderPaste any ciphertext and the tool auto-runs every common decoder (base64, hex, Morse, ROT, Atbash, Bacon, binary, decimal, URL) and ranks the results by English-likeness.
Flag
Reveal flag
picoCTF{R0UND_N_R0UND_B6B...}
Simple modular arithmetic plus a custom alphabet turns the numeric sequence back into the plaintext flag.