{"id":49926,"date":"2026-10-30T20:08:39","date_gmt":"2026-10-30T14:38:39","guid":{"rendered":"https:\/\/www.getastra.com\/blog\/?p=49926"},"modified":"2026-10-30T20:08:42","modified_gmt":"2026-10-30T14:38:42","slug":"cve-2026-116566","status":"publish","type":"post","link":"https:\/\/www.getastra.com\/blog\/vulnerability\/cve-2026-116566\/","title":{"rendered":"CVE-2026-116566: Hostname Allowlist Bypass in CPython"},"content":{"rendered":"<div class=\"gb-container gb-container-83f53fef\">\n\n<p class=\"wp-block-paragraph\"><strong>Product Name:<\/strong> CPython (Python standard library)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Vulnerability:<\/strong> Hostname allowlist bypass via embedded NUL byte in <code>socket.getaddrinfo()<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Affected Versions:<\/strong> CPython versions earlier than 3.16.0 are affected by this vulnerability.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Fixed Version:<\/strong> 3.16.0 (first version outside the affected range; not yet released at time of writing).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>CWE:<\/strong> CWE-158: Improper Handling of NUL Byte<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>CVE:<\/strong> CVE-2026-116566<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Status:<\/strong> Patch merged upstream; awaiting release in CPython 3.16.0.<\/p>\n\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">A security researcher at Astra on 28\/10\/2026 identified a hostname-validation flaw in CPython that could enable hostname-allowlist bypass in apps relying on the standard library\u2019s <code>socket<\/code> module for both validation and connection.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The root cause is an improper NUL-byte handling issue (CWE-158) in the <code>socket.getaddrinfo()<\/code> implementation. The <code>_socket_getaddrinfo_impl<\/code> path accepts a hostname containing an embedded NUL byte and passes it to the C-level operating-system resolver, which may interpret the NUL as a C-string terminator and resolve only the prefix before it. The sibling API <code>socket.gethostbyname()<\/code> already rejects the same input with a <code>TypeError<\/code>, which is where the inconsistency becomes a real security problem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This produces a hostname allowlist bypass, in which an application that validates the full hostname string against a policy and then connects via <code>getaddrinfo()<\/code>-based clients (<code>urllib<\/code>, <code>http.client<\/code>, <code>asyncio<\/code>, and most TLS clients downstream of them) can be steered into resolving a different host than the validator approved, with downstream SSRF-adjacent and policy-enforcement consequences depending on the application\u2019s connection flow.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Technical Breakdown of CVE-2026-116566<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">CVE-2026-116566 was identified during a review of hostname-validation behavior across the CPython <code>socket<\/code> module APIs. The flaw sits at the boundary between Python-level validation and the C-level resolver call, in the way <code>getaddrinfo()<\/code> passes the <code>host<\/code> string into <code>getaddrinfo(3)<\/code> without sanitizing NUL bytes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Consider a hostname of the form:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>127.0.0.1\\x00.allowed.example<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">An application performing a suffix-based allowlist check on the string sees <code>.allowed.example<\/code> and admits the input. The resolver, when invoked through <code>socket.getaddrinfo()<\/code>, treats the NUL byte as a C-string terminator and resolves <code>127.0.0.1<\/code>. The validator and the resolver disagree on what the hostname is, which is the class of bug that allowlist bypasses live in.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The exact resolver behavior depends on the platform. Reported testing confirmed the inconsistency on Windows and on Linux systems using glibc.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How was CVE-2026-116566 discovered?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The behavior was found by comparing the input-validation posture of socket.getaddrinfo() against the sibling API socket.gethostbyname(). The latter already rejects NUL bytes in its argument, which raised the question of whether the former does the same. It does not.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The minimal differential test that confirmed the inconsistency:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import socket\n\nhost = \"127.0.0.1\\x00.allowed.example\"\n\ntry:\n\n\u00a0\u00a0\u00a0\u00a0result = socket.getaddrinfo(host, 21, type=socket.SOCK_STREAM)\n\n\u00a0\u00a0\u00a0\u00a0print(\"getaddrinfo result:\", result&#91;0]&#91;4]&#91;0])\n\nexcept Exception as exc:\n\n\u00a0\u00a0\u00a0\u00a0print(\"getaddrinfo rejected input:\", repr(exc))\n\ntry:\n\n\u00a0\u00a0\u00a0\u00a0socket.gethostbyname(host)\n\n\u00a0\u00a0\u00a0\u00a0print(\"gethostbyname accepted input\")\n\nexcept Exception as exc:\n\n\u00a0\u00a0\u00a0\u00a0print(\"gethostbyname rejected input:\", repr(exc))<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In the reported tests, getaddrinfo() resolved the example to the prefix address (127.0.0.1), while gethostbyname() rejected the embedded NUL. Reproduction was reported on Python <strong>3.11.9<\/strong>, <strong>3.12.10<\/strong>, <strong>3.13.12<\/strong>, <strong>3.13.17<\/strong>, and <strong>3.14.9<\/strong>, and the development branch was inspected at the source level.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How to replicate CVE-2026-116566<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Prerequisites<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A local Python environment running any version in the affected range (earlier than 3.16.0).<\/li>\n\n\n\n<li>A controlled test host, since the proof-of-concept triggers a real resolver call.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Steps<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>In a clean Python shell, import the <code>socket<\/code> module.<\/li>\n\n\n\n<li>Construct a hostname containing an embedded NUL byte, with the real target before the NUL and a decoy allowlisted suffix after it, for example <code>\"127.0.0.1\\x00.allowed.example\"<\/code>.<\/li>\n\n\n\n<li>Call <code>socket.getaddrinfo(host, 21, type=socket.SOCK_STREAM)<\/code> and inspect the resolved address in the result tuple.<\/li>\n\n\n\n<li>Call <code>socket.gethostbyname(host)<\/code> with the same input and observe the raised <code>TypeError<\/code>.<\/li>\n\n\n\n<li>Confirm that the two APIs disagree on whether the input is a valid hostname, which is the differential that produces the bypass in downstream applications.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">The reported outcome is that <code>getaddrinfo()<\/code> returns an address for <code>127.0.0.1<\/code>, while <code>gethostbyname()<\/code> refuses the call outright.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Impact of CVE-2026-116566<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The practical exposure sits in applications that accept attacker-controlled URLs or hostnames, parse out the host component, validate it against an allowlist or denylist, and then make a network connection using a standard-library client that resolves via <code>getaddrinfo()<\/code> (which is the default path for most high-level networking in Python, including <code>urllib<\/code>, <code>http.client<\/code>, <code>requests<\/code> on the socket layer, <code>asyncio<\/code> network calls, and most TLS clients).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Where that pattern exists, CVE-2026-116566 can produce:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Hostname allowlist bypass<\/strong>, in which the string passes the policy check while the connection reaches a different host than the policy intended.<\/li>\n\n\n\n<li><strong>SSRF-adjacent exposure<\/strong>, where an application designed to connect only to permitted external hosts can be steered into resolving an internal-range address embedded before the NUL byte.<\/li>\n\n\n\n<li><strong>Suffix-check and TLS-SNI mismatch<\/strong>, in which the validator, the resolver, and the TLS layer disagree about the destination hostname, which can produce certificate-pinning and policy-enforcement gaps depending on the client\u2019s behavior.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">CVE-2026-116566 is not a universal remote-code-execution issue. Exploitability requires an affected Python runtime and an application flow in which the hostname validation path and the resolver path can be made to disagree on the input. The severity in a given deployment is a function of what the application connects to, with what credentials, and under which network policy.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Current Status of CVE-2026-116566<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The issue was reported to CPython maintainers, and the fix has been merged upstream through a maintainer-created pull request titled <em>\u201cgh-1000000: Reject null bytes in getaddrinfo() host and port\u201d<\/em>, which brings the <code>getaddrinfo()<\/code> input-validation posture in line with <code>gethostbyname()<\/code>. CVE-2026-116566 is in the <strong>Reserved<\/strong> state with CWE-158 and a Moderate severity label on the GitHub Security Advisory.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The patch will ship in <strong>CPython 3.16.0<\/strong>, which is the first version outside the affected range. 3.16.0 has not been released yet at the time of writing, so every currently released CPython version remains affected until the fixed branch is tagged. Backports to the actively maintained branches (3.12, 3.13, 3.14, 3.15) may follow depending on the maintainers\u2019 call.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What Can You Do?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Users running affected CPython versions are advised to upgrade to CPython 3.16.0 as soon as it ships, and to track any backport releases on the actively maintained branches in the interim. Until a patched runtime is available, the following mitigations reduce exposure in applications that perform hostname validation:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Reject NUL bytes in hostname input before validation.<\/strong> A one-line check (<code>\"\\x00\" in host<\/code>) at the application boundary closes the differential before either standard-library API is called.<\/li>\n\n\n\n<li><strong>Validate the final normalized destination used for the connection<\/strong>, so that the string the resolver actually used matches the string the policy approved.<\/li>\n\n\n\n<li><strong>Avoid suffix-only allowlist checks.<\/strong> Combine them with exact-match or domain-tree logic that is not fooled by embedded control bytes.<\/li>\n\n\n\n<li><strong>Prefer URL-parsing libraries that normalize or reject control characters<\/strong> rather than hand-rolled hostname extraction.<\/li>\n\n\n\n<li><strong>Audit any call site that mixes <code>urllib<\/code>, <code>http.client<\/code>, <code>socket<\/code>, or <code>asyncio<\/code> network calls<\/strong> with a prior hostname-validation step, since those are the paths most likely to exhibit the differential.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Astra Security\u2019s manual pentest covers input-validation differentials of this class, including hostname-parsing and SSRF-adjacent flows, during authenticated and unauthenticated testing.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Product Name: CPython (Python standard library) Vulnerability: Hostname allowlist bypass via embedded NUL byte in socket.getaddrinfo() Affected Versions: CPython versions earlier than 3.16.0 are affected by this vulnerability. Fixed Version: 3.16.0 (first version outside the affected range; not yet released at time of writing). CWE: CWE-158: Improper Handling of NUL Byte CVE: CVE-2026-116566 Status: Patch &#8230; <a title=\"CVE-2026-116566: Hostname Allowlist Bypass in CPython\" class=\"read-more\" href=\"https:\/\/www.getastra.com\/blog\/vulnerability\/cve-2026-116566\/\" aria-label=\"Read more about CVE-2026-116566: Hostname Allowlist Bypass in CPython\">Read more<\/a><\/p>\n","protected":false},"author":138,"featured_media":49927,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[723],"tags":[],"class_list":["post-49926","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-vulnerability"],"_links":{"self":[{"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/posts\/49926","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/users\/138"}],"replies":[{"embeddable":true,"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/comments?post=49926"}],"version-history":[{"count":2,"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/posts\/49926\/revisions"}],"predecessor-version":[{"id":49929,"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/posts\/49926\/revisions\/49929"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/media\/49927"}],"wp:attachment":[{"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/media?parent=49926"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/categories?post=49926"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/tags?post=49926"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}