Skip to main content

Password Profiler picoCTF 2026 Solution

Use personal details and open-source intelligence to crack a hashed password and retrieve the flag.

Published: March 20, 2026Updated: September 20, 2026

Description

We intercepted a suspicious file from a system, but instead of the password itself, it only contains its SHA-1 hash. Using OSINT techniques, you are provided with personal details about the target. Generate a custom password list and recover the original password by matching its hash. Download: userinfo.txt, hash.txt, and check_password.py.

Download userinfo.txt, hash.txt, and check_password.py.
Read userinfo.txt to understand the target's personal details.
bash
cat userinfo.txt
bash
cat hash.txt
bash
cat check_password.py

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1Profile the target
    Observation
    The challenge is OSINT-driven and ships a userinfo.txt full of personal details. Profile the target before generating any password candidates.
    Read userinfo.txt carefully. It contains personal details like name, birthdate, favourite things, pet names, or other information people commonly use in passwords.
    bash
    cat userinfo.txt
    What didn't work first

    Tried: Trying to crack the hash directly with hashcat or john using a generic rockyou.txt wordlist.

    rockyou.txt holds 14 million common passwords and none of them are built from this person's initials and birthdate. The challenge is designed around profiling, so the password comes from the target's own details rather than a public leak. CUPP generates candidates no generic list contains.

    Tried: Skimming userinfo.txt quickly and only entering the name and birthdate into CUPP, ignoring fields like nickname or pet name.

    CUPP mutates every field you give it, combining them with leet substitutions, separators, and year suffixes. Leave fields blank and the wordlist shrinks, possibly dropping the exact combination you need. Read every line of userinfo.txt and enter all of it.

    Learn more

    Password profiling is an OSINT (Open Source Intelligence) technique used in penetration testing and red team engagements. Instead of brute-forcing all possible passwords, you generate a targeted wordlist based on information specific to the target: their name, birthday, spouse's name, pet's name, favourite sports team, employer, and common patterns like appending birth years or "123" suffixes.

    Research shows that people overwhelmingly choose passwords based on memorable personal information. A 2019 study found that over 50% of users choose passwords containing their name or birthdate. Attackers who know a target's personal details can often crack their password with a few hundred candidates, whereas a brute-force attack against the same password might take millions of years.

    In real engagements, profiling information comes from LinkedIn profiles, social media, company websites, and data breaches. This challenge simulates receiving that information in a file - in practice, gathering it requires careful OSINT research using tools like Maltego, SpiderFoot, or manual social media enumeration.

  2. Step 2Generate a custom wordlist with CUPP and crack the SHA-1
    Observation
    There is a SHA-1 hash and a pile of personal details, not a generic password list. CUPP's interactive mode builds a targeted wordlist from the name, nickname, and birthdate.
    Clone and run CUPP in interactive mode. Enter the personal details from userinfo.txt when prompted. CUPP generates a targeted wordlist (saved as alice.txt or similar). Copy it to passwords.txt, then run check_password.py to find the matching SHA-1 hash.
    bash
    git clone https://github.com/Mebus/cupp.git
    bash
    cd cupp && python3 cupp.py -i
    bash
    # Fill in the details from userinfo.txt interactively:
    bash
    # Name: Alice Johnson
    bash
    # Nickname: A.J.
    bash
    # Birthdate: 15071990  (CUPP asks for DDMMYYYY, no separators)
    bash
    # (press Enter for fields you don't know)
    bash
    # CUPP saves the wordlist as alice.txt (or similar)
    bash
    cp alice.txt ../passwords.txt
    bash
    cd .. && python3 check_password.py
    bash
    # The correct password is something like Aj_15901990

    Expected output

    Password found: Aj_15901990
    picoCTF{Aj_...}
    What didn't work first

    Tried: Running CUPP without the -i flag (e.g. python3 cupp.py -l) to download a pre-built wordlist instead of generating a custom one.

    The -l flag downloads generic wordlists, none of which contain a password built from this person's initials and birthdate. The -i flag is the one that prompts for personal details and generates a targeted list.

    Tried: Copying the wordlist to a filename other than passwords.txt (e.g. wordlist.txt) and running check_password.py.

    check_password.py opens the wordlist by a hardcoded name, so a differently named file raises a not-found error or finishes silently with no match. That is why the solution copies the generated list to the expected name.

    Learn more

    SHA-1 is a cryptographic hash function that produces a 160-bit (40 hex character) digest. It is no longer considered secure for digital signatures (broken in 2017 by Google's SHAttered attack), but understanding it is important because it's still found in older systems and CTF challenges. SHA-1's main weakness for password storage is that it's fast: a GPU can compute billions of SHA-1 hashes per second, making dictionary attacks trivial.

    The correct tool for password hashing is a purpose-built slow hash function like bcrypt, scrypt, Argon2, or PBKDF2 with a high iteration count. These deliberately take milliseconds to compute, making dictionary attacks tens of thousands of times slower. They also include a salt (a random value mixed into the hash) to prevent pre-computed rainbow table attacks. For a deeper tour of the cracking workflow see Hash cracking for CTF; for the Python idioms used by check_password.py see Python for CTF.

  3. Step 3Run check_password.py to retrieve the flag
    Observation
    check_password.py ships alongside the hash and the userinfo file, so it exists to run the comparison loop over your wordlist and print the flag on a match.
    Copy your generated wordlist to passwords.txt (if not already done), then run check_password.py. The script reads from passwords.txt automatically and prints the flag when it finds the matching password.
    python
    python3 check_password.py
    What didn't work first

    Tried: Running check_password.py from a different working directory than where passwords.txt was saved.

    The script opens the wordlist by relative path, meaning the current working directory. Run it from the cupp subdirectory with the list in the parent and it raises a not-found error and exits. Put both in the same place.

    Tried: Writing a manual Python loop to hash each line of the wordlist and compare it to the SHA-1 from hash.txt instead of using check_password.py.

    That works in principle and usually fails in practice, because each wordlist line carries a trailing newline and hashing the candidate with it produces a different digest. check_password.py strips whitespace before hashing, which is exactly the step hand-rolled scripts forget.

    Learn more

    Hash cracking fundamentally works by preimage attack: given a hash H, find any input m such that hash(m) = H. For targeted attacks like this, you compute hash(candidate) for each candidate in your wordlist and compare against the target hash. This is conceptually simple but the quality of the wordlist determines success.

    From a defensive standpoint, this challenge illustrates why password complexity rules alone are insufficient. "Fluffy123!" satisfies most complexity requirements (upper, lower, number, special character, 9+ characters) but is trivially cracked by any attacker who knows your pet's name. Password managers generating random strings are the correct solution because they make password profiling attacks impossible.

Interactive tools
  • Hash IdentifierIdentify unknown hash types by length and prefix. Covers MD5, SHA-1, SHA-256, SHA-512, bcrypt, NTLM, and more.
  • Checksum CalculatorCompute CRC32, MD5, SHA-1, SHA-256, SHA-384, and SHA-512 hashes for text or uploaded files. Verify against known hashes.
  • Regex TesterTest regular expressions against a string with live match highlighting, flag toggles, and common CTF pattern shortcuts.

Flag

Reveal flag

picoCTF{Aj_...}

Use CUPP (Common User Passwords Profiler) in interactive mode with the target's personal details from userinfo.txt to generate a wordlist. Run check_password.py against it to find the matching SHA-1 hash. The password combines initials and birthdate (e.g. Aj_15901990).

Key takeaway

People build passwords out of things that mean something to them: names, birthdates, pets, favorites, usually with a simple suffix or a character substitution. A wordlist built from OSINT about one person collapses the search space from billions to hundreds, and a fast hash like SHA-1 falls immediately. Only a slow hashing function and randomly generated credentials from a password manager close this off.

Related reading

Useful tools for General Skills

Where to go next