DEV Community

CTFDojo
CTFDojo

Posted on Originally published at ctfdojo.com

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 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");
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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{...}
Enter fullscreen mode Exit fullscreen mode

🚩 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.

Top comments (0)