Skip to main content
Vulnerability Database/CVE-2026-87080

CVE-2026-87080: Perl Net::IDN::Punycode Information Disclosure

CVE-2026-87080 is an information disclosure flaw in Net::IDN::Punycode::PP for Perl where truncated labels decode incorrectly. This article covers the technical details, affected versions, security impact, and mitigation.

Published:

CVE-2026-87080 Overview

CVE-2026-87080 affects Net::IDN::Punycode::PP versions before 2.590 for Perl. The pure-Perl decoder mishandles truncated labels in decode_punycode, producing a name that contains a character never present in the encoded input. The XS backend rejects the same input, creating a parser differential between installations. This backend disagreement lets a sender craft a label that one system resolves to a valid name while another rejects it. The weakness is classified as [CWE-1286] (Improper Validation of Syntactic Correctness of Input).

Critical Impact

Attackers can craft Punycode labels that decode inconsistently across systems, enabling name confusion attacks against Internationalized Domain Name (IDN) processing pipelines that use the pure-Perl backend.

Affected Products

  • Net::IDN::Punycode::PP versions before 2.590
  • Net::IDN::Encode distributions that fall back to the pure-Perl backend when the XS backend is not built
  • Perl applications performing IDN decoding through Net::IDN::Punycode without the XS backend available

Discovery Timeline

  • 2026-09-22 - CVE CVE-2026-87080 published to NVD
  • 2026-09-22 - Disclosure posted to the OpenWall oss-security mailing list
  • 2026-09-22 - Last updated in NVD database

Technical Details for CVE-2026-87080

Vulnerability Analysis

The defect lies in how the pure-Perl decoder detects the end of the encoded input. Inside the decode loop, the code reads one digit at a time using four-argument substr and uses defined to test whether input remains. When the input string is exhausted, substr returns the empty string rather than undef. The defined check therefore succeeds, and decoding continues past the end of the label.

The empty string then converts to a numeric digit value that falls below the valid Punycode range. This reduced value decrements the accumulator, and the decoder derives an extra code point along with its insertion position from the corrupted state. The extra character is deterministic for any given truncated input.

Because the XS backend rejects the same truncated label, two installations of Net::IDN::Punycode can disagree about what a label means depending on whether the XS module was built. A sender can pick a label that one resolver treats as a legitimate name while another rejects the identical input.

Root Cause

The root cause is an incorrect end-of-input sentinel in lib/Net/IDN/Punycode/PP.pm. The decoder uses defined $cp to detect exhaustion, but substr($code, 0, 1, '') on an already-empty string returns '', which is defined. The correct predicate is length $cp, which distinguishes an empty return from a real one-character digit.

Attack Vector

An attacker submits a truncated Punycode label to any application or protocol handler that decodes IDN input through the vulnerable pure-Perl backend. Mail systems, URL parsers, DNS-adjacent tooling, and access-control components that normalize hostnames are typical targets. The attack requires no privileges or user interaction and is delivered over the network.

text
     LOOP:
 	for (my $k = BASE; 1; $k += BASE) {
 	    my $cp = substr($code, 0, 1, '');
-	    croak("incomplete encoded code point in decode_punycode") if !defined $cp;
+	    croak("incomplete encoded code point in decode_punycode") if !length $cp;
 	    my $digit = ord $cp;
 
 	    ## NB: this depends on the PunyRE catching invalid digit characters

Source: GitHub Commit Patch. The fix replaces !defined $cp with !length $cp so that an exhausted input string correctly triggers the incomplete encoded code point in decode_punycode error.

Detection Methods for CVE-2026-87080

Indicators of Compromise

  • Log entries showing successful decoding of Punycode labels that end mid-digit sequence, particularly labels shorter than expected for the number of extended code points they claim to encode.
  • Divergent hostname resolution results between application components that use Net::IDN::Punycode::PP versus the XS backend or independent IDN libraries.
  • Inbound requests containing xn-- labels that round-trip to different Unicode strings on different hosts in the same fleet.

Detection Strategies

  • Inventory Perl environments and record whether Net::IDN::Encode loaded the XS or pure-Perl backend at process start; flag any host on which the pure-Perl backend is active below version 2.590.
  • Add regression tests that decode a corpus of known-truncated Punycode labels and compare output against the XS backend or a reference implementation such as libidn2.
  • Cross-check hostnames decoded at the application edge with hostnames resolved downstream, and alert on mismatches.

Monitoring Recommendations

  • Instrument IDN decode call sites to log the input label, the loaded backend, and the decoded output for post-hoc differential analysis.
  • Monitor CPAN and distribution package indexes for Net-IDN-Encode versions below 2.590 during software composition analysis scans.
  • Watch the OpenWall oss-security thread for follow-up disclosures affecting downstream modules.

How to Mitigate CVE-2026-87080

Immediate Actions Required

  • Upgrade Net-IDN-Encode to version 2.590 or later on every host where Perl processes IDN input.
  • Where feasible, ensure the XS backend is built and loaded so Net::IDN::Punycode does not silently fall back to the vulnerable pure-Perl implementation.
  • Audit application code paths that accept hostnames, email addresses, or URLs from untrusted sources and confirm they route through the patched module.

Patch Information

The fix is committed upstream in robrwo/Net-IDN-Encode and released in Net-IDN-Encode-2.590-TRIAL. See the GitHub commit patch and the MetaCPAN release changes for the authoritative change record. The patch also adds t/punycode_security-pp.t to lock in regression coverage for truncated labels.

Workarounds

  • Force use of the XS backend by installing a C toolchain and rebuilding Net::IDN::Encode so Net::IDN::Punycode::XS is present at load time.
  • Pre-validate Punycode input against a reference decoder such as libidn2 and reject any label that yields a different decoding than the pure-Perl backend.
  • Reject xn-- labels that fail a strict round-trip check (decode then re-encode must return the original label byte-for-byte).
bash
# Upgrade the vulnerable module from CPAN
cpanm Net::IDN::Encode@2.590

# Verify the installed version and confirm the XS backend is loaded
perl -MNet::IDN::Encode -e 'print $Net::IDN::Encode::VERSION, "\n"; \
  print "XS backend: ", (defined $INC{"Net/IDN/Punycode/XS.pm"} ? "yes" : "no"), "\n"'

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.