[Cryptohack] Logon Zero write up

4 min readFeb 11, 2022

This article continues the series of challenges write ups from Cryptohack. This time we are going to look at the vulnerability in AES-CFB8 which lays in the heart of Logonzero vulnerability (CVE-202–1472). I will explain the essentials, but if you feel like you need some more details, read this paper.

For the challenge we are provided with the server network address and its source code. Let’s check the source code:

We should consider two pieces of the provided code.

  1. The decrypt function within CFB8 class (encrypt is never called, so we don’t look there).
  2. challenge function, of course.

The decrypt function does the following:

  1. Set IV to first 16 bytes of the provided ciphertext
  2. Set ct to other part of ciphertext
  3. Initialize AES instance and empty byte string pt
  4. Set state to IV
  5. For every byte of ct, encrypt state and XOR corresponding ct byte with only first byte of the encrypted state. Add XOR result to resulting pt string. Finally remove first byte of state and add current ct byte to the end of state

The challenge function performs some actions according to the provided option. We have three options:

  1. authenticate — we must provide password, it will be compared to the current password, that server holds. If passwords are equal, we get the flag.
  2. reset_connection — just reinitializes CFB8 object with the new random key.
  3. reset_password — changes server’s password according to our input. It takes some provided token, decodes it from hex value and decrypts it with CFB8. Next, it takes 4 last bytes of decrypted value as a password length and then takes corresponding amount of data from the start as a new password.

Now, it is obvious that we should somehow apply our attack within reset_password option. We should be able to send such a token that after it is decrypted we can guess new_password value. But what can we do?

Once again let’s look at the decrypt function. We can control ciphertext, therefore we actually control the state. Too bad the resulting pt value depends on the cipher.encrypt result, and even knowing the state at every iteration there is no way we can predict its output without the key. Well, the least we can do, is we can manufacture such a state that ciphertext.encrypt will always return the same value, at least that’s good.

Another good thing is that only first byte of the AES encryption is used, so there are actually only 256 possible values for that byte. We could try and guess that value, but there is a better way.

Remember, that we also have reset_connection option, which changes the key. The new AES key is still random, but there is a high possibility that new key will yield new encryption result for the same state. The most amazing thing is that there are such keys that will encrypt the state into some value that starts with 0. And there is 1 to 256 chance that new key will be such a key. What does it mean for us? It means, that if the first byte of encrypted state is 0, than the ct byte will be put to pt as is.

The last thing left — we need state value to be the same for every iteration. Well, that’s easy, we just need to send single repeated byte as a ciphertext. Any byte will do, but the best value is 0. If ciphertext only contains zeroes, state never changes, and if first byte of encrypted state is also 0 we will make pt to contain only zeroes. And that’s great, because than we will have new password’s length equal to 0 as well, so we effectively erase the password.

Now let’s combine all above into an attack algorithm. There are actually only 3 steps in our algorithm:

  1. Send reset_password with token equal to all zeroes
  2. Try to authenticate with empty password.
  3. If authentication successful, we get the flag. Otherwise, we send reset_connection to change the encryption key.

We repeat these steps over and over until we lucky enough to get such a random key that will encrypt our all zeroes state into some value that also starts with 0. There is 1 to 256 chance of that, so we will find that key soon enough. But we should automate that, of course. Here is the exploit scrip: