In early 2024, security researchers uncovered a vulnerability—CVE-2024-12224—affecting the idna crate, which is a crucial part of Rust's popular rust-url library. This bug lets attackers craft special "punycode" hostnames (internationalized domain names written so computers can understand them) that trick software components into treating two different domain names as the same—or the same domain as two different entities. This can lead to security disasters anywhere from phishing to authentication bypass.

In this post, you’ll learn what went wrong, see sample exploit code, and get pointers on keeping your code safe.

What’s Punny About Punycode?

Punycode is a way to write internationalized domain names (IDNs) using only ASCII letters. For example, münich.com becomes xn--mnich-kva.com.

Rust’s rust-url library, via the idna crate (from Servo), is widely used to convert these domains back and forth and to validate them. Think of it like a universal translator for web addresses.

The Heart of the Problem: Unsafe Equivalence

The vulnerability stems from the idna crate failing to properly check if a given hostname’s punycode representation—when "decoded" to its Unicode form—might be treated as equivalent (i.e., the same) by one part of a system but as not equivalent by another.

> Danger: This means, for example, a login page could check you’re logging into login.example.com and give you a session cookie, but another part of your app, seeing the punycode, might think it’s a whole different domain and treat you as a stranger—or worse, leak data!

Hostnames are sometimes compared before conversion to Unicode and sometimes after.

- The idna crate did not consistently enforce that these representations resolve to a single, canonical form.
- This means attackers could exploit inconsistencies—especially when Unicode homographs are involved (letters that look the same, like а (Cyrillic) and a (Latin)).

Exploiting CVE-2024-12224

Let’s see how an attacker could exploit this in practice.

Original: login.example.com

- Punycode/homograph: xn--login-xyz.example.com

Imagine your app uses rust-url to canonicalize or sanitize hostnames, but different pieces apply the punycode translation at different times.

The Exploit (Simplified Rust Example)

use idna::domain_to_ascii;

fn main() {
    // Attacker-supplied domain (looks similar to 'login.example.com')
    let unsafe_domain = "ⅼogin.example.com"; // The 'l' here is the Unicode 'Small Roman Numeral Fifty' (U+217C)

    // Convert domain to punycode using idna crate
    let punycode = domain_to_ascii(unsafe_domain).unwrap();

    println!("Punycode: {}", punycode); // Would print something like 'xn--ogin-9pg.example.com'

    // Now, let's say one system component does direct string comparison:
    if unsafe_domain == "login.example.com" {
        println!("Domains match (unsafe comparison)");
    } else {
        println!("Domains do not match (unsafe comparison)");
    }

    // Another component might compare punycode:
    if punycode == "login.example.com" {
        println!("Punycode matches");
    } else {
        println!("Punycode does not match");
    }
}

Here’s how this bug could get you in trouble

1. Session Hijacking: Attacker logs in as themselves on their "homograph" domain, gets a session cookie, and presents it to the real domain—or vice versa.
2. Cross-Origin Confusion: Browser or server treats two different domains as the same, allowing XSS or CORS attacks.
3. Phishing/Evil Twin: An attacker tricks a victim into entering their password on a fake domain that looks visually identical due to Unicode trickery.

Fixes and Workarounds

The fix in the idna crate ensures equivalence is properly validated: all domain representations are canonicalized and mapped in a single, safe way before any comparison or processing.

What to do

- Update to idna crate v.4. (or later), which fixes the bug (release note here).
- Always canonicalize all hostnames using the same method *before* doing any comparison or permission check.

References

- CVE-2024-12224 at NVD
- idna crate on crates.io
- Servo/rust-url issue
- Unicode Homograph Attacks Overview

Conclusion

CVE-2024-12224 is a classic example of how tiny differences in data handling—especially in the messy world of Unicode and punycode—can lead to big security holes. If you are working with URLs, international domains, or authentication, double-check your dependencies, update them, and insist on consistent, canonical comparisons!


Stay safe—don't let punycode puns catch you off guard!

Timeline

Published on: 05/30/2025 02:15:19 UTC
Last modified on: 05/30/2025 16:31:03 UTC