In late 2022, a subtle bug was uncovered lurking inside Python’s standard library—specifically, in the way the hmac.compare_digest function worked. Many developers trust this little function to prevent timing attacks, a kind of security issue where an attacker guesses secrets by measuring how long a program takes to run. But what happens when the defenses themselves don’t actually work as advertised?

Let’s dive into CVE-2022-48566, how it affected Python up through 3.9.1, and why the smallest details sometimes matter most in security.

What is CVE-2022-48566?

The vulnerability sits in the way the hmac.compare_digest function worked in Python’s hmac library (file: Lib/hmac.py). This function was supposed to compare two pieces of data—a password, a token, or a cryptographic signature—in exactly the same amount of time, no matter if they’re the same or different. This helps protect against attackers who try timing attacks to guess secrets.

But: It had a subtle mistake caused by the way Python optimized its code.

Where is the Problem?

In Python versions up to 3.9.1, the compare_digest implementation could allow the Python interpreter and modern CPUs to optimize the loop, so it might “short-circuit” or run faster if the compared values were different early on.

This defeated the purpose of “constant-time” comparison.

Here’s a simplified version of what was in Lib/hmac.py

def compare_digest(a, b):
    # Only relevant for Python's pure Python implementation
    if len(a) != len(b):
        return False
    result = 
    for x, y in zip(a, b):
        result |= x ^ y
    return result == 

Looks innocent, right? In theory, even if values differ early, it keeps looping and accumulating differences.

However, the issue is that Python and the underlying C interpreter (and even hardware CPUs) can do optimizations. For instance, if result already becomes true, some interpreters or JITs might recognize the logical outcome is certain and skip unnecessary work. This allows a timing side channel.

The Problem in Human Terms

*Supposed to*: Always take the same time to compare any two strings.

*Actually*: Could finish earlier when two strings start differing, depending on Python’s execution environment and platform.

Why does this matter?

An attacker can measure how long it takes to check a wrong password, signature, or HMAC, and use those timing clues to slowly build the correct value—like unlocking a safe one digit at a time.

Exploitation Details

Let’s walk through a simple example. Attackers try each possible value for, say, the first byte of a signature until they notice the comparison takes just a little bit longer (because more of the bytes matched). Then, they move on to the next byte.

This kind of attack is very slow, but in high-value targets—think hardware tokens, API keys, etc.—it can be practical.

Here’s some code to play with the timing difference

import time
import hmac

def time_compare(secret, guess):
    start = time.time()
    hmac.compare_digest(secret, guess)
    end = time.time()
    return end - start

real = b"TOPSECRET123"
for i in range(len(real) + 1):
    guess = real[:i] + b"\x00" * (len(real)-i)
    t = time_compare(real, guess)
    print(f"Matching {i} chars, took {t*1e6:.2f} us")

With enough runs, a sophisticated attacker could (very slowly) infer the secret.

How Was It Fixed?

After the issue was reported (reference), the core team updated the standard library to make sure that the comparison is always done in real constant time, regardless of the data or platform. Additionally, CPython uses a much safer native implementation nowadays.

If you use Python 3.9 or earlier, especially 3.9.1 or older, you should upgrade. New versions fix this bug.

More Reading

- CVE-2022-48566 at Mitre
- Python Cpython Issue #101664
- Timing Attacks Explained (OWASP)

Final Thoughts

This bug is a reminder: Don’t roll your own crypto, and always keep your libraries up to date. Even standard library functions in the world’s most popular programming language can have subtle, exploitable flaws hiding inside.

If you write code that compares secrets, always use the most up-to-date version of your language and libraries, and double-check the docs about constant-time safety.

Timeline

Published on: 08/22/2023 19:16:00 UTC
Last modified on: 10/13/2023 17:04:00 UTC