''' passwordProfiling.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.baseGrepPlugin import baseGrepPlugin import core.data.kb.knowledgeBase as kb from core.controllers.misc.factory import * from core.data.getResponseType import * import urllib class passwordProfiling(baseGrepPlugin): ''' This plugin creates a list of possible passwords by reading responses. @author: Andres Riancho ( andres.riancho@gmail.com ) ''' def __init__(self): baseGrepPlugin.__init__(self) # This is nicer, but htmlParser inherits from SGMLParser that AINT # thread safe, So i have to create an instance of htmlParser for every # call to testResponse #self._htmlParser = htmlParser.htmlParser() kb.kb.save( self, 'passwordProfiling', {} ) # names of plugins to run ### TODO: develop more plugins, there is a nice ( all python ) metadata reader named hachoir-metadata ### it will be usefull for doing A LOT of plugins self._pluginsStr = ['html'] # plugin instances, they are created in the first call to self._runPpPlugins self._plugins = [] # This are common words I dont want to use as passwords self._commonWords = {} self._commonWords['en'] = [ 'type', 'that', 'from', 'this', 'been', 'there', 'which', 'line', 'error', 'Warning', 'file'\ 'fatal', 'failed', 'Failed', 'open', 'such', 'required', 'directory', 'valid', 'result', 'argument', 'There', 'some'] self._commonWords['es'] = [ 'otro', 'otra', 'para', 'pero', 'hacia', 'algo', 'poder', 'error'] self._commonWords['unknown'] = self._commonWords['en'] def _testResponse(self, request, response): if isTextOrHtml(response.getHeaders()): self.is404 = kb.kb.getData( 'error404page', '404' ) self.lang = kb.kb.getData( 'lang', 'lang' ) if self.is404 == [] or self.lang == []: # Let the discovery fase finish its work... pass else: if not self.is404( response ) and request.getMethod() in ['POST', 'GET'] and \ response.getCode() != 500: data = self._runPpPlugins( response ) oldData = kb.kb.getData( 'passwordProfiling', 'passwordProfiling' ) # "merge" both maps and update the repetitions for d in data.keys(): if d.lower() not in self._commonWords[ self.lang ] \ and not self._wasSent( request, d ): if d in oldData.keys(): oldData[ d ] += data[ d ] else: oldData[ d ] = data[ d ] # save the merged map kb.kb.save( self, 'passwordProfiling', oldData ) def _wasSent( self, request, theWord ): ''' Checks if the theWord was sent in the request. ''' url = urllib.unquote( request.getURL() ) if request.getMethod().upper() == 'POST': sentData = request.getData() sentData = urllib.unquote( sentData ) else: sentData = '' if url.count( theWord ) or sentData.count( theWord ): return True else: return False def _runPpPlugins( self, response ): ''' Runs password profiling plugins to collect data from HTML, TXT, PDF, etc files. @parameter response: A httpResponse object @return: A map with word:repetitions ''' if len(self._plugins)==0: # Create plugins for pluginName in self._pluginsStr: self._plugins.append( factory( 'plugins.grep.passwordProfilingPlugins.' + pluginName ) ) res = {} for plugin in self._plugins: wordMap = plugin.getWords( response ) if wordMap != None: # If a plugin returned something thats not None, then we are done. res = wordMap break return res def setOptions( self, OptionList ): pass 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/output.xsd ''' return '\ \ \ ' def end(self): ''' This method is called when the plugin wont be used anymore. ''' def sortfunc(x,y): return cmp(y[1],x[1]) items = kb.kb.getData( 'passwordProfiling', 'passwordProfiling' ).items() if len( items ) != 0: items.sort(sortfunc) om.out.information('Password profiling TOP 100:') listLen = len(items) if listLen > 100: xLen = 100 else: xLen = listLen for i in xrange(xLen): om.out.information('- [' + str(i + 1) + '] ' + items[i][0] + ' with ' + str(items[i][1]) + ' repetitions.' ) def getPluginDeps( self ): ''' @return: A list with the names of the plugins that should be runned before the current one. ''' return ['discovery.error404page','grep.lang'] def getLongDesc( self ): ''' @return: A DETAILED description of the plugin functions and features. ''' return ''' This plugin creates a list of possible passwords by reading responses and counting the most common words. '''