tl;dr
- CVE-2024-6827
- CVE-2024-31617
Gunicorn
Introduction
Iam usually not a great fan of Request smuggling since its exploitability is laregly depeneded on the combinations of the FE BE servers.This all started Yadhu Krishna found a zeroday in gunicorn and decided to Host an internal challenge on the Same where he used combination of openlitespeed as a frondend proxy server and gunicorn as BE server.
Diving into Source Code
I initially started looking into Gunicorn’s source code, specifically where they handle the TE and CL headers, and my attention was immediately drawn to something at the very first stage.
1 | elif name == "TRANSFER-ENCODING": |
When TRANSFER-ENCODING is passed as a header, Gunicorn strictly checks if the value exactly matches to chunked
, which looks like a valid implementation to catch troublemakers like TRANSFER-ENCODING: chunkedxd
. But what if Transfer-Encoding: chunked, gzip
is passed? This is a legitimate header, and Gunicorn, as per its implementation, is not expecting stacked header values. This helped me to imagine a hypothetical situation where we pass valid headers of TE and CL where FE server consider TRANSFER-ENCODING
since it has precedence over Content-Length
while Gunicorn try to strictly compare the value and fail since we have stacked values in chunked, gzip
and continue to proceed with Content-Length
a TE CL request smuggling.
Proof of Concept
Consider the following dummy application where the /admin route is forbidden by the frontend server(OpenLiteSpeed)
1 | from flask import Flask, render_template, request, redirect, Response |
Exploit
1 | POST / HTTP/1.1 |
Video Poc
Fix
The value of TRANSFER-ENCODING is converted into an array using a comma as the delimiter and then compared.
1 | for (name, value) in self.headers: |
OpenLiteSpeed
Introduction
Since the frontend server used for the challenge was OpenLiteSpeed, I was trying out different requests and inspecting them to exploit Gunicorn. I encountered a common vulnerable implementation related to request smuggling.
Diving into Source Code
OpenLiteSpeed was comparing if the value of transfer-encoding
starts with chunked
. This would intercept an invalid header TRANSFER-ENCODING: chunkedxd
as a valid TRANSFER-ENCODING
, while the other server rejects it and falls back to Content-Length
. Any BE server that integrates with this vulnerable version of OpenLiteSpeed can be easily exploited.
1 | if (strncasecmp(pCur, "chunked", 7) == 0) |
POC
Fix
1 | if (strncasecmp(pCur, "chunked", 7) == 0 |