Monday, May 29, 2017

findBackdoor.py

class findBackdoor(baseDiscoveryPlugin):
00124     '''
00125     Find web backdoors and web shells.
00126     @author: Andres Riancho ( andres.riancho@gmail.com )
00127     '''
00128 
00129     def __init__(self):
00130         baseDiscoveryPlugin.__init__(self)
00131         
00132         # Internal variables
00133         self._analyzed_dirs = disk_list()
00134         self._fuzzable_requests_to_return = []
00135 
00136     def discover(self, fuzzableRequest):
00137         '''
00138         For every directory, fetch a list of shell files and analyze the response.
00139         
00140         @parameter fuzzableRequest: A fuzzableRequest instance that contains 
00141         (among other things) the URL to test.
00142         '''
00143         domain_path = fuzzableRequest.getURL().getDomainPath()
00144         self._fuzzable_requests_to_return = []
00145 
00146         if domain_path not in self._analyzed_dirs:
00147             self._analyzed_dirs.append(domain_path)
00148 
00149             # Search for the web shells
00150             for web_shell_filename in WEB_SHELLS:
00151                 web_shell_url = domain_path.urlJoin(web_shell_filename)
00152                 # Perform the check in different threads
00153                 targs = (web_shell_url,)
00154                 self._tm.startFunction(target=self._check_if_exists, 
00155                                        args=targs, ownerObj=self)
00156 
00157             # Wait for all threads to finish
00158             self._tm.join(self)
00159 
00160             return self._fuzzable_requests_to_return
00161 
00162     
00163     def _check_if_exists(self, web_shell_url):
00164         '''
00165         Check if the file exists.
00166         
00167         @parameter web_shell_url: The URL to check
00168         '''
00169         try:
00170             response = self._urlOpener.GET(web_shell_url, useCache=True)
00171         except w3afException:
00172             om.out.debug('Failed to GET webshell:' + web_shell_url)
00173         else:
00174             if self._is_possible_backdoor(response):
00175                 v = vuln.vuln()
00176                 v.setPluginName(self.getName())
00177                 v.setId(response.id)
00178                 v.setName('Possible web backdoor')
00179                 v.setSeverity(severity.HIGH)
00180                 v.setURL(response.getURL())
00181                 msg = 'A web backdoor was found at: "%s"; this could ' \
00182                 'indicate that the server was hacked.' % v.getURL()
00183                 v.setDesc(msg)
00184                 kb.kb.append(self, 'backdoors', v)
00185                 om.out.vulnerability(v.getDesc(), severity=v.getSeverity())
00186 
00187                 fuzzable_requests = self._createFuzzableRequests(response)
00188                 self._fuzzable_requests_to_return += fuzzable_requests
00189             
00190     def _is_possible_backdoor(self, response):
00191         '''
00192         Heuristic to infer if the content of  has the pattern of a
00193         backdoor response.
00194         
00195         @param response: httpResponse object
00196         @return: A bool value
00197         '''
00198         if not is_404(response):
00199             body_text = response.getBody()
00200             dom  = response.getDOM()
00201             if dom:
00202                 for ele, attrs in BACKDOOR_COLLECTION.iteritems():
00203                     for attrname, attr_vals in attrs.iteritems():
00204                         # Set of lowered attribute values
00205                         dom_attr_vals = \
00206                             set(n.get(attrname).lower() for n in \
00207                                 (dom.xpath('//%s[@%s]' % (ele, attrname))))
00208                         # If at least one elem in intersection return True
00209                         if (dom_attr_vals and set(attr_vals)):
00210                             return True
00211     
00212             # If no regex matched then try with keywords. At least 2 should be
00213             # contained in 'body_text' to succeed.
00214             times = 0
00215             for back_kw in KNOWN_OFFENSIVE_WORDS:
00216                 if re.search(back_kw, body_text, re.I):
00217                     times += 1
00218                     if times == 2:
00219                         return True
00220         return False
00221 
00222     def getOptions(self):
00223         '''
00224         @return: A list of option objects for this plugin.
00225         '''
00226         ol = optionList()
00227         return ol
00228 
00229     def setOptions(self, OptionList):
00230         '''
00231         This method sets all the options that are configured using the user interface 
00232         generated by the framework using the result of getOptions().
00233         
00234         @parameter OptionList: A dictionary with the options for the plugin.
00235         @return: No value is returned.
00236         '''
00237         pass
00238 
00239     def getPluginDeps(self):
00240         '''
00241         @return: A list with the names of the plugins that should be runned before the
00242         current one.
00243         '''
00244         return []
00245 
00246     def getLongDesc(self):
00247         '''
00248         @return: A DETAILED description of the plugin functions and features.
00249         '''
00250         return '''
00251         This plugin searches for web shells in the directories that are sent as input.
00252         For example, if the input is:
00253             - http://host.tld/w3af/f00b4r.php
00254             
00255         The plugin will perform these requests:
00256             - http://host.tld/w3af/c99.php
00257             - http://host.tld/w3af/cmd.php
00258             - http://host.tld/w3af/webshell.php
00259             ...
00260         '''
https://sourcecodebrowser.com/w3af/1.0.0/find_backdoor_8py_source.html

No comments: