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 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.
I have an APK or an IPA
An app is an archive, and most of the answer is in metadata rather than in code. Read the manifest before you disassemble anything.
First move
Open it in the mobile analyzer. The manifest, the signing scheme, the DEX count and a secret scan across every text-like entry all come back in one pass, and one of them is usually enough.
A component is exported and guarded by no permission
That is the attack surface: any app on the device can reach it. Read what its intent-filter accepts and you have the entry point.
A component with an intent-filter and no android:exported attribute IS exported. Absent does not mean private.
APK and IPA analyzer: manifest, exported components, DEX xrefsandroid:debuggable is true
You can attach jdb or Frida without root. Hooking the check is almost always faster than defeating it statically.
APK and IPA analyzer: manifest, exported components, DEX xrefsA string in the DEX looks like a key, a URL or half a flag
Find where it is loaded, then read backwards from the method that loads it. The xref workbench answers that directly.
APK and IPA analyzer: manifest, exported components, DEX xrefsThe interesting method is `native`
The body is in a `.so` under `lib/`, not in the DEX. Extract it and treat it as an ELF.
ELF, PE and Mach-O binary analyzerAn iOS binary whose strings are all garbage
Check cryptid. A non-zero LC_ENCRYPTION_INFO means __TEXT is FairPlay-encrypted on disk and you are reading ciphertext.
APK and IPA analyzer: manifest, exported components, DEX xrefsAn `assets/` or `res/raw/` file that is not what its name says
Carve it. Challenge authors hide a second archive, an image or a database there.
File type identifier by magic bytes
If none of that fits: Check the signing block. A v1-only APK can be modified without breaking verification on older Android, and a challenge about repackaging is a challenge about exactly that.
I have a contract address or some EVM bytecode
There is no source, and there does not need to be. The compiler follows two conventions and both of them are readable straight out of the bytecode.
First move
Disassemble the runtime bytecode and read the dispatcher at the top. Those four-byte constants are the ABI, in the order the compiler emitted it.
A selector in the dispatcher resolves to a known signature
You have the function name and its argument types without any source at all.
EVM bytecode analyzer: disassemble, decode selectors, read storageThe flag is in a `private` variable
`private` is a compile-time visibility rule, not a secret. Every storage slot is readable on chain by anyone.
Value types pack several to a slot in declaration order, so read the slot as fields rather than as one number.
EVM bytecode analyzer: disassemble, decode selectors, read storageThe value you want is in a mapping
It is at keccak256(key . slot). Compute the hash and read that slot; nothing about it is hidden.
EVM bytecode analyzer: disassemble, decode selectors, read storageYou have the transaction that did the interesting thing
Decode its calldata against the signature. The selector is the first four bytes and the arguments are 32-byte aligned after it.
EVM bytecode analyzer: disassemble, decode selectors, read storageA seed phrase, a private key or an address to check
Derive it properly - BIP-39 to seed, BIP-32 to key, keccak to address - rather than trusting a checksum by eye.
EVM bytecode analyzer: disassemble, decode selectors, read storage
If none of that fits: Look for the constructor arguments appended after the deployment bytecode, and for a delegatecall - the logic you are reading may not be the logic that runs.
I have a memory dump or a disk image
The three questions a dump is asked are always the same: what was running, what was typed, and what was on disk.
First move
Run the native scan. It finds processes by pool tag rather than by symbol, so it needs no profile for the Windows build the dump came from - and it finds terminated processes a live process list no longer contains.
A process name you do not recognise
Look at its parent. A shell spawned by a document reader, or anything parented to an office application, is the story.
Memory dump, disk image and registry hive analysisA command line with `-enc`, `certutil`, `mshta` or a base64 blob
That is what was run. Decode the blob; it is usually one layer from the answer.
Windows keeps command lines as UTF-16, which a byte-oriented strings pass sees as nothing at all.
Memory dump, disk image and registry hive analysisA registry hive inside the dump
Carve it at the offset given and open it. Run keys, typed URLs, mounted devices and UserAssist all live there.
Memory dump, disk image and registry hive analysisA disk image rather than memory
ext, FAT and the NTFS $MFT all read here. A small file may be resident entirely inside its own MFT record.
Memory dump, disk image and registry hive analysisNothing at all is recognised
It may be a crash dump or a compressed acquisition rather than a raw image, and neither can be scanned in place.
Check the first bytes: a raw dump starts with page data, a Windows crash dump with PAGEDU64.
If none of that fits: Fall back to strings over the whole image with the flag pattern. A dump is enormous, so a targeted search beats reading, and the flag is frequently in a buffer no structure points at any more.
I have a capture of a signal rather than a file
A CSV of digital lines, a Flipper .sub, an .ir - all of them are a list of durations, and the durations name the protocol before you decode anything.
First move
Look at how many distinct pulse widths there are. Two means a bit encoding; a very long one at the start means a leader or a reset, and that is what identifies the protocol.
One line that idles high, with pulses at a regular width
UART. The shortest pulse is one bit time, so 104us is 9600 baud and 8.7us is 115200.
Bytes but no readable text usually means the capture is inverted - try that before anything else.
Logic analyzer decoder: UART, I2C, SPI and 1-WireTwo lines where one changes while the other is high
I2C. That is a start or stop condition, and it is the one thing that may not happen during data.
Logic analyzer decoder: UART, I2C, SPI and 1-WireThree or four lines, one a regular clock
SPI. The extra line is chip select, and without it there is no framing.
If three of the four clock modes decode identically and one does not, the odd one out is not the right one.
Logic analyzer decoder: UART, I2C, SPI and 1-WireA 9ms mark followed by a 4.5ms space
NEC infrared. Address, inverted address, command, inverted command.
Flipper .sub and .ir decoder: RF and infrared capturesA 433MHz capture that decodes to 24 bits
Princeton. A static code - identical every press - so capture and replay is the answer and there is nothing to break.
Flipper .sub and .ir decoder: RF and infrared capturesA 433MHz capture that decodes to 66 bits
KeeLoq. A rolling code whose hopping half changes every press, so replaying the capture will be rejected and the challenge is the cipher.
Flipper .sub and .ir decoder: RF and infrared captures
If none of that fits: Check whether it is a firmware image rather than a signal. A `.bin` from the same challenge is usually where the protocol is implemented, and the entropy profile will say whether there is anything unpackable in it.