Decision guide
Everything else on this site is organised by technique, which only helps once you know which technique you need. This page is organised by symptom. Each tree starts from something you can observe without knowing anything, and every branch ends at a tool.
These are the seven situations that actually recur - not an attempt at completeness. A tree that covers everything helps nobody at 2am.
I have a string of text and no idea what it is
The most common opening in any CTF. The alphabet tells you almost everything before you decode anything.
First move
Run the cipher identifier. It applies every decoder, including multi-layer cascades, and ranks by English-likeness - which resolves most wrappers in one action.
Letters, digits, `+` and `/`, length divides by 4
Base64. If you see `-` and `_` instead, it is Base64-URL.
Base64 decoder and encoderOnly `0-9a-f`, even length
Hex - unless the length is 32, 40, or 64, in which case it is probably a hash.
Check the length against digest sizes before decoding it as text.
Hex to text converterUppercase letters and digits 2-7 only
Base32. The missing 0, 1, 8, and 9 are the giveaway.
Base32 decoder and encoderOnly ones and zeroes
Binary. Group by 8 first, then by 7 if that fails.
Binary to text converterDots and dashes
Morse. The separators matter more than the symbols.
Morse code translatorReadable letters, wrong words, normal-looking letter frequencies
A transposition - the letters are right and only the order is wrong.
Rail fence cipher solverReadable letters, wrong words, skewed letter frequencies
A substitution. Try Caesar first, then the general solver.
Caesar cipher decoder with automatic shift detectionThree segments separated by dots, starting `eyJ`
A JSON Web Token. The `eyJ` is Base64 for `{"`.
JWT decoder and signature verifierContains `n =`, `e =`, `c =` or a very long integer
RSA parameters. Read them before choosing an attack.
RSA decryption and attack runner
If none of that fits: The input is probably bytes rather than text. Decode whatever wrapper it is in, check the first bytes against the magic byte table, and move to File mode.
I have a file and it will not open, or opens and shows nothing
The extension is a suggestion. The first bytes are the fact.
First move
Identify the real type from its signature, and scan the whole file for embedded signatures rather than just reading the header.
The header does not match the extension
The file was renamed. Treat it as what the bytes say it is.
File type identifier by magic bytesA second file signature appears partway through
A polyglot or an appended archive - very common, and usually the whole challenge.
`PK\x03\x04` inside an image means a ZIP is glued on the end.
Magic byte and file signature tableThe header is zeroed or subtly wrong
A deliberately corrupted file. Write the correct magic bytes back.
Magic byte and file signature tableIt opens fine and shows nothing interesting
The data is hidden inside rather than appended. Sweep the steganography techniques.
Automatic steganography solverIt is a PNG that renders wrong or not at all
Check the chunk CRCs - a bad CRC in IHDR usually means the dimensions were edited.
PNG chunk analyzerHigh entropy throughout, no readable strings
Compressed or encrypted. Look for the container that should have unpacked it.
Hex viewer and hexdump
If none of that fits: Run strings with a low minimum length and read everything. A surprising share of file challenges are solved by output nobody bothered to scroll through.
I have something that looks like a hash
Identification decides whether cracking is the intended path or a trap. Do it before spending any compute.
First move
Identify the format by length and prefix, and get the hashcat mode number.
32, 40, or 64 hex characters, no prefix
A fast unsalted digest. Wordlist cracking is on the table.
In-browser hash cracker with rule transformsStarts `$2a$`, `$2b$`, or `$2y$`
bcrypt. Deliberately slow - cracking is almost certainly not the intended path.
Find the password elsewhere in the challenge and use the hash only to confirm it.
Hash identifier with hashcat mode lookupStarts `$1$`, `$5$`, or `$6$`
A crypt(3) variant from /etc/shadow. The number names the algorithm.
Hash identifier with hashcat mode lookup64 characters but SHA-256 does not match a known value
Try SHA3-256, Keccak-256, and BLAKE2s - all the same length, all different.
SHA-3, BLAKE2, Keccak and RIPEMD calculatorThe application computes `hash(secret || message)` and shows you the result
A length extension attack, not a cracking problem.
Hash length extension attack
If none of that fits: It may not be a hash. A fixed-length hex string can also be a key, an IV, or a raw digest of something you are supposed to compute yourself.
I have RSA parameters
Textbook RSA is not breakable. The challenge is breakable because one parameter was chosen badly - so read the parameters before running anything.
First move
Look at the size of n, the size of e, and how many ciphertexts you were given. Those three facts pick the attack.
n is small - under about 256 bits
Just factor it.
RSA decryption and attack runnerp and q are close, or the generator used `nextprime(p)`
Fermat factorization terminates in a handful of steps.
Fermat factorization for close RSA primese is enormous, comparable in size to n
d is small. Wiener’s attack recovers it from continued fractions.
Wiener’s attack on small RSA private exponentse = 3 and the message is short
No factoring needed - take the exact integer cube root of c.
Use an exact integer root. A floating-point root silently loses the digits that matter.
Modular arithmetic and number theory toolkitThree ciphertexts, three different moduli, e = 3
Hastad broadcast: CRT the ciphertexts, then take the cube root.
Hastad broadcast attack on RSASame n, two different e
Common modulus. Bezout coefficients recover the plaintext with no factoring.
RSA common modulus attackTwo moduli provided at once
Compute their GCD first. If they share a prime, it is instant.
Modular arithmetic and number theory toolkit
If none of that fits: Look for partial information - known high bits of p, a leaked fragment of d, a decryption oracle. Those point at Coppersmith and lattice methods rather than classical attacks.
I have a URL and a login page
The interesting endpoint is rarely the one you were given. Enumerate first, then read what the application trusts.
First move
Fetch robots.txt and probe for common paths. `.git/HEAD` is a single request that occasionally ends the challenge on the spot.
`.git/HEAD` returns content
The whole repository is readable, including deleted secrets in history.
Exposed .git directory dumperA session cookie with three dot-separated segments
A JWT. The header names the attack.
JWT decoder and signature verifierA session cookie with dots and a leading period
A Flask session - zlib-compressed, signed, and readable.
Flask session cookie decoderA parameter reflected in the page
Test whether the characters survive unescaped before building a payload.
Web attack payload catalog`{{7*7}}` comes back as 49
Server-side template injection. Identify the engine before escalating.
Web attack payload catalogA single quote changes the response and two quotes restore it
SQL injection - that is the parser talking.
Web attack payload catalogThe response includes an object id you did not choose
Try someone else’s id. Broken access control is the most common real-world bug.
HTTP request replayer
If none of that fits: Read the client-side JavaScript in full. Endpoints, API keys, and the entire logic of the check are frequently sitting in a bundle nobody expected you to read.
I have an executable
The protections decide the exploit. Read them before opening a disassembler, because half the approaches are already ruled out.
First move
Check the architecture, the protections, and the imports. Then run strings - it solves more challenges than it has any right to.
NX disabled
Shellcode on the stack works. This is the simplest case.
Shellcode assembler and libraryNX enabled, no PIE
ROP at fixed addresses. Find gadgets and build a chain.
ROP gadget finderPIE enabled
You need an address leak before anything else is possible.
Format string exploit builderA stack canary is present
A plain overflow aborts. Leak the canary or find a different primitive.
ELF, PE and Mach-O binary analyzer`printf` is called on user input
A format string bug - an arbitrary read and write in one.
Format string exploit builder`malloc` and `free` on user-controlled sizes
A heap challenge. Which attacks apply depends on the glibc version.
Glibc heap exploitation helperIt is a .class, .pyc, or .wasm
Not native code - these retain names and strings and read close to source.
Python .pyc bytecode disassembler
If none of that fits: If the binary just checks input against a computed value, do not reverse the algorithm - set a breakpoint at the comparison and read the operand.
I have a packet capture
A capture is a story with one relevant thread. Look for the anomaly rather than reading the traffic.
First move
Look at the protocol breakdown first. Whatever is unusual - not whatever is largest - is the challenge.
HTTP traffic present
Extract every transferred object. The flag is often inside one of them.
PCAP analyzer for CTF network forensicsUnusually long or numerous DNS queries
DNS tunnelling. Decode the concatenated labels in query order.
Base32 decoder and encoderFTP, Telnet, or HTTP Basic auth
Credentials in the clear. Read them straight out of the stream.
PCAP analyzer for CTF network forensicsICMP packets with large payloads
Data smuggled in ping payloads - a classic exfiltration channel.
PCAP analyzer for CTF network forensicsAlmost entirely TLS, and keys were provided
Decrypt with the SSLKEYLOGFILE or private key, then triage normally.
PCAP analyzer for CTF network forensicsAlmost entirely TLS, and no keys were provided
The answer is metadata - SNI hostnames, certificates, sizes, timing.
PCAP analyzer for CTF network forensics
If none of that fits: Check the capture’s own timestamps and packet ordering. Occasionally the payload is the timing rather than the content.