Abhijith Namboothiry
5 min readJun 30, 2024

Exploiting Cache Poisoning via Unkeyed Parameters and Headers in a Drupal Application

Introduction

During a recent bug hunting session, I came across a fascinating cache poisoning vulnerability in a Drupal application. This post will detail the steps I took to uncover the vulnerability, the technical specifics of the exploit, and the impact it had on the application.

Initial Observations

While testing the application, I noticed following intriguing headers in the response:

X-Drupal-Cache: MISS & Cache-Control: max-age=3600, public & Expires: Sun, 19 Nov 1978 05:00:00 GMT

HTTP/1.1 200 OK
Date: Wed, 26 Jun 2024 07:43:45 GMT
Server: <redacted>
Cache-Control: max-age=3600, public
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Expires: Sun, 19 Nov 1978 05:00:00 GMT
Vary: Cookie
X-XSS-Protection: 1
Strict-Transport-Security: max-age=31536000; includeSubDomains
From-Origin: same
Referrer-Policy: no-referrer-when-downgrade
X-Drupal-Cache: MISS

The “Expires: Sun, 19 Nov 1978 05:00:00 GMT” header indicates when the content expires. In this case, the date is set to a time far in the past, which is a common way to indicate that the response should not be cached without respecting the Cache-Control directives.

Also the headers indicated that the application was using Drupal’s default caching mechanism and that the content was public and cacheable for an hour.

How Caching Works in Drupal

  1. Cache Generation: When a page is requested and it’s not found in the cache (MISS), Drupal generates the page content and stores it in the cache for subsequent requests.
  2. Cache Serving: On subsequent requests for the same page, if the content is still valid in the cache (HIT), Drupal serves the cached version, which is faster and more efficient.

X-Drupal-Cache header is used by Drupal, a popular content management system, to indicate the cache status of a response. Here’s a detailed explanation of what the values HIT and MISS signify:

X-Drupal-Cache: HIT

  • Meaning: When the X-Drupal-Cache header has a value of HIT, it means that the response was served from the cache.
  • Implication: The requested page or resource was already stored in the cache, so Drupal did not need to regenerate the content. Serving content from the cache generally results in faster response times and reduced load on the server because it bypasses the need to execute the underlying code and database queries.
  • Example: If a user requests a page that has been previously cached, they will receive the cached version of that page, as indicated by X-Drupal-Cache: HIT.

X-Drupal-Cache: MISS

  • Meaning: When the X-Drupal-Cache header has a value of MISS, it means that the response was not served from the cache.
  • Implication: The requested page or resource was not found in the cache, so Drupal had to generate the content from scratch. This usually involves executing PHP code and database queries, which can take longer compared to serving a cached response.
  • Example: If a user requests a page that has not been cached yet or the cache has expired, Drupal will regenerate the content and serve it, as indicated by X-Drupal-Cache: MISS. This content may then be stored in the cache for future requests.

The Investigation Begins

Being curious about the caching behavior, I started experimenting with various URL parameters and headers. Upon further analysis, it became evident that the application was caching the value provided via an unkeyed URL parameter and the unkeyed header x-forwarded-port. Somtimes Param Miner is very much handy in figuring out the possibility of cache poisoning.

Evidences for possible cache-poisoning observed by Param-Miner

If you don’t know, then Param Miner is an extension that identifies hidden, unlinked parameters. It’s particularly useful for finding web cache poisoning vulnerabilities, and requires Burp Suite v2021.9 or later.

Read more about Param Miner here.

Crafting the Payload

To exploit this, I crafted a payload that included both an unkeyed parameter and the unkeyed header:

Triggering the Cache

After sending the request, I observed that the response now included:

“X-Drupal-Cache: HIT” and the payload that was getting reflected in the social media links of the website.

HTTP/1.1 200 OK
Date: Wed, 26 Jun 2024 05:36:17 GMT
Server: <something>
Cache-Control: max-age=3600, public
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Expires: Sun, 19 Nov 1978 05:00:00 GMT
Vary: Cookie
Content-Security-Policy-Report-Only: default-src <something>
X-XSS-Protection: 1
Strict-Transport-Security: max-age=31536000; includeSubDomains
From-Origin: same
Referrer-Policy: no-referrer-when-downgrade
X-Drupal-Cache: HIT
Last-Modified: Wed, 26 Jun 2024 05:36:16 GMT
ETag: "1719380176"
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
Content-Length: 53853
Several occurrences of payload being cached

This indicated that my payload was successfully cached by the application.

Impact and Exploitation

Further inspection revealed that the payload was being cached inside the links for the website. This meant that if someone clicked a cached URL, the payload would be executed, resulting in DNS/HTTP interactions being sent back to Burp Suite’s collaborator.

In practical terms, this could allow an attacker to poison the cache with malicious content, potentially redirecting users to malicious sites or stealing sensitive information.

Social handle loaded with payload being shared as a post

Reporting and Response

I reported the issue to the application developers. They acknowledged the bug and promptly fixed it. Unfortunately, they deemed it not severe enough to process a reward.

Response from the security team

Conclusion

This experience underscored the importance of properly keying all cacheable parameters and headers. Even seemingly minor misconfigurations can lead to significant security vulnerabilities. As always, continuous vigilance and thorough testing are essential to maintaining robust security.

Stay tuned for more bug hunting adventures!

About the Author

I’m Abhijith Namboothiry, a YouTuber, bug hunter, and tech geek. Follow my journey as I explore the depths of cybersecurity, share tips and tricks, and uncover hidden vulnerabilities.