·

CVE-2026-4437: glibc accepts forged hostnames from the wrong DNS section

Second CVE. This one’s in glibc, which is a step up from bcrypt-ruby.

When your Linux system does a reverse DNS lookup (gethostbyaddr), glibc’s resolver parses the DNS response and pulls out the PTR record from the answer section. Except it doesn’t actually stop at the answer section. A missing loop decrement means it keeps reading into the authority and additional sections, and will happily accept a forged PTR record it finds there.

A malicious DNS server can return whatever hostname it wants, and glibc will hand it to your application as the real result.

What is the bug?

DNS responses have sections: questions, answers, authority, and additional. The header tells you how many records are in each section. The code is supposed to iterate through exactly ancount answer records and stop. Here’s what glibc does in getanswer_ptr (resolv/nss_dns/dns-host.c):

int ancount = ns_rr_cursor_ancount(&c);   // line 818

while (ancount > 0)                        // line 823
  {
    struct ns_rr_wire rr;
    if (!__ns_rr_cursor_next(&c, &rr))     // advances through ENTIRE packet
      { ... }

    if (rr.rtype == T_PTR
        && __ns_samebinaryname(rr.rname, expected_name))
      {
        *hnamep = ...;
        return NSS_STATUS_SUCCESS;          // accepts this PTR
      }
    // ancount is NEVER decremented — loop continues forever
  }

ancount starts at whatever the DNS header says (usually 1) and never goes down. The loop just keeps going until __ns_rr_cursor_next runs out of packet data. So it blows right past the answer section into authority and additional records.

Two other functions in the exact same file do this correctly:

// getanswer_r, line 741:
for (; ancount > 0; --ancount)    // correct

// gaih_getanswer_slice, line 932:
for (; ancount > -0; --ancount)   // correct (with a bonus typo)

That > -0 is harmless (equivalent to > 0 in two’s complement) but funny. The bug was introduced in commit e32547d661 in August 2022, which rewrote the function and changed while (ancount-- > 0 && ...) to while (ancount > 0). The decrement just got dropped. Shipped in glibc 2.37.

How it works

A malicious DNS server responds to a PTR query like this:

DNS Header:
  ancount = 1       (1 answer record)
  arcount = 1       (1 additional record)

Answer Section:
  1.2.0.192.in-addr.arpa  TXT  "hello"
  (TXT — skipped by getanswer_ptr, only matches CNAME/PTR)

Additional Section:
  1.2.0.192.in-addr.arpa  PTR  evil.attacker.com
  (PTR with matching owner name — accepted as the answer)

The loop processes the TXT record in the answer section. Not a PTR, so it skips it. ancount is still 1, so it keeps going. __ns_rr_cursor_next advances into the additional section, finds the PTR record with a matching owner name, and returns evil.attacker.com to the caller.

Your application calls gethostbyaddr("192.0.2.1") and gets back evil.attacker.com.

How I found it

I was reading through glibc’s DNS resolver code and noticed the while (ancount > 0) pattern with no decrement. Then I saw the two other functions right next to it that both use for (; ancount > 0; --ancount). Same variable, same file, different pattern. That kind of inconsistency usually means someone introduced a bug during a rewrite.

I wrote a small PoC DNS server in Python that crafts a response with a TXT record in the answer section and a forged PTR in the additional section, plus a C client harness that calls gethostbyaddr directly. On a vulnerable glibc, it prints:

[+] gethostbyaddr returned:
    h_name: evil.attacker.com

[!] BUG CONFIRMED: glibc returned forged hostname 'evil.attacker.com'
[!] This hostname came from the additional section, not the answer section

I reported it to the glibc security team. Turns out another researcher (Antonio Maini) had independently found the same bug around the same time. We’re both credited on the advisory.

What is the real-world impact?

The attacker needs to control the DNS response, which means either a compromised resolver, DNS MITM (local network, public WiFi), compromised authoritative server, or cache poisoning. That’s not trivial but it’s not unrealistic either.

Once you control the response, anything that trusts reverse DNS is affected:

The glibc security team’s assessment is that no known DNS server currently returns responses that would trigger this in normal operation. An attacker would need to be network-adjacent or have compromised infrastructure. But the bug has been sitting in every Linux system since glibc 2.37 (August 2022), which is basically everything shipping in the last 3+ years.

What is the fix?

One line. Change while (ancount > 0) to for (; ancount > 0; --ancount). Same pattern the other two functions already use.

-  while (ancount > 0)
+  for (; ancount > 0; --ancount)

Fixed by Carlos O’Donell in commit 9f5f18aab. Patched in glibc 2.44. If you’re running anything from glibc 2.37 to 2.43, update.

Full details in Bug 34014.