Breaking Down the Basics: My Dive into Hop-by-Hop Header Exploits

Dive into the hidden risks of HTTP/1.1 hop-by-hop headers. We uncover how these can be exploited to trick web applications and detail the dangers of misconfigured caches spreading malicious content. Essential insights for bolstering web security.

Contents
1. What exactly is a hop-by-hop header? 
2. Abusing hop-by-hop headers
3. Testing
4. Exploitation
5. References

In today's blog post, I'm diving into a fascinating topic that might not be widely discussed outside of tech circles: the art of influencing web systems and applications in unexpected ways. Specifically, we'll explore how HTTP/1.1 hop-by-hop headers can be manipulated to achieve this.

For those who might not be familiar, HTTP/1.1 includes mechanisms that allow headers to control how proxies and caches handle web requests. By "abusing" these headers, one can influence the behavior of web systems that involve multiple layers of caches or proxies before a request reaches its final destination, i.e., the backend application.

This technique is particularly intriguing because it exposes the vulnerabilities in the architecture of web systems, especially those heavily reliant on these intermediate layers. Today, we'll break down these techniques, how they work, and discuss which systems are most vulnerable to these kinds of attacks.

What exactly is a hop-by-hop header?

Let's break it down. In IT terms, a hop-by-hop header is specifically designed to be used and handled by the proxy that's currently managing the request. This is different from an end-to-end header, which is meant to stay with the request from start to finish of a request.

Under the guidelines of RFC 2616, which lays out the specifications for HTTP/1.1, certain headers are classified as hop-by-hop by default. These include headers are:

  • Keep-Alive,
  • Transfer-Encoding,
  • TE,
  • Connection,
  • Trailer,
  • Upgrade,
  • Proxy-Authorization, and
  • Proxy-Authenticate.

When a proxy comes across these headers, it's expected to take the necessary action indicated by them and not pass them on to the next point in the chain.

Additionally, HTTP/1.1 allows for some custom headers. You can specify additional headers to be treated as hop-by-hop by including them in the Connection header. For example,

Connection: close, X-Foo, X-Bar

In this case, you're instructing the proxy to handle X-Foo and X-Bar as hop-by-hop headers. This means these headers should be removed from the request before it’s forwarded.

Abusing hop-by-hop headers

https://nathandavison.com/blog/abusing-http-hop-by-hop-request-headers

When it comes to handling HTTP requests, removing a header might seem harmless enough, but it's a bit like flipping a switch—you never quite know what will happen. It could have no effect at all, or it could lead to catastrophic results. This uncertainty arises particularly when headers are removed that weren’t part of the original request but were added by a frontend or another proxy further up the chain ( ❗ ).

Example scenario ( 🧪 ) : a header that’s added somewhere along the chain could be crucial for backend access control decisions or might signal that the request originated from an Internet user. If this header gets dropped, it could trigger unexpected changes in the application’s logic. There might be cases where the application expects a header to always be present because it’s usually added by a proxy. If that header is missing, the application could start spilling out sensitive debug information—a goldmine for anyone with bad intentions.

The real issue comes up with the handling of the hop-by-hop headers, which are meant to be used and discarded by the proxy they reach, not passed along further. However, this isn't always what happens. In practice, some proxies might forward these hop-by-hop headers to the next server in the chain. This could include the Connection header itself, which interestingly is listed as a hop-by-hop header. According to the rules, a compliant proxy should consume the Connection header and not forward it. Proxies like HAProxy and Nginx, when configured as a proxy, sometimes pass the Connection header untouched, which could propagate this header handling issue even further along the chain.

This oddness in the proxy behavior highlights a critical oversight in the standard implementation of HTTP proxies and serves as a reminder of the complex interdependencies in modern web architectures.

Testing 👊

There's a relatively straightforward way to test for this. Start by identifying a request header that significantly alters the response when it is and isn't included. Then, see what happens when you list it as a hop-by-hop header. If the system removes the header—as indicated by a similar response when the header is in the Connection header and when it’s absent altogether—but different from when the header is present and not listed as hop-by-hop, then you might be onto something.

A practical test involves using the Cookie header against an endpoint that requires authentication, assuming the system uses cookie-based authentication. Consider this example:

GET /api/me HTTP/1.1
Host: localhost
Cookie: session=AAAA...
Connection: close, Cookie

Here, if /api/me normally returns HTTP 200 with user details when authenticated, and session is a valid cookie, then altering the request as above might yield an unexpected response. This would suggest that the system is removing the Cookie header as directed by its listing as hop-by-hop. This test merely confirms that a proxy is removing the header as instructed—it doesn't verify whether the custom hop-by-hop list is passed further along the chain, which is where things get even more intriguing ( 😨 ).

Automated

Python script by Nathan (https://gist.github.com/ndavison/298d11b3a77b97c908d63a345d3c624d)

Header list: https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/BurpSuite-ParamMiner/lowercase-headers

for HEADER in $(cat headers.txt); do python poison-test.py -u "https://target.com" -x "$HEADER"; sleep 3; done

Exploitation

1️⃣ Bypassing Security Controls with X-Forwarded-For

Steps:

  1. Attacker sends an HTTP request with a fake IP in the X-Forwarded-For header.
  2. Attacker includes "Connection: close, X-Forwarded-For" to mark X-Forwarded-For as hop-by-hop.
  3. Misconfigured proxy forwards request without the spoofed X-Forwarded-For header.
  4. Web application sees the request as coming from a trusted proxy, potentially allowing unauthorized access.

2️⃣ Cache Poisoning via Hop-by-Hop Header Injection

Steps:

  1. Attacker sends a request with hop-by-hop headers that shouldn't be cached (e.g., "Connection: close, Cookie").
  2. Poorly configured cache server fails to remove hop-by-hop header and caches the attacker-specific response.
  3. Future users receive the cached response meant for the attacker, risking session hijacking or exposure of sensitive info.

References

  1. https://nathandavison.com/blog/abusing-http-hop-by-hop-request-headers
  2. https://book.hacktricks.xyz/pentesting-web/abusing-hop-by-hop-headers
  3. https://medium.com/@0xwan/hop-by-hop-header-78d0866101f6