Skip to content

Multiple cryptographic vulnerabilities: padding oracle, CTR counter reuse, T-table timing, no AEAD #58

@spartan8806

Description

@spartan8806

Security Advisory: Multiple Cryptographic Vulnerabilities

Reporter: Conner Webber (conner.webber000@gmail.com)
Severity: HIGH
Affected versions: <= 1.6.1

Note: I attempted to file this via GitHub's Private Vulnerability Reporting (PVRA), but it is not enabled on this repository. There is also no SECURITY.md. Maintainer: please consider enabling PVRA for future security reports. I'm also emailing this to github@ricmoo.com.


Finding 1: CBC Padding Oracle (HIGH, CWE-209)

File: pyaes/util.py:51-60

strip_PKCS7_padding() performs incomplete padding validation — it only checks that the last byte is ≤16 but does NOT verify that all padding bytes match. Additionally, pad=0 is accepted (returning empty data). Different exception types for length vs. padding-value errors create distinguishable error paths, enabling a classic Vaudenay padding oracle attack for full plaintext recovery.

PoC sketch: An application that returns different errors for "invalid padding" vs "decryption failed" allows an attacker to recover the full plaintext byte-by-byte without knowing the key.


Finding 2: Default All-Zero IV (HIGH, CWE-329)

File: pyaes/aes.py:378-379, 497-498, 425-426

CBC, OFB, and CFB modes default to iv=None which silently uses an all-zero IV. This makes encryption deterministic (identical plaintexts → identical ciphertexts), violating IND-CPA security. No warning is emitted.

Note: This overlaps with #56 and #57. I'm including it here for completeness of the security audit.


Finding 3: CTR Default Counter=1 with No Nonce (HIGH, CWE-323)

File: pyaes/aes.py:556-562, 278

CTR mode defaults to counter=NoneCounter(initial_value=1) with no random nonce. Every encryption with the same key starts from the same counter, producing identical keystreams. If two messages are encrypted with the same key and default counter, XORing the ciphertexts yields the XOR of the plaintexts (two-time pad), which is trivially breakable.


Finding 4: T-table Timing Side-Channel (HIGH, CWE-208)

File: pyaes/aes.py:219-232, 253-265

AES implementation uses T-table lookups indexed by secret-dependent byte values. In pure Python, list indexing is variable-time, creating cache-timing side channels exploitable in shared-host environments.


Finding 5: No Authenticated Encryption (HIGH, CWE-353)

Architectural issue — no GCM, CCM, EAX, or HMAC wrapper is provided. All modes produce malleable ciphertext. An attacker can flip ciphertext bits to predictably alter the plaintext without detection.


Impact

  • Finding 1 (padding oracle): Full plaintext recovery without the key in applications that expose error types
  • Findings 2-3 (zero IV/counter): Silent production of insecure, deterministic encryption
  • Finding 4 (timing): Key recovery in shared environments
  • Finding 5 (no AEAD): Ciphertext manipulation without detection

Recommended Remediation

  1. Validate all padding bytes in strip_PKCS7_padding(), reject pad=0
  2. Require explicit IV/nonce — no silent defaults (aligns with Insecure (because of) default IV provided #56/Raise on default IV #57)
  3. Add GCM mode or at minimum document that HMAC-then-encrypt is required
  4. Use constant-time S-box lookups (single byte-indexed array, no T-tables)

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions