CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Dashboard
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-27959
7.50.05%

Hostile Takeover: Dissecting Koa's Host Header Injection

Alon Barad
Alon Barad
Software Engineer

Feb 27, 2026·6 min read·6 visits

PoC Available

Executive Summary (TL;DR)

Koa versions < 2.16.4 and < 3.1.2 utilized a flawed string-splitting method to parse the HTTP Host header. Attackers can inject an '@' symbol to trick the parser into identifying a malicious domain as the hostname. This enables URL spoofing attacks, notably compromising password reset flows.

A high-severity Host Header Injection vulnerability in the popular Node.js framework Koa allows attackers to manipulate context.hostname via malformed headers. By exploiting Koa's naive string-splitting logic, attackers can spoof the hostname used by the application for critical functions like URL generation and routing, leading to password reset poisoning and potential cache poisoning.

The Hook: Minimalism vs. Reality

Koa is often touted as the "elegant," "minimalist" successor to Express. It strips away the bloat, giving you a bare-bones context (ctx) and a promise-based middleware stack. It’s beautiful code. But in the world of security, "minimalism" is often a polite synonym for "we didn't implement the safety rails because they looked ugly."

Here is the problem with minimalism: The HTTP specification is not minimal. It is a sprawling, ancient beast full of optional parameters, legacy authentication schemes, and edge cases that haven't been relevant since the 90s but are still technically valid. Koa's developers fell into a classic trap: assuming that a Host header is always just hostname:port.

CVE-2026-27959 is the result of that assumption. It’s a logic error that allows an attacker to lie to the application about where it is running, simply by confusing the parser with a character that virtually no modern web app expects to see in a Host header: the @ symbol.

The Flaw: Split Decisions

To understand this vulnerability, we have to look at how Koa handles the ctx.hostname property. When a request comes in, the application often needs to know its own hostname to generate absolute URLs (e.g., for email links or OAuth callbacks). Koa derives this from the Host header (or X-Forwarded-Host if trusted).

The vulnerability lies in lib/request.js. The developers needed to strip the port number from the Host header to get the clean hostname. Their logic was effectively this:

// The naive implementation
const host = this.get('Host'); // e.g., "localhost:3000"
if (!host) return '';
if ('[' === host[0]) return this.URL.hostname || ''; // IPv6 handling
return host.split(':', 1)[0];

Do you see the issue? split(':', 1)[0] takes everything before the first colon. This works perfectly for google.com (no colon) and google.com:443 (returns google.com).

But RFC 3986 defines the authority component of a URI as: [ userinfo "@" ] host [ ":" port ]

This means user:password@domain.com is a valid structure. If an attacker sends Host: evil.com:junk@victim.com, Koa's naive parser sees the colon after evil.com, splits there, and returns evil.com. It ignores the fact that evil.com:junk is actually the userinfo part of the authority, and the real host is victim.com.

The Code: The Smoking Gun

Let's look at the actual diff that fixed this. The patch didn't just tweak the regex; it completely capitulated to the complexity of the spec by delegating parsing to the WHATWG URL API.

The Vulnerable Code (simplified):

// Old logic
get hostname() {
  const host = this.get('Host');
  // ... IPv6 checks ...
  return host.split(':', 1)[0]; // The fatal flaw
}

The Fix (v2.16.4 / v3.1.2):

// New logic
get hostname() {
  let host = this.get('Host');
  // ... IPv6 checks ...
  
  // Handle Userinfo confusion
  if (host.includes('@')) {
    try {
      // Wrap in a protocol to make it a valid URL string for parsing
      const url = new URL(`http://${host}`);
      host = url.hostname;
    } catch (e) {
      // If parsing fails, fail safe
      return '';
    }
  } else {
    // Safe to split if no '@' is present
    host = host.split(':', 1)[0];
  }
  return host;
}

The fix explicitly checks for the @ symbol. If present, it stops playing string-manipulation games and constructs a full URL object to parse the string correctly. This is computationally more expensive, but it prevents the bypass.

The Exploit: Stealing Tokens

How do we weaponize this? The most common impact of Host Header Injection is Password Reset Poisoning. Developers love to use ctx.hostname (or req.hostname in Express) to build the link sent in password reset emails.

The Scenario:

  1. Vulnerable Code:

    router.post('/forgot', async (ctx) => {
      const token = generateToken();
      // VULNERABLE: Relies on ctx.hostname
      const link = `${ctx.protocol}://${ctx.hostname}/reset?token=${token}`;
      await sendEmail(ctx.request.body.email, link);
    });
  2. The Attack: The attacker triggers the password reset flow for the victim's email address, but modifies the Host header.

    POST /forgot HTTP/1.1
    Host: attacker.com:xy@victim-site.com
    Content-Type: application/json
     
    { "email": "ceo@victim-site.com" }
  3. The Execution:

    • Koa sees attacker.com:xy@victim-site.com. It splits at the first colon.
    • ctx.hostname becomes attacker.com.
    • The link generated is http://attacker.com/reset?token=....
    • The Email Server receives this link and sends it to the CEO.
  4. The Payoff: The CEO sees an email from the legitimate victim-site.com (because the email sender wasn't spoofed). They click the link, expecting to reset their password. Instead, their browser sends a GET request to attacker.com with the valid reset token in the URL query string. The attacker logs the token, uses it on the real site, and takes over the account.

The Impact: Why Panic?

While this isn't Remote Code Execution (RCE) out of the box, the Integrity impact is high. Host Header Injection is a facilitator vulnerability—it turns innocent features against the user.

  • Password Reset Poisoning: As described above, this is the primary vector. It allows full account takeover without user interaction beyond a click.
  • Cache Poisoning: If a caching proxy sits in front of Koa and keys the cache based on the actual host (victim-site.com) but serves the response generated by Koa (which thinks it's attacker.com), you could poison the cache. Every subsequent user visiting victim-site.com could be served content containing malicious links to attacker.com.
  • OAuth/SSO Redirection: If ctx.hostname is used to validate redirect_uri in OAuth flows, an attacker could bypass whitelist checks or steal authorization codes.

> [!WARNING] > Even with the patch, beware of ctx.origin. The patch fixes ctx.hostname, but ctx.origin (which is often protocol + '://' + host) might still return the raw, tainted header in some contexts or frameworks. Always validate.

The Fix: Remediation

The immediate fix is simple: Upgrade.

  • If you are on Koa v2, upgrade to 2.16.4 or higher.
  • If you are on Koa v3 (beta), upgrade to 3.1.2 or higher.

Defense in Depth: Don't rely solely on the framework to parse headers correctly. The Host header is user input. Treat it like toxic waste.

  1. Configure your Reverse Proxy: Nginx or Apache should be your first line of defense. Configure them to only accept requests for specific, whitelisted server_name entries. If Nginx receives a request for attacker.com, it should drop it before it ever reaches Node.js.
  2. Hardcode URLs: If your app lives at api.example.com, put that in your ENV variables (BASE_URL=https://api.example.com). Use that variable to generate links, not ctx.hostname. Trusting the client to tell you who you are is like trusting a stranger to hold your wallet.

Official Patches

koajsPatch commit for Koa v2

Technical Appendix

CVSS Score
7.5/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N
EPSS Probability
0.05%
Top 83% most exploited

Affected Systems

Koa Framework (Node.js)

Affected Versions Detail

Product
Affected Versions
Fixed Version
koa
koajs
< 2.16.42.16.4
koa
koajs
>= 3.0.0 < 3.1.23.1.2
AttributeDetail
Attack VectorNetwork (Remote)
CVSS v3.17.5 (High)
ImpactIntegrity (High)
Exploit StatusPoC Available
WeaknessImproper Input Validation
EPSS0.05%

MITRE ATT&CK Mapping

T1566.002Phishing: Spearphishing Link
Initial Access
T1190Exploit Public-Facing Application
Initial Access
CWE-20
Improper Input Validation

Known Exploits & Detection

GitHub AdvisoryAdvisory detailing the PoC logic for Host Header Injection

Vulnerability Timeline

Initial documentation PRs identified issue
2025-11-04
Advisory Published (GHSA-7gcc-r8m5-44qm)
2026-02-25
Patched versions 2.16.4 and 3.1.2 released
2026-02-25
CVE-2026-27959 Published
2026-02-26

References & Sources

  • [1]GitHub Advisory: Host Header Injection in Koa
  • [2]RFC 3986: URI Generic Syntax (Authority)

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.