Flash CTF – Tunny Ache

I recently saw a Lorenz SZ42 in person, and thought the story about how it was broken was really cool, so this challenge was my way of showing CTF players how the machine worked, and to give you a chance to partially break it yourself.

Basic Operation of an SZ40

The Lorenz SZ40 has a total of 12 wheels, each with a number of cams on them that are set either in an up or down position. There are 5 χ (Chi) wheels, 5 Ψ (Psi) wheels, and 2 μ (Mu) wheels. The actual operation of the machine was pretty simple.

Text Formatting

Text sent over the SZ40 was sent in Baudot Code, specifically the Murray Code variation designed in 1901. This means that instead of 8 bits to a character, 5 were used. This also means that many special characters had to be encoded by switching to a different mode, noted by a figure or letter shift character. This code does not support case distinction, all text is simply uppercase (or technically it could be lowercase as well).

Encryption

Encryption worked almost as a stream cipher, XORing the plaintext (or ciphertext in decrypting) with the current χ wheel’s cams, and the current Ψ wheel’s cams. So the equation for the ciphertext bit in position 1 is k1 = m1 ⊕ χ1 ⊕ Ψ1, where χ1 and Ψ1 are the current cams on χ and Ψ wheel number 1. Because the machine has 5 wheels for χ and Ψ, each step of the machine encrypts a single character at a time.

Each step would turn the χ wheels one position, and would also turn the μ 61 wheel by one position, but the μ37 wheel only turned if the μ61 wheel was set to on, and the Ψ wheels would only turn if the μ 37 wheel was set to on.

To prevent a repeating keystream, the wheel lengths were designed with prime or co-prime lengths, which are as follows:

  • χ1 – 41
  • χ2 – 31
  • χ3 – 29
  • χ4 – 26
  • χ5 – 23
  • Ψ1 – 43
  • Ψ2 – 47
  • Ψ3 – 51
  • Ψ4 – 53
  • Ψ5 – 59
  • μ61 – 61
  • μ37 – 37

Each of the wheels had cams that were set to on or off, typically denoted by x or . such as the well known KH Pattern setting for μ61: .xxxx.xxxx.xxx.xxxx.xx....xxx.xxxx.xxxx.xxxx.xxxx.xxx.xxxx...

Breaking the cipher

Since we have a partial plaintext and a ciphertext, we need to find a way to reverse it back into the χ, Ψ and μ wheels to continue the keystream and decrypt the rest of the ciphertext. The first easy step is to just xor m (plaintext) with z (ciphertext) to get k (keystream). After that, we need to be creative.

The key insight to this attack is that while it’s nearly impossible to find any useful information from the keystream itself, it’s very easy to find insight in the keystream Δ (Delta). By XORing one character of the keystream with the last character, we can see which bits of the keystream changed that step. In theory, χ should change about half of the time, assuming 50/50 on and off cams, and Ψ should change about 25% of the time, since it has about 50/50 on and off cams, and also only moves when the μ37 wheel is set, which should also be about half the time. This means that our Δ k should bias to Δχ, since ΔΨ will affect the Δk much less often.

If we assume the wheels all start in position 1, and then rotate them as we step through the Δk, counting the Δk bits in each corresponding position of the χ wheels gives us a statistical understanding of the Δχ for each position of the wheels. Doing this with the entire ciphertext, gives us high confidence Δχ for each χ wheel, which will correspond to one of two possible χ wheel settings for each wheel.

From there, getting ΔΨ is a bit more complex since the wheels only move some of the time. However, once we have Δχ, we can determine exactly when the ΔΨ is moving on any bit of the character, telling us that μ37 was set to on, and giving us another bit of the ΔΨ keystreams. If we see all 5 bits of ΔΨ set to 0, we assume that none of the cams moved because μ37 was set to off, but this is not always the case. However, statistically it is extremely rare for all 5 Ψ cams to not change values when turning, so we can just look for repeating patterns and count the most likely one for each Ψ wheel.

We can then determine the μ61 cams by checking when in a cycle of 61 we see the cams move after not moving the step before. From there, it’s easy to determine the μ37 cams by cross referencing the movements of the Ψ wheels with the known μ61 wheel and tracking the state.

From there, it’s as easy as testing each of the two χ and Ψ candidates for each wheel, along with the 2257 (61 x 37) μ positions by decrypting the ciphertext with the candidate settings, and checking if the known plaintext matches, if it does, decrypt the entire text, and get the flag METACTF 0BSCUR3C0MM5I5N0TS3CURECOMM5. This attack could be optimized further, but the limited amount of settings makes that unnecessary in this instance.

(I have a solve script for this, but it’s multiple files and is still pretty messy, so I’ll leave implementing the attack as an exercise to the reader, plus, it was really fun to write!)