WEP Cracking

Network Security Spring 2021

The goals of this lab are to:

  1. Recover a WEP key by analyzing encrypted 802.11 frames
  2. Gain experience in probabilistic key recovery attacks

WEP is known to be vulnerable to key recovery attacks due to key reuse. Recall from lecture that WEP keys are either 40-bit or 104-bit values. In conjunction with a 24-bit IV, a key \(k\) is (re)used to initialize a fresh RC4 cipher state for each frame to be encrypted. A ciphertext \(C\) is produced from a plaintext frame \(M\) and a checksum \(c(M)\) using the following construction:

\[C = (M || c(M)) \oplus (\mathsf{RC4}(IV || k))\]

An encrypted frame \(F\) is then:

\[F = IV || C\]

FMS found that certain weak IVs allow a statistical attack on the RC4 key scheduling algorithm, where an attacker could recover key bytes with enough observations of frames encrypted using a weak IV. A weak IV in this case is one of the following form:

\[IV = (i + 3, 255, \cdot)\]

That is, for a key index \(i\), the first two bytes of a weak IV are \(i+3, 255\). Then, an attacker can simulate the first \(i+3\) key scheduling rounds of RC4 and return a value derived from that state that is a guessed key byte. These guesses are simply that, guesses, but are statistically biased towards the correct key byte. Over enough observations, this bias becomes reliably observable. Thus, iterating over a large-enough number of encrypted frames and generating guesses for each key byte in turn allows an attacker to recover the correct WEP key with high probability.

You can use the following pseudocode to implement RC4 key scheduling for the purposes of this lab.

fn simulate_rc4(key: &[u8], frame: &[u8]) -> u8 {
    // Init a new RC4 state
    let mut state = [0u8; 256];
    for i in 0..state.len() {
        state[i] = i as u8;

    // Simulate key_index + iv.len() rounds of key scheduling
    let mut j = 0u8;
    for i in 0..(key.len() + 3) {
        if i < 3 {
            j = j.wrapping_add(state[i]).wrapping_add(frame[i]);
        } else {
            j = j.wrapping_add(state[i]).wrapping_add(key[i - 3]);
        state.swap(i, j as usize);

    let x = frame[3] ^ 0xffu8;
    x.wrapping_sub(j).wrapping_sub(state[key.len() + 3])

Keep in mind that WEP cracking can be slow. For the reference solution which is implemented using native code, recovering each key byte takes approximately three minutes. Interpreted languages like Python will be substantially slower. In addition, loading the entire packet dump into memory may not be feasible on your development system.

Lab Objectives

A ZST-compressed JSON file containing encrypted WEP frames is available in Canvas. Each line of this file is one vector of byte values representing an encrypted frame.

  1. Analyze the file to recover the WEP key (graduates only)

Submission Instructions

Package a README containing the key, your source code in src/, and a Dockerfile that builds and runs your solution when given a path to a decompressed JSON file containing WEP-encrypted frame vectors as a gzipped TAR archive. Submit the archive to Canvas.