Arbitrary File Reading in Next.js < 2.4.1

Arbitrary File Reading in Next.js < 2.4.1

Next.js is a quite popular (>13k stars on GitHub) framework for server-rendered React applications. It includes a NodeJS server which allows to render HTML pages dynamically. While digging into server’s code, a list of internal routes drew my attention:

 defineRoutes () {
     const routes = {
       /* ... */
       '/_next/:path+': async (req, res, params) => {
         const p = join(__dirname, '..', 'client', ...(params.path || []))
         await this.serveStatic(req, res, p)
       },
       '/static/:path+': async (req, res, params) => {
         const p = join(this.dir, 'static', ...(params.path || []))
         await this.serveStatic(req, res, p)
       }
       /* ... */
 }
 

As you can see you can pass arbitrary path into serveStatic() function via /_next/ and /static/ endpoints:

 export function serveStatic (req, res, path) {
   return new Promise((resolve, reject) => {
     send(req, path)
     .on('directory', () => {
       // We don't allow directories to be read.        const err = new Error('No directory access')
       err.code = 'ENOENT'
       reject(err)
     })
     .on('error', reject)
     .pipe(res)
     .on('finish', resolve)
   })
 }
 

This function just pipes the contents of files into the output without any validation or restrictions. So, we can try to perform a path traversal:

GET /_next/../../../../../../../../../etc/passwd HTTP/1.1

And it works! However, NodeJS application servers are usually deployed behind nginx. Due to path normalization in nginx we cannot just use forward slashes and dots, nginx will return a Bad Request error code. Luckily, NodeJS server transforms backslashes into forward slashes, so we can bypass nginx validation.

GET /_next..................etcpasswd HTTP/1.1

ZEIT, the company which develops Next.js, was very quick to respond and roll out the patch . Be sure to update to the latest version.

Alt text

Цифровые следы - ваша слабость, и хакеры это знают.

Подпишитесь и узнайте, как их замести!

Арсений Реутов

блог о web-безопасности