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

I locked myself out of my own KeePass vault. Here is what I built to recover it.

A while ago I locked myself out of my own KeePass database. I did not remember the master password as a string. I remembered pieces of it: a base word, a number that meant something, a symbol I liked at the time, and the

A while ago I locked myself out of my own KeePass database. I did not remember the master password as a string. I remembered pieces of it: a base word, a number that meant something, a symbol I liked at the time, and the vague sense that I had stuck them together in some order with some capitalization. I wrote two throwaway scripts that recombined those pieces until one opened the vault, and they got my data back. keepass-kdbx-recover is that idea rebuilt as a real tool.

It does not break the encryption. It guesses, and a guess only lands if the password is within reach. Fragments mode turns "I remember the pieces" into an organized search you can stop, narrow, and run again, and between runs you can tighten the fragments or raise the minimum to shrink the space. The other two modes do not lean on memory at all: a mask brute-forces the positions you mark as unknown, and a wordlist tries a list you already have, so either can turn up a weak or common password you never really knew. What stays out of reach is a strong master password under a modern KDF, where the search space and the cost per guess are too large to finish. That is the part KeePass is built to guarantee.

Repo (AGPL-3.0): https://github.com/HighMarck17/keepass-kdbx-recover

This is for databases you own or are authorized to recover. The disclaimer is wired into the CLI, the GUI and the generated config, not just written in the README.

Recombining the pieces you remember

The mode the tool is really built around is fragments. You hand it the pieces you remember and it rebuilds the candidates from them. Say you remember the word, a year and a symbol, but not the order or the exact casing:

keepass-kdbx-recover run --db vault.kdbx --fragments "sun,flower,42" --permute

Four knobs decide how wide it searches:

  • --min-fragments sets the smallest number of pieces a candidate has to use, so you can say "at least two of these, maybe all three". It tries every subset from that size up to all of them.
  • --permute tries every order of each subset. Without it, the order you typed is the order it keeps.
  • --case-variants adds the lower, UPPER and Capitalized form of each piece, because "Flower" and "flower" are different passwords and you probably do not remember which one you used.
  • in the config file, fragments.fixed_prefix pins one piece to the front, for when you are sure the password starts with your base word even if the rest is a blur.

This space gets big fast

Every knob multiplies the work. With n pieces, subsets of size r, permutations on and case variants on, the number of candidates grows like perm(n, r) * 4^r. Three pieces is nothing. Eight pieces with permutations and case variants is in the billions, and on a slow key derivation function (more on that below) a few billion candidates is not an afternoon.

So instead of pretending the space is small, the tool does two things:

  • It computes a cheap upper bound on the candidate count up front, with plain combinatorics, without generating a single password. That is what lets it tell you whether it even knows the total.
  • The 500,000 threshold only decides whether it can show you an exact total and percentage. Under that bound it enumerates the deduplicated candidates once before it starts, so the progress bar shows a real percentage and an ETA. Over it, it skips that pre-count and runs without a known total, reporting how many candidates it has tried rather than how far along it is. Either way the fragment candidates are deduplicated as they are generated, so a repeat is never tested twice. The cap changes what it can tell you about progress, not the work it does.

In practice, a few well-remembered pieces beat throwing everything you can think of at it with every switch on. The switches exist for when you genuinely do not remember the order or the casing, not as defaults.

Before you start, check what each guess costs

How long the search takes depends on one thing you did not choose: the key derivation function (KDF) your database uses. Every candidate has to pass through it, so the KDF sets the price per guess. inspect reads it from the unencrypted header, so it never touches your secrets:

keepass-kdbx-recover inspect vault.kdbx
KDBX format : 4.1
Cipher      : AES-256
KDF         : Argon2id
  memory    : 67,108,864 bytes (~64 MiB)
  iterations: 10
  parallel. : 4
  => Argon2 is memory-hard: a GPU gives LITTLE advantage; use CPU here.

Argon2 (the default since KDBX 4) is memory-hard: each guess needs a large block of RAM, a GPU barely helps, and the tool runs on the CPU with the worker count capped at min(6, CPU count). AES-KDF (the KDBX 3.1 default) is compute-bound, so a GPU is far faster, and export writes your candidates to a wordlist with the hashcat command to match:

hashcat -m 13400 -a 0 hash.txt candidates.txt

(extract the hash first with keepass2john; mode 13400 is KeePass).

A wide fragment space is fine on an old AES-KDF vault you can hand to a GPU, and brutal on a modern Argon2 one. Check the KDF before you leave anything running overnight.

Two simpler ways in

If you remember the exact shape rather than the pieces, there is mask mode: hashcat-style placeholders where Summer?d?d?d?d means Summer0000 to Summer9999. If you already have a list from somewhere, there is wordlist mode, streamed one line at a time so the file size does not matter. You can turn on more than one; they are tried in the order mask, fragments, wordlist.

If you would rather not touch flags at all, wizard asks a few questions and writes the config for you, and there is a small desktop window (keepass-kdbx-recover-gui) with the same two steps: say what you remember, then watch it run.

Keyfiles

KeePass derives a single key from the password and the keyfile together, so a wrong keyfile looks exactly like a wrong password. If your vault uses one and nothing matches, check the keyfile before you blame your memory. It was the first thing I got wrong.

What it does not do

  • It does not break or weaken the encryption. It only tries candidates you describe.
  • It will not pull a strong password out of nothing. Fragments mode needs you to remember the pieces. Mask and wordlist mode do not: a mask is brute force over the positions you leave open, and a wordlist is a dictionary attack. They can reach a password you never knew, but only one weak enough to brute-force or common enough to sit on a list, and only while the KDF is cheap enough for the search to finish. A long, random master password under Argon2 is not recoverable by guessing, here or anywhere else.
  • --case-variants only covers lower, UPPER and Capitalized. A piece you typed as "fLoWeR" will not come back from "flower"; give it as its own fragment.
  • The Windows build is an unsigned PyInstaller bundle, so an antivirus may flag it as a false positive. If that bothers you, build from source; it is a couple of commands.

Install

git clone https://github.com/HighMarck17/keepass-kdbx-recover
cd keepass-kdbx-recover
pip install .

Python 3.11+, two runtime dependencies (pykeepass and tomlkit), AGPL-3.0. Installs as keepass-kdbx-recover (also kpr) plus the GUI. The candidate generators, the config and the header inspector are covered by tests, and CI runs them on Linux and Windows across Python 3.11 to 3.13.

Repo: https://github.com/HighMarck17/keepass-kdbx-recover

I wrote it for myself on a bad afternoon, then cleaned it up in case it saves someone else the same one.

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