From 7c841235b4167cd96a22709c8d62c3b654a7305e Mon Sep 17 00:00:00 2001 From: Robert Rothenberg Date: Sun, 4 Oct 2026 22:22:49 +0000 Subject: [PATCH] Fix CVE-2026-19954 pwhois converted non-ASCII domain labels with Net::IDN::Punycode and prepended xn-- without the IDNA mapping and normalization steps, so a label with uppercase non-ASCII letters, or not in NFC, was encoded to a different A-label than its IDNA form, and pwhois queried WHOIS for the wrong domain. Use Net::IDN::Encode::domain_to_ascii instead, and reject names that it cannot convert. See https://github.com/regru/Net-Whois-Raw/issues/34 Assisted-by: Claude Opus 5.5 (1M context) --- Makefile.PL | 4 ++-- bin/pwhois | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Makefile.PL b/Makefile.PL index 96622c7..9e8cb60 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -29,7 +29,7 @@ my %WriteMakefileArgs = ( "HTTP::Request" => 0, "IO::Socket::IP" => 0, "LWP::UserAgent" => 0, - "Net::IDN::Punycode" => 0, + "Net::IDN::Encode" => 0, "Regexp::IPv6" => 0, "URI::URL" => 0, "strict" => 0, @@ -59,7 +59,7 @@ my %FallbackPrereqs = ( "HTTP::Request" => 0, "IO::Socket::IP" => 0, "LWP::UserAgent" => 0, - "Net::IDN::Punycode" => 0, + "Net::IDN::Encode" => 0, "Regexp::IPv6" => 0, "Socket" => 0, "Test::More" => 0, diff --git a/bin/pwhois b/bin/pwhois index c9bf1de..3e2853a 100644 --- a/bin/pwhois +++ b/bin/pwhois @@ -8,7 +8,7 @@ use warnings; use Net::Whois::Raw; use Getopt::Long; use Encode; -use Net::IDN::Punycode qw( :all ); +use Net::IDN::Encode qw( domain_to_ascii ); use utf8; my $help; @@ -89,7 +89,12 @@ unless ( validate_domain_name( $dname ) ) { exit -1; } -$query = to_punycode( $dname ); +$query = eval { to_punycode( $dname ) }; + +unless ( defined $query ) { + print encode_output( "\nIncorrect dname:\n$dname\n", $output_cp ); + exit -1; +} $query .= '?' . $options if defined $options; @@ -194,15 +199,13 @@ sub decode_query { return $query; } -# Decode domain name to punycode if needed +# Convert domain name to IDNA A-labels if needed (dies on invalid names) sub to_punycode { my ( $dname ) = @_; return $dname if $dname =~ /^[a-z0-9.\-]*$/; - return join '.', - map { /^[a-z0-9.-]*$/ ? $_ : 'xn--' . Net::IDN::Punycode::encode_punycode( $_ ) } - split /[.]/, $dname; + return domain_to_ascii( $dname ); } sub validate_domain_name { -- 2.53.0