OpenResty/lua-nginx-module HTTP Request Smuggling in HEAD requests - CVE-2024-33452
Discovery & Motivation
I first came across this HTTP Request Smuggling vulnerability while conducting an internal pentest for a client at work. Realizing its potential impact, I decided to dig deeper into its root cause and discovered that it stemmed from the lua-nginx-module implementation in OpenResty, which ignores the body of a HEAD request.
In this blog post, I’ll break down the technical details of the vulnerability, its impact, and the attack scenarios it enables. Also, stay tuned for the second part of this research, where we will show case exploitation in real-world applications for cool findings and big bounties at FrogSec Research.
Lastly, I’d like to thank Mr. James Kettle for his invaluable insights, both through his incredible resources on HTTP Request Smuggling and by answering my questions, which greatly helped me in understanding this vulnerability.
Description
I found a HTTP Request Smuggling vulnerability in OpenResty/lua-nginx-module
<= v0.10.26 which allows attackers to smuggle requests.
When processing HTTP/1.1 requests, lua-nginx-module
incorrectly parses HEAD
requests with a body and treats the body as the new separate request.
Normally for other proxies, the following request is treated as a single request because the GET /smuggle request is inside of the HEAD
request’s body.
1 | HEAD / HTTP/1.1 |
But when parsed by lua-nginx-module
this request is treated as 2 separate requests. This leads to discrepancies between proxies if chained together.
Steps to Reproduce:
Setup OpenResty/
lua-nginx-module
like the following video
https://www.youtube.com/watch?v=eSfYLvVQMxwSend this request in Burp Suite.
1
2
3
4
5
6
7HEAD / HTTP/1.1
Host: 192.168.17.130:8000
Content-Length: 52
GET /smuggle HTTP/1.1
Host: 192.168.17.130:8000
As you can see in the response, and the access log. There is a smuggled GET request even though we have only sent the
HEAD
request.
Root-cause analysis:
The vulnerability lives in src/ngx_http_lua_util.c file.
ngx_http_lua_send_chain_link
is called when processing pipeline requests.ngx_http_discard_request_body
at line 614 instructs nginx to discard (skip through it) the request bodyngx_http_discard_request_body
callsngx_http_discard_request_body_filter
, which moves the request pointer forward by the Content-Length value to skip the body and correctly position the server for processing the next request.- However, at line 599, if the current method is
HEAD
,ngx_http_lua_send_chain_link
will returns before reachingngx_http_discard_request_body
and the current request body will be treated as a new request. Causing the HTTP Request Smuggling issue!
Attack scenerios:
I’ve observed that proxies which are implemented on top of lua-nginx-module
also suffer from this issue (Kong Gateway, Apache APISIX, …). Here are some attack scenarios to signify the impactfulness of this vulnerability:
I will be using Kong Gateway as an example for my POCs.
This is not a problem if Kong stands on its own at the front since it is just a normal HTTP pipelining behaviour. But, if Kong is chained with a front-end proxy (Nginx, Cloudflare, …) it would allow attackers to serve malicious responses and smuggle requests through the front-end proxies, which have persistent connection (keep-alive) to Kong. Let me explained how:
As I have observed in many proxies, a HEAD
request with a body will be treated as a single request, however when this request is passed to Kong gateway it would be treated as 2 separated pipelined requests (a HEAD
request and a malicious request in the body) due to lua-nginx-module
behaviour. This would leave the malicious request dangling in the response queue, and when another user sends a request that malicious response will be sent back to them.
Attack scenario 1: Serving XSS Responses to all victims
I have set up another POC (in kong-xss folder) where the latest Nginx will be the front-end proxy, Kong as the API gateway, and a default Apache page with a single configuration to redirect /assets to /assets/ path.
An attacker will be able to achieve XSS with this attack, even with a default Apache web page.
XSS Smuggle Payload:
1 | HEAD / HTTP/1.1 |
You can read more detail the HEAD technique here here.
Attack scenario 2: Bypassing Front-end proxies protection (E.g: Cloudflare)
I have created an attack scenario where CloudFlare will be blocking requests to /admin. However, an attacker can smuggle a GET /admin request in a HEAD
request’s body to bypass CloudFlare.
Attack Example 3: Stealing other user’s responses
This attack allows the attacker to desync the response queue and capture other user’s responses. Here is a visualization video on how this attack works:
POC
The proxy setup can be found in my GitHub repository:
https://github.com/Benasin/OpenResty_HEAD_HRS/tree/main
Impact:
The attacker can use this attack to bypass any frontend proxies protection, serve malicious responses to all the users in the same connection pool and capture responses of other users.
Discovery/Disclosure Timeline
- 24/01/2024: Vulnerability reported to OpenResty
- 09/03/2024: Patch provided by OpenResty in commit e5248aa8203d3e0075822a577c1cdd19f5f1f831
- 24/04/2024: CVE-2024-33452 assigned