Feature #1989
closedWorkaround broken operating systems - Displaying source code of php, ruby or any other file
Description
If you put a trailing slash / after the .php and that file is a link, it will just display the content of the file.
2009-05-22 13:40:37: (response.c.221) -- splitting Request-URI
2009-05-22 13:40:37: (response.c.222) Request-URI : /index.php/
2009-05-22 13:40:37: (response.c.223) URI-scheme : http
2009-05-22 13:40:37: (response.c.224) URI-authority: localhost
2009-05-22 13:40:37: (response.c.225) URI-path : /index.php/
2009-05-22 13:40:37: (response.c.226) URI-query :
2009-05-22 13:40:37: (response.c.254) -- sanatising URI
2009-05-22 13:40:37: (response.c.255) URI-path : /index.php/
2009-05-22 13:40:37: (response.c.221) -- splitting Request-URI
2009-05-22 13:40:37: (response.c.222) Request-URI : /index.php/
2009-05-22 13:40:37: (response.c.223) URI-scheme : http
2009-05-22 13:40:37: (response.c.224) URI-authority: localhost
2009-05-22 13:40:37: (response.c.225) URI-path : /index.php/
2009-05-22 13:40:37: (response.c.226) URI-query :
2009-05-22 13:40:37: (response.c.254) -- sanatising URI
2009-05-22 13:40:37: (response.c.255) URI-path : /index.php/
2009-05-22 13:40:37: (mod_access.c.135) -- mod_access_uri_handler called
2009-05-22 13:40:37: (response.c.391) -- before doc_root
2009-05-22 13:40:37: (response.c.392) Doc-Root : /work/websites/freesoft.com/htdocs/
2009-05-22 13:40:37: (response.c.393) Rel-Path : /index.php/
2009-05-22 13:40:37: (response.c.394) Path :
2009-05-22 13:40:37: (response.c.442) -- after doc_root
2009-05-22 13:40:37: (response.c.443) Doc-Root : /work/websites/freesoft.com/htdocs/
2009-05-22 13:40:37: (response.c.444) Rel-Path : /index.php/
2009-05-22 13:40:37: (response.c.445) Path : /work/websites/freesoft.com/htdocs/index.php/
2009-05-22 13:40:37: (response.c.462) -- logical > physical handling physical path
2009-05-22 13:40:37: (response.c.463) Doc-Root : /work/websites/freesoft.com/htdocs/
2009-05-22 13:40:37: (response.c.464) Rel-Path : /index.php/
2009-05-22 13:40:37: (response.c.465) Path : /work/websites/freesoft.com/htdocs/index.php/
2009-05-22 13:40:37: (response.c.482) -
2009-05-22 13:40:37: (response.c.483) Path : /work/websites/freesoft.com/htdocs/index.php/
2009-05-22 13:40:37: (response.c.490) -- file found
2009-05-22 13:40:37: (response.c.491) Path : /work/websites/freesoft.com/htdocs/index.php/
2009-05-22 13:40:37: (response.c.640) -- handling subrequest
2009-05-22 13:40:37: (response.c.641) Path : /work/websites/freesoft.com/htdocs/index.php/
2009-05-22 13:40:37: (mod_indexfile.c.151) -- handling the request as Indexfile
2009-05-22 13:40:37: (mod_indexfile.c.152) URI : /index.php/
2009-05-22 13:40:37: (mod_access.c.135) -- mod_access_uri_handler called
2009-05-22 13:40:37: (mod_staticfile.c.394) -- handling file as static file
2009-05-22 13:40:37: (response.c.652) -- subrequest finished
2009-05-22 13:40:37: (response.c.121) Response-Header:
Files
Updated by venatir over 15 years ago
# lighttpd configuration file # # use it as a base for lighttpd 1.0.0 and above # # $Id: lighttpd.conf,v 1.7 2004/11/03 22:26:05 weigon Exp $ ############ Options you really have to take care of #################### ## modules to load # at least mod_access and mod_accesslog should be loaded # all other module should only be loaded if really neccesary # - saves some time # - saves memory server.modules = ( "mod_rewrite", # "mod_redirect", "mod_alias", "mod_access", # "mod_cml", # "mod_trigger_b4_dl", # "mod_auth", # "mod_status", # "mod_setenv", "mod_fastcgi", # "mod_proxy", "mod_simple_vhost", # "mod_evhost", # "mod_userdir", # "mod_cgi", # "mod_compress", # "mod_ssi", # "mod_usertrack", # "mod_expire", # "mod_secdownload", # "mod_rrdtool", "mod_accesslog" ) ## a static document-root, for virtual-hosting take look at the ## server.virtual-* options server.document-root = "/work/websites/freesoft.com/htdocs/" ## where to send error-messages to server.errorlog = "/var/log/lighttpd/error.log" # files to check for if .../ is requested index-file.names = ( "index.php", "index.html", "index.htm", "default.htm" ) ## set the event-handler (read the performance section in the manual) # server.event-handler = "freebsd-kqueue" # needed on OS X # mimetype mapping mimetype.assign = ( ".pdf" => "application/pdf", ".sig" => "application/pgp-signature", ".spl" => "application/futuresplash", ".class" => "application/octet-stream", ".ps" => "application/postscript", ".torrent" => "application/x-bittorrent", ".dvi" => "application/x-dvi", ".gz" => "application/x-gzip", ".pac" => "application/x-ns-proxy-autoconfig", ".swf" => "application/x-shockwave-flash", ".tar.gz" => "application/x-tgz", ".tgz" => "application/x-tgz", ".tar" => "application/x-tar", ".zip" => "application/zip", ".mp3" => "audio/mpeg", ".m3u" => "audio/x-mpegurl", ".wma" => "audio/x-ms-wma", ".wax" => "audio/x-ms-wax", ".ogg" => "application/ogg", ".wav" => "audio/x-wav", ".gif" => "image/gif", ".jar" => "application/x-java-archive", ".jpg" => "image/jpeg", ".jpeg" => "image/jpeg", ".png" => "image/png", ".xbm" => "image/x-xbitmap", ".xpm" => "image/x-xpixmap", ".xwd" => "image/x-xwindowdump", ".css" => "text/css", ".html" => "text/html", ".htm" => "text/html", ".js" => "text/javascript", ".asc" => "text/plain", ".c" => "text/plain", ".cpp" => "text/plain", ".log" => "text/plain", ".conf" => "text/plain", ".text" => "text/plain", ".txt" => "text/plain", ".dtd" => "text/xml", ".xml" => "text/xml", ".mpeg" => "video/mpeg", ".mpg" => "video/mpeg", ".mov" => "video/quicktime", ".qt" => "video/quicktime", ".avi" => "video/x-msvideo", ".asf" => "video/x-ms-asf", ".asx" => "video/x-ms-asf", ".wmv" => "video/x-ms-wmv", ".bz2" => "application/x-bzip", ".tbz" => "application/x-bzip-compressed-tar", ".tar.bz2" => "application/x-bzip-compressed-tar", # default mime type "" => "application/octet-stream", ) # Use the "Content-Type" extended attribute to obtain mime type if possible #mimetype.use-xattr = "enable" ## send a different Server: header ## be nice and keep it at lighttpd # server.tag = "lighttpd" #### accesslog module accesslog.filename = "/var/log/lighttpd/access.log" ## deny access the file-extensions # # ~ is for backupfiles from vi, emacs, joe, ... # .inc is often used for code includes which should in general not be part # of the document-root url.access-deny = ( "~", ".inc" ) $HTTP["url"] =~ "\.pdf$" { server.range-requests = "disable" } ## # which extensions should not be handle via static-file transfer # # .php, .pl, .fcgi are most often handled by mod_fastcgi or mod_cgi static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) ######### Options that are good to be but not neccesary to be changed ####### ## bind to port (default: 80) #server.port = 81 ## bind to localhost (default: all interfaces) #server.bind = "127.0.0.1" ## error-handler for status 404 #server.error-handler-404 = "/error-handler.html" #server.error-handler-404 = "/error-handler.php" ## to help the rc.scripts server.pid-file = "/opt/local/var/run/lighttpd.pid" ###### virtual hosts ## ## If you want name-based virtual hosting add the next three settings and load ## mod_simple_vhost ## ## document-root = ## virtual-server-root + virtual-server-default-host + virtual-server-docroot ## or ## virtual-server-root + http-host + virtual-server-docroot ## simple-vhost.server-root = "/work/websites/" simple-vhost.default-host = "freesoft.com" simple-vhost.document-root = "/htdocs/" alias.url = ( "/sf/" => "/opt/local/lib/php/data/symfony/web/sf/" ) url.rewrite-once = ( "^/(.*\..+(?!html))$" => "$0", "^/(.*)\.(.*)" => "$0", "^/([^.]+)$" => "/index.php/$1", "^/$" => "/index.php" ) ## ## Format: <errorfile-prefix><status-code>.html ## -> ..../status-404.html for 'File not found' #server.errorfile-prefix = "/usr/share/lighttpd/errors/status-" #server.errorfile-prefix = "/srv/www/errors/status-" ## virtual directory listings #dir-listing.activate = "enable" ## select encoding for directory listings #dir-listing.encoding = "utf-8" ## enable debugging debug.log-request-header = "enable" debug.log-response-header = "enable" debug.log-request-handling = "enable" debug.log-file-not-found = "enable" ### only root can use these options # # chroot() to directory (default: no chroot() ) #server.chroot = "/" ## change uid to <uid> (default: don't care) #server.username = "wwwrun" ## change uid to <uid> (default: don't care) #server.groupname = "wwwrun" #### compress module #compress.cache-dir = "/var/cache/lighttpd/compress/" #compress.filetype = ("text/plain", "text/html") #### proxy module ## read proxy.txt for more info #proxy.server = ( ".php" => # ( "localhost" => # ( # "host" => "192.168.0.101", # "port" => 80 # ) # ) # ) #### fastcgi module ## read fastcgi.txt for more info ## for PHP don't forget to set cgi.fix_pathinfo = 1 in the php.ini fastcgi.server = ( ".php" => ( "localhost" => ( "socket" => "/var/run/lighttpd/php-fastcgi.socket", "bin-path" => "/opt/local/bin/php-cgi" ) ) ) #### CGI module #cgi.assign = ( ".pl" => "/opt/local/bin/perl", # ".cgi" => "/opt/local/bin/perl" ) # #### SSL engine #ssl.engine = "enable" #ssl.pemfile = "/etc/ssl/private/lighttpd.pem" #### status module #status.status-url = "/server-status" #status.config-url = "/server-config" #### auth module ## read authentication.txt for more info #auth.backend = "plain" #auth.backend.plain.userfile = "lighttpd.user" #auth.backend.plain.groupfile = "lighttpd.group" #auth.backend.ldap.hostname = "localhost" #auth.backend.ldap.base-dn = "dc=my-domain,dc=com" #auth.backend.ldap.filter = "(uid=$)" #auth.require = ( "/server-status" => # ( # "method" => "digest", # "realm" => "download archiv", # "require" => "user=jan" # ), # "/server-config" => # ( # "method" => "digest", # "realm" => "download archiv", # "require" => "valid-user" # ) # ) #### url handling modules (rewrite, redirect, access) #url.rewrite = ( "^/$" => "/server-status" ) #url.redirect = ( "^/wishlist/(.+)" => "http://www.123.org/$1" ) #### both rewrite/redirect support back reference to regex conditional using %n #$HTTP["host"] =~ "^www\.(.*)" { # url.redirect = ( "^/(.*)" => "http://%1/$1" ) #} # # define a pattern for the host url finding # %% => % sign # %0 => domain name + tld # %1 => tld # %2 => domain name without tld # %3 => subdomain 1 name # %4 => subdomain 2 name # #evhost.path-pattern = "/srv/www/vhosts/%3/htdocs/" #### expire module #expire.url = ( "/buggy/" => "access 2 hours", "/asdhas/" => "access plus 1 seconds 2 minutes") #### ssi #ssi.extension = ( ".shtml" ) #### rrdtool #rrdtool.binary = "/opt/local/bin/rrdtool" #rrdtool.db-name = "/opt/local/var/lib/lighttpd/lighttpd.rrd" #### setenv #setenv.add-request-header = ( "TRAV_ENV" => "mysql://user@host/db" ) #setenv.add-response-header = ( "X-Secret-Message" => "42" ) ## for mod_trigger_b4_dl # trigger-before-download.gdbm-filename = "/var/lib/lighttpd/trigger.db" # trigger-before-download.memcache-hosts = ( "127.0.0.1:11211" ) # trigger-before-download.trigger-url = "^/trigger/" # trigger-before-download.download-url = "^/download/" # trigger-before-download.deny-url = "http://127.0.0.1/index.html" # trigger-before-download.trigger-timeout = 10 ## for mod_cml ## don't forget to add index.cml to server.indexfiles # cml.extension = ".cml" # cml.memcache-hosts = ( "127.0.0.1:11211" ) #### variable usage: ## variable name without "." is auto prefixed by "var." and becomes "var.bar" #bar = 1 #var.mystring = "foo" ## integer add #bar += 1 ## string concat, with integer cast as string, result: "www.foo1.com" #server.name = "www." + mystring + var.bar + ".com" ## array merge #index-file.names = (foo + ".php") + index-file.names #index-file.names += (foo + ".php") #### include #include /etc/lighttpd/lighttpd-inc.conf ## same as above if you run: "lighttpd -f /etc/lighttpd/lighttpd.conf" #include "lighttpd-inc.conf" #### include_shell #include_shell "echo var.a=1" ## the above is same as: #var.a=1
Updated by venatir over 15 years ago
This was tested on:
1.4.20 OSX Leopard 10.5.7 filesystem: hfs+
1.4.22 freebsd 7.2 stable amd64 filesystem: ufs
On linux, I tested on a centos 5.2 box and the bug does not manifest on linux.
Updated by darix over 15 years ago
- Status changed from New to Invalid
operating system bug:
http://www.freebsd.org/cgi/query-pr.cgi?pr=kern/21768
Updated by icy over 15 years ago
- File stat_cache.c.patch stat_cache.c.patch added
- Category changed from mod_access to core
- Status changed from Invalid to Reopened
- Target version changed from 1.4.22 to 1.4.23
Preliminary patch to work around that bug. Needs review and testing.
Updated by peto over 15 years ago
- File stat_cache.c.2.patch stat_cache.c.2.patch added
darix: Real-world applications need to cope with OS bugs, particularly when there are security implications. Ignoring them ("oh well, not our problem!") when you're maintaining security critical, internet-facing software like this is very irresponsible.
icy: This patch seems to work (in FBSD 7.1), but returns the wrong error (500). That's because it doesn't set errno, probably leaving it at ESUCCESS from the stat() call. Updated patch attached; now it returns 403.
Only tested by creating a symlink, disabling follow-symlinks, and reading the file with and without a trailing slash. I havn't tried it combined with CGI of any kind.
Note that if follow-symlinks is true, it does still serve the file with a trailing slash. stat_cache_get_entry is called three times: twice with the trailing slash, returning an error, and then once with the slash removed. stat_cache_get_entry is operating correctly and I havn't looked further up the logic. This behavior is fine with follow-symlinks enabled, but someone should specifically verify it with CGI.
I assume this needs porting to 1.5, too.
Updated by icy over 15 years ago
Thanks peto for enhencing the patch. I only hacked it quickly together :)
Here's a list of OSs that have been tested for this bug:
affected: FreeBSD, OSX, Solaris < 10
not affected: Linux, NetBSD, OpenBSD, DragonflyBSD, Solaris 10
Thanks also to everyone who tested on those systems at this point.
Updated by darix over 15 years ago
peto wrote:
darix: Real-world applications need to cope with OS bugs, particularly when there are security implications. Ignoring them ("oh well, not our problem!") when you're maintaining security critical, internet-facing software like this is very irresponsible.
Well. The fbsd behavior is just broken and it is known since 2000. while we can add a workaround for this. imho the real fix should be applied to the broken OS.
Updated by icy over 15 years ago
While it might not be our "job" to fix it, providing a fix (which doesn't break anything and is not complex) is a good thing for sure.
The enhenced patch by peto seems to work well after some quick testing.
Updated by stbuehler over 15 years ago
Use this to prevent the source code leakage (returns 403 instead of using "/" as PATH_INFO for a normal request):
static-file.exclude-extensions += ( "/" )
Imho it is the job of the lighty package maintainers of the broken OS to apply the patch. I don't like workarounds upstream (they often make things worse...); they broke it, they can fix it easily as peto provided a patch (thx peto).
Btw: There are similar issues with alternate data streams - NTFS ::$DATA
; and we don't care about them either.
And as it looks like some people didn't get that: 1.4.23 is neither the affected version (it isn't even released yet...) nor is it a lighty bug.
Updated by stbuehler over 15 years ago
- Subject changed from SECURITY ISSUE - Displaying source code of php, ruby or any other file to Workaround broken operating systems - Displaying source code of php, ruby or any other file
- Priority changed from Immediate to High
Updated by stbuehler over 15 years ago
- Status changed from Reopened to Fixed
- % Done changed from 0 to 100
Applied in changeset r2510.
Also available in: Atom