Challenges
Solve challenges to earn points and climb the scoreboard
Titanic: A Data Tragedy
mediumforensics
Overview
The RMS Titanic passenger manifest has been digitized and loaded into an interactive SQL database. A survivor count has been encoded as a flag.
Use the SQL Playground below to query the dataset.
Dataset
The table contains records for 891 passengers. Start by exploring:
SELECT * FROM dataset LIMIT 5;
Schema:
| Column | Type | Description |
|---|---|---|
PassengerId |
INT | Unique passenger identifier |
Survived |
INT | 0 = did not survive, 1 = survived |
Pclass |
INT | Ticket class (1 = First, 2 = Second, 3 = Third) |
Name |
TEXT | Passenger full name |
Sex |
TEXT | male or female |
Age |
FLOAT | Age in years |
Fare |
FLOAT | Ticket price paid |
Mission
How many passengers survived? The flag is hctf{N} where N is the survivor count.
-- Start here:
SELECT COUNT(*) FROM dataset WHERE Survived = ???;
Hint: Filter on the
Survivedcolumn. Survivors have a value of 1.
Titanic: Women and Children First
hardforensics
Overview
Dig deeper into the Titanic passenger manifest. This challenge requires more targeted analysis of the survivors.
Use the SQL Playground below to query the dataset.
Dataset
Same Titanic dataset as the previous challenge. The dataset table is pre-loaded.
DESCRIBE dataset;
SELECT DISTINCT Sex FROM dataset;
Mission
"Women and children first" — but how many women actually made it?
The flag is hctf{N} where N is the number of female passengers who survived.
-- Build your query step by step:
SELECT COUNT(*) FROM dataset
WHERE Survived = 1
AND Sex = '???';
SQL Tips
| Clause | Purpose |
|---|---|
WHERE col = val |
Filter rows by exact value |
AND |
Combine multiple conditions |
COUNT(*) |
Count matching rows |
Hint: String comparisons in SQL are case-sensitive. Check the exact value with
SELECT DISTINCT Sex FROM datasetfirst.
Cookie Monster
easyweb
Overview
A mysterious HTTP cookie holds the key. Someone left a secret value in a response header — can you spot it?
Objective: Find the hidden value stored in a browser cookie.
What You Need to Know
HTTP cookies are set via the Set-Cookie response header:
Set-Cookie: session=abc123; HttpOnly; Path=/
Not all cookies are visible in document.cookie — HttpOnly cookies are only visible via DevTools or a raw HTTP client.
Tools
- Browser DevTools → Network tab → Response Headers
curl -v http://target/to see raw headers- Burp Suite or any HTTP proxy
The flag is hiding right in front of you — you just need to know where to look.
Caesar's Secret
easycrypto
Overview
Julius Caesar allegedly invented this cipher to protect his military communications. The message looks scrambled, but the key is simpler than you think.
Ciphertext:
kfwi2{mxolxv_z0xog_eh_su0xg}
Background
The Caesar cipher shifts each letter by a fixed number of positions. With only 26 possible keys, brute force is trivial.
| Plaintext | Shift | Ciphertext |
|---|---|---|
| A | +3 | D |
| H | +3 | K |
| Z | +3 | C |
Numbers and special characters are not shifted — only letters.
Solve It
Try each shift from 1 to 25, or use CyberChef.
Hint: ROT13 is shift 13. This one uses a different key.
Base64? No.
mediumcrypto
Overview
It looks like Base64, but something is off. Peel back the layers one by one.
Encoded payload:
Vm0wd2QyUXlVWGxWV0d4V1YwZDRWMVl3WkRSV01WbDNXa1JTV2xZd01UQlhhMUpUWWtaS
(truncated — full payload in the challenge files)
Encoding Layers
The data has been encoded multiple times using different schemes:
- Base64 — the outer shell
- Hex encoding — lurking beneath
- ROT47 — the final veil
import base64, codecs
data = b"..."
step1 = base64.b64decode(data)
step2 = bytes.fromhex(step1.decode())
step3 = codecs.decode(step2, "rot_13") # hint: not exactly ROT13
print(step3)
Hint:
fileandxxdare helpful for identifying encoding types at each layer.
Hidden in Plain Sight
easyforensics
Overview
This image looks completely normal. But data lurks beneath the surface — examine every byte.
What is Steganography?
Steganography is the practice of hiding secret information within ordinary, non-secret data.
| Technique | Description |
|---|---|
| LSB encoding | Data hidden in the least significant bits of pixel values |
| File appending | Secret data appended after the image EOF marker |
| Metadata embedding | Data hidden in EXIF or other metadata fields |
Tools to Try
# Check metadata
exiftool image.png
# Search for embedded strings
strings image.png | grep -i flag
# LSB steganography tools
zsteg image.png
steghide extract -sf image.png
Hint: The flag isn't in the visible pixels — think about what's hiding in the least significant bits.
Ghost in the Binary
mediummisc
Overview
A stripped binary with anti-debug tricks. Find the hidden validation logic.
The binary accepts a passphrase and validates it against a hardcoded secret:
$ ./challenge
Enter secret: hello
Wrong!
$ ./challenge
Enter secret: ???
Correct! Flag: hctf{...}
Static Analysis
# Identify the binary
file challenge
objdump -d challenge | grep -B5 -A10 "cmp"
# Search for string constants
strings challenge | grep -E "hctf|flag|secret"
Dynamic Analysis
# Trace library calls (often reveals strcmp arguments)
ltrace ./challenge
# Debug with GDB
gdb ./challenge
(gdb) break strcmp
(gdb) run
Hint: The anti-debug technique uses
ptrace(). Set a breakpoint after the check, or patch the binary.
SQL Injection 101
mediumweb
Overview
The login form looks secure at first glance — but one query was not parameterized, and that's all it takes.
The Vulnerability
The application builds SQL like this:
$query = "SELECT * FROM users
WHERE username='" . $username . "'
AND password='" . $password . "'";
Classic Payloads
-- Bypass authentication
' OR '1'='1' --
-- UNION-based data extraction
' UNION SELECT null, table_name, null FROM information_schema.tables --
-- Blind boolean-based
' AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin')='a' --
Your Target
The flag is stored in a table called secrets. Use a UNION injection to read the secret_value column.
Tip: First determine the number of columns with
ORDER BY N --, then craft your UNION.
XOR Master
hardcrypto
Overview
A file was XOR-encrypted with a short repeating key. Frequency analysis and known-plaintext attacks will crack it.
XOR Refresher
XOR with a repeating key is a classic (but weak) stream cipher:
plaintext: H e l l o
key: k e y k e
ciphertext: 0x23 0x00 0x15 0x07 0x0a
Key property: A XOR B XOR B = A — XOR is its own inverse.
Known-Plaintext Attack
If the flag format is known (hctf{), the first bytes of the key are:
ciphertext = bytes.fromhex(open("challenge.bin", "rb").read().hex())
known = b"hctf{"
key_start = bytes(a ^ b for a, b in zip(ciphertext, known))
print(f"Key starts with: {key_start}")
Finding the Key Length
Try key lengths 1–16. The Index of Coincidence spikes at the correct length.
Hint: Once you have the key,
bytes(a ^ b for a, b in zip(ciphertext, key * 999))decrypts everything.
Memory Forensics
hardforensics
Overview
A memory dump was captured from a compromised server. Somewhere in 2 GB of data, the attacker left their backdoor credentials.
Tools
Volatility is the standard framework for memory forensics:
# Identify OS profile
volatility -f memory.raw imageinfo
# List running processes
volatility -f memory.raw --profile=LinuxDebian pslist
# Find network connections
volatility -f memory.raw --profile=LinuxDebian netscan
# Dump process command lines
volatility -f memory.raw --profile=LinuxDebian cmdline
What to Look For
The attacker installed a bind shell with credentials encoded in process arguments:
# Search raw memory for flag-shaped strings
strings memory.raw | grep -E "hctf\{.*\}"
# Scan for base64-encoded credentials
strings memory.raw | grep -E "[A-Za-z0-9+/]{20,}={0,2}" | base64 -d 2>/dev/null
Hint: Look for processes spawned by
bashwith unusual parent relationships. The credentials are Base64-encoded inargv.