Partitioning Oracle Attack: Cracking Passwords Fast

Partitioning Oracle Attack: Cracking Passwords Fast

Written by

in

A partitioning oracle attack is a fast way to guess a password or key by asking a server one clever question at a time. Instead of testing a single candidate per query, the attacker builds one ciphertext that decrypts successfully under a whole group of candidate keys at once. When the server reports that ciphertext as valid, the attacker learns the real key is somewhere in that group. One yes or no answer splits the search space in half, or better, which turns a slow online guessing attack into something close to a binary search. It is one member of the wider family of cryptographic oracle attacks.

Where the partitioning oracle attack shows up

The setting is password based encryption. A low entropy secret, a password or a PIN, is stretched into a key, and that key is used with an authenticated encryption mode to protect a message. Think of an encrypted backup, a messaging system that derives keys from a shared passphrase, or a token sealed with a key made from a user secret. Anywhere a server decrypts attacker supplied ciphertext and reveals whether it authenticated, you have the raw material for the attack.

How one ciphertext decrypts under many keys

You might assume a ciphertext belongs to exactly one key. Decrypt it under the wrong key and the authentication tag fails, so you get a reject. That intuition is wrong for the AEAD modes most systems use. As explained in the key commitment attack, those modes are not committing: with some effort an attacker can build one ciphertext and tag that verify as authentic under two different keys, and with more work, under hundreds or thousands of keys at once.

A ciphertext that authenticates under a set S of candidate keys is called a key multi collision. When the server decrypts it with the real key, one of two things happens:

  • If the real key is in S, decryption authenticates and the server accepts.
  • If the real key is not in S, the tag fails and the server rejects.

So the accept or reject answer tells the attacker whether the real key is inside the set they chose. That is the whole oracle.

The search

With that oracle in hand, the attacker runs a search over the candidate list. In the clean version it looks like this:

candidates = all likely passwords        # say one million
while more than one candidate remains:
    S  = half of the remaining candidates
    ct = build_multi_collision(S)        # one ciphertext valid for every key in S
    if server_accepts(ct):
        candidates = S                   # real key is in this half
    else:
        candidates = candidates - S      # real key is in the other half

Building a multi collision for a very large set is expensive, so real attacks use a splitting factor smaller than half and eliminate a batch of candidates per query rather than exactly half. The principle holds either way: each query removes many guesses at once, not one.

A normal guessing attack asks is it this one. A partitioning oracle asks is it any of these thousand, and gets a useful answer every time.

Why this is so much worse than normal guessing

Online password guessing is usually slow on purpose. You test one password per request, a rate limit caps you at a handful per minute, and a lockout stops you after a few misses. A candidate space of a million passwords is safe behind that wall because you can never send a million requests.

A partitioning oracle tears the wall down. Each request now covers a large slice of the candidate space, so the number of requests drops from the size of the space to roughly its logarithm. A search that would have needed a million queries can finish in a few thousand, which fits inside many rate limits. The password was never weak in isolation. The encryption mode let the attacker ask about many passwords per question.

How to defend against it

The fixes attack both halves of the problem: the oracle itself and the speed it grants.

  • Use a key committing AEAD. This is the direct fix. A committing scheme makes a ciphertext valid under exactly one key, so a crafted multi collision cannot exist and a single query can only ever rule out a single key. See the key commitment attack for how commitment works and which constructions provide it.
  • Rate limit and lock out decryption attempts. Treat repeated failed decryptions like failed logins. Caps, backoff, and alerts shrink the window even if some oracle remains.
  • Do not expose the accept or reject bit. If an external caller never learns whether a ciphertext authenticated, there is no oracle to query. Fail the same way for every bad input.
  • Raise the entropy of the secret. Partitioning helps most against small candidate spaces. A long random key leaves nothing to enumerate, though this is not an option when humans pick the secret.
  • Bind context into the key. Mix a per user salt or a server side secret into the key derivation so one crafted ciphertext cannot apply across accounts.

The assumption that breaks

The mistake here is believing that one valid decryption equals one correct key. That holds only if the encryption commits to its key, and the common modes do not. For background on this class of usage bug, see vulnerability basics. Finding a partitioning oracle means noticing that a decryption endpoint leaks a validity bit, then asking whether the mode behind it commits to a single key. That is reasoning about how an app uses a primitive, not matching a known bad call, and it is the kind of question an autonomous researcher like UnboundCompute is built to ask. Read more on our about page.

Frequently asked questions

What is a partitioning oracle attack?

It is a method for guessing a password or key quickly by crafting one ciphertext that decrypts validly under a whole group of candidate keys at once. When a server accepts that ciphertext, the attacker learns the real key is somewhere in the group, so a single query rules in or out many guesses instead of one.

Why is a partitioning oracle faster than normal password guessing?

Normal online guessing tests one password per request, so a large candidate space stays safe behind rate limits. A partitioning oracle covers many candidates per request, so the number of queries drops from the size of the space to roughly its logarithm. A search that would need a million queries can finish in a few thousand.

What makes a partitioning oracle attack possible?

The encryption mode must fail to commit to a single key. Common AEAD modes allow an attacker to build one ciphertext and tag that authenticate under many keys at once, called a key multi collision. If a ciphertext could only ever verify under one key, each query would rule out just one guess and the attack would collapse.

How do you stop a partitioning oracle attack?

Use a key committing AEAD so a ciphertext is valid under exactly one key, which is the direct fix. Also rate limit and lock out repeated failed decryptions, avoid exposing the accept or reject bit to external callers, and bind a per user salt into key derivation so one crafted ciphertext cannot apply across accounts.


Put an autonomous researcher on your own systems

UnboundCompute is an autonomous security researcher that reasons about how an application fits together and proves the access control and injection bugs it finds. We are opening a small number of founding design partner seats: private early access pointed at a staging target you choose, and a say in what it looks for. If your team ships software worth pressure testing, apply to the design partner program.