Dynamic caching: What could go wrong?
Read Time:5 Minute, 38 Second

Dynamic caching: What could go wrong?

5 0

The Engintron plugin for CPanel presents a default configuration which could expose applications to account takeover and / or sensitive data exposure due to cache poisoning attacks.

Whenever a client sends a request to a web server, the received response is processed and served by the back-end service each time.

Default Request-Response HTTP Flow

In case of an high traffic volume, this behavior could generate a server overload, resulting in service performance issues. To solve this problem, reverse proxies implements mechanisms such as the web cache. When a user sends a request to a reverse proxy, the nginx core module will first check if a valid response is available in its caching storage. if no valid response is found, the original client request is then forwarded to the webserver, and the response is stored for future use before being send back to the client.

When another user will request the same resource, the nginx core will serve the response stored in the cache, instead of forwarding the request to the backend server, resulting in a much more fluent browsing for the client.

Request-Cached response HTTP Flow

Once the cache time is expired, the cached response is deleted. When another user request the same resource, the flow starts again by getting a new response from the webserver, storing it in the cache and so on.

Request-Store in cache HTTP Flow

Web-cache is also a complex mechanism which could easily be misconfigured, resulting in a wide variety of attack vectors.

Finding the bug

Engintron is an Nginx implementation for CPanel, which comes with some pre-enabled advanced functionalities, such as a micro-caching service for dynamic HTML content.

Micro caching configuration

This caching service allows the storage of dynamic HTML responses in the cache for 1 second. To avoid caching responses containing sensitive information, the application avoids caching responses for requests carrying cookies or urls with some common prefix

Cookie validation regex
URI validation regex

The cache key is set to $MOBILE$scheme$host$request_uri, meaning that two users sending a request to the same URL could receive the same responses.

Attack scenario

Scenario: A small webapplication used to send personal information for a candidacy, hosted by an Apache Web Server behind Nginx and running on a CPanel instance. The Nginx implementation is handled with the plugin “Engintron”.

Session handling is required, as the application allows to resume the candidacy later on by using a password set during the initial submission.

The first step requires some basic personal information, an email address and a password.

Vulnerable service homepage

The email and the password can be used to resume the candidacy later on.

Resume candidacy on vulnerable service

When submitting this form, the backend writes the provided information into a temporary file, and the client gets redirected to the second step.

Temporary file containing sensitive information

After a redirect to step2.php, some additional information are required, before being able to submit the final candidacy.

vulnerable application candidacy step 2

When the form gets submitted the temporary file is deleted, and the session is discarded as not useful anymore.

The attack

the response where the session cookie is being set is not handling cache control, exposing the application to some sort of web-cache poisoning attack, which would lead to account takeovers and sensitive data disclosure.

As mentioned at the beginning of this article, Engintron presents a micro-caching service which holds dynamic HTML resources in the reverse proxy cache for 1 second. Let’s analyze some responses:

Non cached response HTTP response

A legitimate request carrying the set “session-cookie” results in the Engintron cache being ignored. To verify this it is possible to send several requests in a short period of time, looking for discrepances in the responses, or for some header containing cache directives, such as “X-Nginx-Upstream-Cache-Status”. In this case, the cache-status header explicitly declare a bypass of the cached context.

Using the Burpsuite intruder we can send the request 100 times in a short period of time to verify it

Configuring intruder attack

and as expected, there is no difference in any of the responses

Verifying differences in responses

This happens because the “session-cookie” cookie matches the engintron regex and prevents caching of private responses.

To get a cached response we have to provide a request which does not get validated by any of the Engintron cache bypass conditions.
This is possible simply by removing the Cookie header from the request.

Cached response

The cached response contained a really interesting header: “Set-Cookie”. This header is setting the session-cookie value for the current user, identifying a session.

By sending the request twice in the same second, we would get the same set-cookie header.
To verify this, we can wrap a curl command in a while loop and observe that multiple responses are carrying the same value.

Verifying cached session cookie

Because of this, an attacker could automate the process of retrieving valid session cookies from the cached context, and try to use them to retrieve user sensitive information before he submits the final form.

To perform this attack I’ve built a small tool written in GO.
Please excuse me for my bad code writing skill, I will try to explain the relevant parts of the exploit code.

The exploit starts a thread which collects a new cookie for each second, storing it in a JSON file

Exploit function retrieving cached cookies

The cookie struct is holding the cookie value, how many times it got used by the script (Count), and if it got used to retrieve sensitive information (Consumed).

the JSON file looks like this

Stored / stolen session cookies JSON file

While the first thread collects cookies, a second thread is spawned to collect any new sensitive data associated with the stolen sessions.

Exploit collecting sensitive information

When the “mydata.php” page content length is greater then 933, it means that some data has been stored, and in that case a copy of the response would get saved.

Follows a video showing a proof of concept of the mentioned exploit

Exploit video proof-of-concept

This kind of application logic is common in many scenarios such as job candidacies. The impact of this issue highly depends on the kind of data processed by the application.

Another thing to note is that detecting attacks like this would be really difficult, as the generated traffic would look legit.


As a workaround for this issue it is recommended to disable the dynamic cache service from Engintron. To do this, it is necessary to comment the line 53 in the configuration file located at /etc/nginx/common_http.conf

Workaround remediation
0 %
0 %
50 %
0 %
0 %
50 %
Previous post Linux Kernel Exploit Development: 1day case study
Next post Workshop: Linux Kernel Exploitation 101 – Part 1