How a simple-httpd Vulnerability Slipped In
Over Thanksgiving weekend I discovered a vulnerability in my recent simple-httpd overhaul. I fixed it immediately and pushed out the patch. Despite being careful about the translation of request paths to filesystems paths, one thing slipped by. Here’s how.
When writing my original web server a couple of years ago I
established a global variable,
httpd-root, which is the location on
the filesystem from where all files are served. Nothing above this
directory should be visible to clients in any way. The simple,
dangerous way to do this is with a plain
(concat httpd-root request-path)
The vulnerability here is that
request-path could contain
reference to the parent directory. This would allow a request to
access anything on the filesystem. This was obvious to me at the time,
so I wrote a
httpd-clean-path to remove any
.. portions in the
request. As long as
httpd-root isn’t an empty string, this closes
all the holes.
(concat httpd-root (httpd-clean-path request-path))
A couple of years later after, I’ve honed my Emacs Lisp skill, I go
through refactoring everything. I’ve since learned about the function
expand-file-name and when I see
used to build a path, I change it to this function, which more
appropriate for the job. This happened in commit 3b405343
(2012-08-07). I’m using
httpd-clean-path to handle everything
dangerous, so it’s safe, right?!
(expand-file-name (httpd-clean-path request-path) httpd-root)
When the path being expanded by
expand-file-name is an absolute
path, that path is returned directly, ignoring the second
argument. Unfortunately, anything beginning with
~ is an absolute
path, because this automatically expands into a home directory. With
concat this didn’t need to be handled because any
~ was always
httpd-root. Now that I was using
this allowed everyone read-access to everything in the hosting user’s
home directory if the request path started the request path with a
(expand-file-name "~foo" "/etc") ; => "/home/foo"
The fix is dead simple: prefix the cleaned path with a
expand-file-name. This forces the path to be relative so that
it’s expanded properly.
(expand-file-name (concat "./" "~foo") "/etc") ; => "/etc/~foo"
I apologize to anyone using simple-httpd. The fix has been on MELPA
for about a month now, so make sure you’re updated. I myself have a
long-running simple-httpd instance exposed to the Internet
(impatient-mode makes for a wonderful pastebin!), and when I found
this my stomach sunk in panic. I do keep an eye on my
and I never saw anyone exploit this. Due to simple-httpd’s obscurity
I’m pretty sure no one else discovered this anyway. The vulnerability
only existed for about three months before I caught it. If you are
able to find any other vulnerability please tell me!