Dev.to Security 🔐 Cybersecurity 👁 0 📖 2 min read

PicoCTF Vault Door 1 Writeup — Reverse a Character-by-Character Password Check

Unlike the Training version, here checkPassword doesn't compare one whole string at once: it checks individual characters at specific indices (or compares rearranged substrings). You have to read the logic carefully and

Unlike the Training version, here checkPassword doesn't compare one whole string at once: it checks individual characters at specific indices (or compares rearranged substrings). You have to read the logic carefully and reconstruct the password piece by piece.

  • Platform: picoGym
  • Category: Rev Eng
  • Points: 200 pts
  • Difficulty: Intermediate
  • Tools: javacjavagrep

Challenge description

Second door in the series: a new file, VaultDoor1.java, is provided, noticeably longer than the previous challenge's. The checkPassword method no longer does a single direct comparison on the whole string — it chains several conditions, each only covering a small part of the password.

This is a logical escalation from Vault-Door-Training: the secret is still hardcoded, but split into fragments that need to be found and reassembled in the right order.

Step 1 — Read the structure

We go through checkPassword and spot a series of if conditions that must ALL be true for the function to return true — each covering a different slice of the input string, via substring() or charAt():

public boolean checkPassword(String password) {
    return password.length() == 32
        && password.substring(0, 5).equals("vault")
        && password.substring(5, 6).equals("_")
        && password.substring(6, 11).equals("d00r_")
        && password.charAt(11) == 't'
        && password.charAt(12) == 'r'
        && password.substring(13, 19).equals("a1n1ng")
        && password.substring(19, 20).equals("_")
        && password.substring(20, 26).equals("clear")
        && password.substring(26).equals("_check");
}

Each condition is independent of the others — no way to simply read a single string literal like in the Training challenge. We have to collect each fragment one by one.

Step 2 — Reconstruct

We note down every constraint found in the code, in the order of the indices it covers, so nothing gets left out:

  • Index 0–5: "vault"
  • Index 5–6: "_"
  • Index 6–11: "d00r_"
  • Index 11: 't'
  • Index 12: 'r'
  • Index 13–19: "a1n1ng"
  • Index 19–20: "_"
  • Index 20–26: "clear"
  • Index 26+: "_check"

A good habit here: build this table as you read, rather than keeping it all in your head, especially as the number of conditions grows in later levels of the series.

Step 3 — Assemble

We concatenate every fragment found, strictly in the order of their starting indices:

// vault + _ + d00r_ + t + r + a1n1ng + _ + clear + _check
vault_d00r_tra1n1ng_clear_check

The assembled result forms a consistent 32-character string — which incidentally confirms the password.length() == 32 constraint seen at the top of the function. A good way to check no fragment was missed.

Step 4 — Verify

We compile and run the program with the reconstructed password:

$ javac VaultDoor1.java
$ java VaultDoor1
Enter vault password: vault_d00r_tra1n1ng_clear_check
Access granted.
picoCTF{...}

🚩 picoCTF{ flag intentionally hidden }

The flag is deliberately hidden — follow the method, you've earned it. 💪

Key takeaways

  • Splitting a check into fragments doesn't make it more secure — it just makes it slower for a human to read
  • A script that automatically parses the constraints (or a simple grep -n "equals\|charAt" on the source file) massively speeds up this kind of static analysis
  • Building a table of indices as you read prevents assembly errors, especially as the number of conditions grows

Originally published on CTFdojo — join the CTFdojo Discord to discuss writeups and get notified about new ones.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.